Merge pull request #2404 from Secalot/master

Add Secalot hardware token support.
This commit is contained in:
Dominik Schürmann 2018-10-30 15:45:58 +01:00 committed by GitHub
commit 894bac6c8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 39 additions and 13 deletions

View File

@ -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: {

View File

@ -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() {

View File

@ -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

View File

@ -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<TokenType> 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<TokenType> 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() {

View File

@ -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);

View File

@ -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