Read life cycle management from historical bytes
This commit is contained in:
@@ -20,24 +20,32 @@ package org.sufficientlysecure.keychain.securitytoken;
|
||||
import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
class CardCapabilities {
|
||||
private static final int MASK_CHAINING = 1 << 7;
|
||||
private static final int MASK_EXTENDED = 1 << 6;
|
||||
|
||||
private byte[] mCapabilityBytes;
|
||||
private static final int STATUS_INDICATOR_NO_INFORMATION = 0x00;
|
||||
private static final int STATUS_INDICATOR_INITIALISATION_STATE = 0x03;
|
||||
private static final int STATUS_INDICATOR_OPERATIONAL_STATE = 0x05;
|
||||
|
||||
private static final byte[] EXPECTED_PROCESSING_STATUS_BYTES = {(byte) 0x90, (byte) 0x00};
|
||||
|
||||
private byte[] historicalBytes;
|
||||
private byte[] capabilityBytes;
|
||||
|
||||
public CardCapabilities(byte[] historicalBytes) throws UsbTransportException {
|
||||
if ((historicalBytes == null) || (historicalBytes[0] != 0x00)) {
|
||||
throw new UsbTransportException("Invalid historical bytes category indicator byte");
|
||||
}
|
||||
|
||||
mCapabilityBytes = getCapabilitiesBytes(historicalBytes);
|
||||
this.historicalBytes = historicalBytes;
|
||||
capabilityBytes = getCapabilitiesBytes(historicalBytes);
|
||||
}
|
||||
|
||||
public CardCapabilities() {
|
||||
mCapabilityBytes = null;
|
||||
capabilityBytes = null;
|
||||
}
|
||||
|
||||
private static byte[] getCapabilitiesBytes(byte[] historicalBytes) {
|
||||
@@ -57,10 +65,34 @@ class CardCapabilities {
|
||||
}
|
||||
|
||||
public boolean hasChaining() {
|
||||
return mCapabilityBytes != null && (mCapabilityBytes[2] & MASK_CHAINING) != 0;
|
||||
return capabilityBytes != null && (capabilityBytes[2] & MASK_CHAINING) != 0;
|
||||
}
|
||||
|
||||
public boolean hasExtended() {
|
||||
return mCapabilityBytes != null && (mCapabilityBytes[2] & MASK_EXTENDED) != 0;
|
||||
return capabilityBytes != null && (capabilityBytes[2] & MASK_EXTENDED) != 0;
|
||||
}
|
||||
|
||||
public boolean hasResetSupport() throws UsbTransportException {
|
||||
byte[] lastBytes = Arrays.copyOfRange(historicalBytes, historicalBytes.length - 2, historicalBytes.length);
|
||||
boolean hasExpectedLastBytes = Arrays.equals(lastBytes, EXPECTED_PROCESSING_STATUS_BYTES);
|
||||
|
||||
// Yk neo simply ends with 0x0000
|
||||
if (!hasExpectedLastBytes) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int statusIndicatorByte = historicalBytes[historicalBytes.length - 3];
|
||||
switch (statusIndicatorByte) {
|
||||
case STATUS_INDICATOR_NO_INFORMATION: {
|
||||
return false;
|
||||
}
|
||||
case STATUS_INDICATOR_INITIALISATION_STATE:
|
||||
case STATUS_INDICATOR_OPERATIONAL_STATE: {
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
throw new UsbTransportException("Status indicator byte not specified in OpenPGP specification");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,13 +144,13 @@ class OpenPgpCommandApduFactory {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
CommandApdu createReactivate2Command() {
|
||||
return CommandApdu.create(CLA, INS_ACTIVATE_FILE, P1_EMPTY, P2_EMPTY);
|
||||
CommandApdu createReactivate1Command() {
|
||||
return CommandApdu.create(CLA, INS_TERMINATE_DF, P1_EMPTY, P2_EMPTY);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
CommandApdu createReactivate1Command() {
|
||||
return CommandApdu.create(CLA, INS_TERMINATE_DF, P1_EMPTY, P2_EMPTY);
|
||||
CommandApdu createReactivate2Command() {
|
||||
return CommandApdu.create(CLA, INS_ACTIVATE_FILE, P1_EMPTY, P2_EMPTY);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -167,16 +167,31 @@ public class SecurityTokenUtilsTest extends Mockito {
|
||||
capabilities = new CardCapabilities(Hex.decode("007300008000000000000000000000"));
|
||||
Assert.assertEquals(capabilities.hasChaining(), true);
|
||||
Assert.assertEquals(capabilities.hasExtended(), false);
|
||||
Assert.assertEquals(capabilities.hasResetSupport(), true);
|
||||
|
||||
// Yk 4
|
||||
capabilities = new CardCapabilities(Hex.decode("0073000080059000"));
|
||||
Assert.assertEquals(capabilities.hasChaining(), true);
|
||||
Assert.assertEquals(capabilities.hasExtended(), false);
|
||||
Assert.assertEquals(capabilities.hasResetSupport(), true);
|
||||
|
||||
// Nitrokey pro
|
||||
capabilities = new CardCapabilities(Hex.decode("0031c573c00140059000"));
|
||||
Assert.assertEquals(capabilities.hasChaining(), false);
|
||||
Assert.assertEquals(capabilities.hasExtended(), true);
|
||||
Assert.assertEquals(capabilities.hasResetSupport(), true);
|
||||
|
||||
// GNUK without Life Cycle Management
|
||||
capabilities = new CardCapabilities(Hex.decode("00318473800180009000"));
|
||||
Assert.assertEquals(capabilities.hasChaining(), true);
|
||||
Assert.assertEquals(capabilities.hasExtended(), false);
|
||||
Assert.assertEquals(capabilities.hasResetSupport(), false);
|
||||
|
||||
// GNUK with Life Cycle Management: ./configure --enable-factory-reset
|
||||
capabilities = new CardCapabilities(Hex.decode("00318473800180059000"));
|
||||
Assert.assertEquals(capabilities.hasChaining(), true);
|
||||
Assert.assertEquals(capabilities.hasExtended(), false);
|
||||
Assert.assertEquals(capabilities.hasResetSupport(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user