diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/CardCapabilities.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/CardCapabilities.java index cadb167a3..40a81ea50 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/CardCapabilities.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/CardCapabilities.java @@ -18,7 +18,7 @@ package org.sufficientlysecure.keychain.securitytoken; import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; - +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TokenType; import java.nio.ByteBuffer; import java.util.Arrays; @@ -36,12 +36,15 @@ class CardCapabilities { private byte[] historicalBytes; private byte[] capabilityBytes; - public CardCapabilities(byte[] historicalBytes) throws UsbTransportException { + private TokenType tokenType; + + public CardCapabilities(byte[] historicalBytes, TokenType tokenType) throws UsbTransportException { if ((historicalBytes == null) || (historicalBytes[0] != 0x00)) { throw new UsbTransportException("Invalid historical bytes category indicator byte"); } this.historicalBytes = historicalBytes; capabilityBytes = getCapabilitiesBytes(historicalBytes); + this.tokenType = tokenType; } public CardCapabilities() { @@ -81,6 +84,10 @@ class CardCapabilities { return true; } + if (tokenType == TokenType.SECALOT) { + return true; + } + int statusIndicatorByte = historicalBytes[historicalBytes.length - 3]; switch (statusIndicatorByte) { case STATUS_INDICATOR_NO_INFORMATION: { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/OpenPgpCapabilities.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/OpenPgpCapabilities.java index e364b8b10..18f22e9b7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/OpenPgpCapabilities.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/OpenPgpCapabilities.java @@ -98,11 +98,11 @@ public abstract class OpenPgpCapabilities { } public int getPw1MaxLength() { - return getPwStatusBytes()[MAX_PW1_LENGTH_INDEX]; + return getPwStatusBytes()[MAX_PW1_LENGTH_INDEX] & 0xFF; } public int getPw3MaxLength() { - return getPwStatusBytes()[MAX_PW3_LENGTH_INDEX]; + return getPwStatusBytes()[MAX_PW3_LENGTH_INDEX] & 0xFF; } public int getPw1TriesLeft() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java index a34029bac..0237c12fd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java @@ -167,7 +167,7 @@ public class SecurityTokenConnection { @VisibleForTesting void setConnectionCapabilities(OpenPgpCapabilities openPgpCapabilities) throws IOException { this.openPgpCapabilities = openPgpCapabilities; - this.cardCapabilities = new CardCapabilities(openPgpCapabilities.getHistoricalBytes()); + this.cardCapabilities = new CardCapabilities(openPgpCapabilities.getHistoricalBytes(), tokenType); } // endregion diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java index ecdb885e5..77ab3d987 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java @@ -117,7 +117,7 @@ public abstract class SecurityTokenInfo implements Parcelable { public enum TokenType { YUBIKEY_NEO, YUBIKEY_4, FIDESMO, NITROKEY_PRO, NITROKEY_STORAGE, NITROKEY_START_OLD, - NITROKEY_START_1_25_AND_NEWER, GNUK_OLD, GNUK_1_25_AND_NEWER, LEDGER_NANO_S, UNKNOWN + NITROKEY_START_1_25_AND_NEWER, GNUK_OLD, GNUK_1_25_AND_NEWER, LEDGER_NANO_S, SECALOT, UNKNOWN } public static final Set SUPPORTED_USB_TOKENS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( @@ -129,7 +129,8 @@ public abstract class SecurityTokenInfo implements Parcelable { TokenType.NITROKEY_START_1_25_AND_NEWER, TokenType.GNUK_OLD, TokenType.GNUK_1_25_AND_NEWER, - TokenType.LEDGER_NANO_S + TokenType.LEDGER_NANO_S, + TokenType.SECALOT ))); private static final Set SUPPORTED_USB_SETUP = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( @@ -138,7 +139,8 @@ public abstract class SecurityTokenInfo implements Parcelable { TokenType.NITROKEY_PRO, TokenType.NITROKEY_STORAGE, TokenType.NITROKEY_START_1_25_AND_NEWER, - TokenType.GNUK_1_25_AND_NEWER + TokenType.GNUK_1_25_AND_NEWER, + TokenType.SECALOT ))); public boolean isPutKeySupported() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java index 610106288..134985338 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java @@ -70,6 +70,9 @@ public class UsbTransport implements Transport { private static final int VENDOR_FSIJ = 9035; private static final int VENDOR_LEDGER = 11415; + private static final int VENDOR_SECALOT = 4617; + private static final int PRODUCT_SECALOT = 28672; + private final UsbDevice usbDevice; private final UsbManager usbManager; @@ -254,6 +257,13 @@ public class UsbTransport implements Transport { case VENDOR_LEDGER: { return TokenType.LEDGER_NANO_S; } + case VENDOR_SECALOT: { + switch (productId) { + case PRODUCT_SECALOT: + return TokenType.SECALOT; + } + break; + } } Timber.d("Unknown USB token. Vendor ID: %s, Product ID: %s", vendorId, productId); diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtilsTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtilsTest.java index 56e702f9f..a3c55a1b2 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtilsTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtilsTest.java @@ -28,6 +28,7 @@ import org.mockito.Mockito; import org.robolectric.shadows.ShadowLog; import org.sufficientlysecure.keychain.KeychainTestRunner; import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TokenType; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -164,34 +165,40 @@ public class SecurityTokenUtilsTest extends Mockito { CardCapabilities capabilities; // Yk neo - capabilities = new CardCapabilities(Hex.decode("007300008000000000000000000000")); + capabilities = new CardCapabilities(Hex.decode("007300008000000000000000000000"), TokenType.YUBIKEY_NEO); Assert.assertEquals(capabilities.hasChaining(), true); Assert.assertEquals(capabilities.hasExtended(), false); Assert.assertEquals(capabilities.hasLifeCycleManagement(), true); // Yk 4 - capabilities = new CardCapabilities(Hex.decode("0073000080059000")); + capabilities = new CardCapabilities(Hex.decode("0073000080059000"), TokenType.YUBIKEY_4); Assert.assertEquals(capabilities.hasChaining(), true); Assert.assertEquals(capabilities.hasExtended(), false); Assert.assertEquals(capabilities.hasLifeCycleManagement(), true); // Nitrokey pro - capabilities = new CardCapabilities(Hex.decode("0031c573c00140059000")); + capabilities = new CardCapabilities(Hex.decode("0031c573c00140059000"), TokenType.NITROKEY_PRO); Assert.assertEquals(capabilities.hasChaining(), false); Assert.assertEquals(capabilities.hasExtended(), true); Assert.assertEquals(capabilities.hasLifeCycleManagement(), true); // GNUK without Life Cycle Management - capabilities = new CardCapabilities(Hex.decode("00318473800180009000")); + capabilities = new CardCapabilities(Hex.decode("00318473800180009000"), TokenType.GNUK_OLD); Assert.assertEquals(capabilities.hasChaining(), true); Assert.assertEquals(capabilities.hasExtended(), false); Assert.assertEquals(capabilities.hasLifeCycleManagement(), false); // GNUK with Life Cycle Management: ./configure --enable-factory-reset - capabilities = new CardCapabilities(Hex.decode("00318473800180059000")); + capabilities = new CardCapabilities(Hex.decode("00318473800180059000"), TokenType.GNUK_OLD); Assert.assertEquals(capabilities.hasChaining(), true); Assert.assertEquals(capabilities.hasExtended(), false); Assert.assertEquals(capabilities.hasLifeCycleManagement(), true); + + // Secalot + capabilities = new CardCapabilities(Hex.decode("0031C573C00140009000"), TokenType.SECALOT); + Assert.assertEquals(capabilities.hasChaining(), false); + Assert.assertEquals(capabilities.hasExtended(), true); + Assert.assertEquals(capabilities.hasLifeCycleManagement(), true); } @Test