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 org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
class CardCapabilities {
|
class CardCapabilities {
|
||||||
private static final int MASK_CHAINING = 1 << 7;
|
private static final int MASK_CHAINING = 1 << 7;
|
||||||
private static final int MASK_EXTENDED = 1 << 6;
|
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 {
|
public CardCapabilities(byte[] historicalBytes) throws UsbTransportException {
|
||||||
if ((historicalBytes == null) || (historicalBytes[0] != 0x00)) {
|
if ((historicalBytes == null) || (historicalBytes[0] != 0x00)) {
|
||||||
throw new UsbTransportException("Invalid historical bytes category indicator byte");
|
throw new UsbTransportException("Invalid historical bytes category indicator byte");
|
||||||
}
|
}
|
||||||
|
this.historicalBytes = historicalBytes;
|
||||||
mCapabilityBytes = getCapabilitiesBytes(historicalBytes);
|
capabilityBytes = getCapabilitiesBytes(historicalBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CardCapabilities() {
|
public CardCapabilities() {
|
||||||
mCapabilityBytes = null;
|
capabilityBytes = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] getCapabilitiesBytes(byte[] historicalBytes) {
|
private static byte[] getCapabilitiesBytes(byte[] historicalBytes) {
|
||||||
@@ -57,10 +65,34 @@ class CardCapabilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasChaining() {
|
public boolean hasChaining() {
|
||||||
return mCapabilityBytes != null && (mCapabilityBytes[2] & MASK_CHAINING) != 0;
|
return capabilityBytes != null && (capabilityBytes[2] & MASK_CHAINING) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasExtended() {
|
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
|
@NonNull
|
||||||
CommandApdu createReactivate2Command() {
|
CommandApdu createReactivate1Command() {
|
||||||
return CommandApdu.create(CLA, INS_ACTIVATE_FILE, P1_EMPTY, P2_EMPTY);
|
return CommandApdu.create(CLA, INS_TERMINATE_DF, P1_EMPTY, P2_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
CommandApdu createReactivate1Command() {
|
CommandApdu createReactivate2Command() {
|
||||||
return CommandApdu.create(CLA, INS_TERMINATE_DF, P1_EMPTY, P2_EMPTY);
|
return CommandApdu.create(CLA, INS_ACTIVATE_FILE, P1_EMPTY, P2_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|||||||
@@ -167,16 +167,31 @@ public class SecurityTokenUtilsTest extends Mockito {
|
|||||||
capabilities = new CardCapabilities(Hex.decode("007300008000000000000000000000"));
|
capabilities = new CardCapabilities(Hex.decode("007300008000000000000000000000"));
|
||||||
Assert.assertEquals(capabilities.hasChaining(), true);
|
Assert.assertEquals(capabilities.hasChaining(), true);
|
||||||
Assert.assertEquals(capabilities.hasExtended(), false);
|
Assert.assertEquals(capabilities.hasExtended(), false);
|
||||||
|
Assert.assertEquals(capabilities.hasResetSupport(), true);
|
||||||
|
|
||||||
// Yk 4
|
// Yk 4
|
||||||
capabilities = new CardCapabilities(Hex.decode("0073000080059000"));
|
capabilities = new CardCapabilities(Hex.decode("0073000080059000"));
|
||||||
Assert.assertEquals(capabilities.hasChaining(), true);
|
Assert.assertEquals(capabilities.hasChaining(), true);
|
||||||
Assert.assertEquals(capabilities.hasExtended(), false);
|
Assert.assertEquals(capabilities.hasExtended(), false);
|
||||||
|
Assert.assertEquals(capabilities.hasResetSupport(), true);
|
||||||
|
|
||||||
// Nitrokey pro
|
// Nitrokey pro
|
||||||
capabilities = new CardCapabilities(Hex.decode("0031c573c00140059000"));
|
capabilities = new CardCapabilities(Hex.decode("0031c573c00140059000"));
|
||||||
Assert.assertEquals(capabilities.hasChaining(), false);
|
Assert.assertEquals(capabilities.hasChaining(), false);
|
||||||
Assert.assertEquals(capabilities.hasExtended(), true);
|
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
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user