diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/NfcTransport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/NfcTransport.java index e4d811034..3dfd9ba19 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/NfcTransport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/NfcTransport.java @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.securitytoken; import android.nfc.Tag; +import android.support.annotation.Nullable; import android.util.Log; import org.bouncycastle.util.encoders.Hex; @@ -118,8 +119,10 @@ public class NfcTransport implements Transport { return TransportType.NFC; } + @Nullable @Override - public TokenType getTokenType() { + public TokenType getTokenTypeIfAvailable() { + // Sadly, the NFC transport has no direct information about the token type. return null; } 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 495ffddb8..5fe3f57cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java @@ -76,8 +76,7 @@ import java.util.List; public class SecurityTokenConnection { private static final int APDU_SW1_RESPONSE_AVAILABLE = 0x61; - // Fidesmo constants - private static final String FIDESMO_APPS_AID_PREFIX = "A000000617"; + private static final String AID_PREFIX_FIDESMO = "A000000617"; private static final byte[] BLANK_FINGERPRINT = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -91,6 +90,7 @@ public class SecurityTokenConnection { private final Passphrase mPin; private final OpenPgpCommandApduFactory commandFactory; + private TokenType tokenType; private CardCapabilities mCardCapabilities; private OpenPgpCapabilities mOpenPgpCapabilities; private SecureMessaging mSecureMessaging; @@ -184,8 +184,8 @@ public class SecurityTokenConnection { mTransport.connect(); - // Connect on smartcard layer - // Command APDU (page 51) for SELECT FILE command (page 29) + determineTokenType(); + CommandApdu select = commandFactory.createSelectFileOpenPgpCommand(); ResponseApdu response = communicate(select); // activate connection @@ -210,6 +210,30 @@ public class SecurityTokenConnection { } } + private void determineTokenType() throws IOException { + tokenType = mTransport.getTokenTypeIfAvailable(); + if (tokenType != null) { + return; + } + + CommandApdu selectFidesmoApdu = commandFactory.createSelectFileCommand(AID_PREFIX_FIDESMO); + if (communicate(selectFidesmoApdu).isSuccess()) { + tokenType = TokenType.FIDESMO; + return; + } + + /* We could determine if this is a yubikey here. The info isn't used at the moment, so we save the roundtrip + // AID from https://github.com/Yubico/ykneo-oath/blob/master/build.xml#L16 + CommandApdu selectYubicoApdu = commandFactory.createSelectFileCommand("A000000527200101"); + if (communicate(selectYubicoApdu).isSuccess()) { + tokenType = TokenType.YUBIKEY_UNKNOWN; + return; + } + */ + + tokenType = TokenType.UNKNOWN; + } + @VisibleForTesting void setConnectionCapabilities(OpenPgpCapabilities openPgpCapabilities) throws IOException { this.mOpenPgpCapabilities = openPgpCapabilities; @@ -797,20 +821,6 @@ public class SecurityTokenConnection { return lastResponse; } - public boolean isFidesmoToken() { - if (isConnected()) { // Check if we can still talk to the card - try { - // By trying to select any apps that have the Fidesmo AID prefix we can - // see if it is a Fidesmo device or not - CommandApdu apdu = commandFactory.createSelectFileCommand(FIDESMO_APPS_AID_PREFIX); - return communicate(apdu).isSuccess(); - } catch (IOException e) { - Log.e(Constants.TAG, "Card communication failed!", e); - } - } - return false; - } - /** * Generates a key on the card in the given slot. If the slot is 0xB6 (the signature key), * this command also has the effect of resetting the digital signature counter. @@ -919,6 +929,10 @@ public class SecurityTokenConnection { return mTransport.isConnected(); } + public TokenType getTokenType() { + return tokenType; + } + public void clearSecureMessaging() { if (mSecureMessaging != null) { mSecureMessaging.clearSession(); @@ -931,10 +945,6 @@ public class SecurityTokenConnection { mSecureMessaging = sm; } - OpenPgpCapabilities getOpenPgpCapabilities() { - return mOpenPgpCapabilities; - } - public SecurityTokenInfo getTokenInfo() throws IOException { byte[] rawFingerprints = getFingerprints(); @@ -951,16 +961,9 @@ public class SecurityTokenConnection { byte[] pwInfo = getPwStatusBytes(); TransportType transportType = mTransport.getTransportType(); - TokenType tokenType; - if (transportType == TransportType.USB) { - tokenType = mTransport.getTokenType(); - } else { - tokenType = isFidesmoToken() ? TokenType.FIDESMO : TokenType.UNKNOWN; - } - // TODO: bail out earlier on unsupported tokens to not execute other commands - - SecurityTokenInfo info = SecurityTokenInfo.create(transportType, tokenType, fingerprints, aid, userId, url, pwInfo[4], pwInfo[6]); + SecurityTokenInfo info = SecurityTokenInfo + .create(transportType, tokenType, fingerprints, aid, userId, url, pwInfo[4], pwInfo[6]); if (! info.isSecurityTokenSupported()) { throw new UnsupportedSecurityTokenException(); @@ -974,5 +977,4 @@ public class SecurityTokenConnection { while (minv > 0) minv /= 10.0; return aid[6] + minv; } - } 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 11d74b7ec..08d0d194a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java @@ -93,29 +93,27 @@ public abstract class SecurityTokenInfo implements Parcelable { YUBIKEY_NEO, YUBIKEY_4, FIDESMO, NITROKEY_PRO, NITROKEY_STORAGE, NITROKEY_START, GNUK, LEDGER_NANO_S, UNKNOWN } - private static final HashSet SUPPORTED_SECURITY_TOKENS = new HashSet<>(Arrays.asList( - TokenType.FIDESMO, + private static final HashSet SUPPORTED_USB_TOKENS = new HashSet<>(Arrays.asList( TokenType.YUBIKEY_NEO, TokenType.YUBIKEY_4, TokenType.NITROKEY_PRO, TokenType.NITROKEY_STORAGE )); - private static final HashSet SUPPORTED_PUT_KEY = new HashSet<>(Arrays.asList( - TokenType.FIDESMO, + private static final HashSet SUPPORTED_USB_PUT_KEY = new HashSet<>(Arrays.asList( TokenType.YUBIKEY_NEO, TokenType.YUBIKEY_4 // Not clear, will be tested: https://github.com/open-keychain/open-keychain/issues/2069 )); public boolean isSecurityTokenSupported() { - boolean isKnownSupported = SUPPORTED_SECURITY_TOKENS.contains(getTokenType()); + boolean isKnownSupported = SUPPORTED_USB_TOKENS.contains(getTokenType()); boolean isNfcTransport = getTransportType() == TransportType.NFC; return isKnownSupported || isNfcTransport; } public boolean isPutKeySupported() { - boolean isKnownSupported = SUPPORTED_PUT_KEY.contains(getTokenType()); + boolean isKnownSupported = SUPPORTED_USB_PUT_KEY.contains(getTokenType()); boolean isNfcTransport = getTransportType() == TransportType.NFC; return isKnownSupported || isNfcTransport; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/Transport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/Transport.java index 033ac1dc4..10f2d53e6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/Transport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/Transport.java @@ -22,6 +22,9 @@ import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.Transport import java.io.IOException; +import android.support.annotation.Nullable; + + /** * Abstraction for transmitting APDU commands */ @@ -60,6 +63,6 @@ public interface Transport { void connect() throws IOException; TransportType getTransportType(); - - TokenType getTokenType(); + @Nullable + TokenType getTokenTypeIfAvailable(); } 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 ab8804a88..707c6a076 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 @@ -230,8 +230,9 @@ public class UsbTransport implements Transport { return TransportType.USB; } + @Nullable @Override - public TokenType getTokenType() { + public TokenType getTokenTypeIfAvailable() { switch (usbDevice.getVendorId()) { case VENDOR_YUBICO: { switch (usbDevice.getProductId()) { @@ -267,9 +268,9 @@ public class UsbTransport implements Transport { } } - Log.d(Constants.TAG, "Unknown USB token. Vendor ID: " + usbDevice.getVendorId() - + ", Product ID: " + usbDevice.getProductId()); - return TokenType.UNKNOWN; + Log.d(Constants.TAG, "Unknown USB token. Vendor ID: " + usbDevice.getVendorId() + ", Product ID: " + + usbDevice.getProductId()); + return null; } /** diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java index 756295f0e..117dba444 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java @@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.securitytoken.CardException; import org.sufficientlysecure.keychain.securitytoken.NfcTransport; import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection; import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TokenType; import org.sufficientlysecure.keychain.securitytoken.Transport; import org.sufficientlysecure.keychain.securitytoken.UnsupportedSecurityTokenException; import org.sufficientlysecure.keychain.securitytoken.UsbConnectionDispatcher; @@ -338,7 +339,7 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity } // 6A82 app not installed on security token! case 0x6A82: { - if (stConnection.isFidesmoToken()) { + if (stConnection.getTokenType() == TokenType.FIDESMO) { // Check if the Fidesmo app is installed if (isAndroidAppInstalled(FIDESMO_APP_PACKAGE)) { promptFidesmoPgpInstall();