From 0ca46c46aa48f4f64b84745998895450f216ab89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 23 Oct 2017 22:12:07 +0200 Subject: [PATCH 1/8] Route all class 11 USB devices to OpenKeychain, some class 3 for info --- .../securitytoken/usb/UsbTransport.java | 9 ++-- .../ui/base/BaseSecurityTokenActivity.java | 3 +- .../src/main/res/xml/usb_device_filter.xml | 51 +++++++++---------- 3 files changed, 29 insertions(+), 34 deletions(-) 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 d02c06f2d..bf814b6d6 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 @@ -105,8 +105,7 @@ public class UsbTransport implements Transport { public void connect() throws IOException { usbInterface = getSmartCardInterface(usbDevice); if (usbInterface == null) { - // Shouldn't happen as we whitelist only class 11 devices - throw new UsbTransportException("USB error - device doesn't have class 11 interface"); + throw new UsbTransportException("USB error: CCID mode must be enabled (no class 11 interface)"); } final Pair ioEndpoints = getIoEndpoints(usbInterface); @@ -114,16 +113,16 @@ public class UsbTransport implements Transport { UsbEndpoint usbBulkOut = ioEndpoints.second; if (usbBulkIn == null || usbBulkOut == null) { - throw new UsbTransportException("USB error - invalid class 11 interface"); + throw new UsbTransportException("USB error: invalid class 11 interface"); } usbConnection = usbManager.openDevice(usbDevice); if (usbConnection == null) { - throw new UsbTransportException("USB error - failed to connect to device"); + throw new UsbTransportException("USB error: failed to connect to device"); } if (!usbConnection.claimInterface(usbInterface, true)) { - throw new UsbTransportException("USB error - failed to claim interface"); + throw new UsbTransportException("USB error: failed to claim interface"); } byte[] rawDescriptors = usbConnection.getRawDescriptors(); 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 bfb17a3a3..4e8ba2e10 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 @@ -136,8 +136,9 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity public void securityTokenDiscovered(final Transport transport) { // Actual Security Token operations are executed in doInBackground to not block the UI thread - if (!mTagHandlingEnabled) + if (!mTagHandlingEnabled) { return; + } final SecurityTokenConnection stConnection = SecurityTokenConnection.getInstanceForTransport(transport, mCachedPin); diff --git a/OpenKeychain/src/main/res/xml/usb_device_filter.xml b/OpenKeychain/src/main/res/xml/usb_device_filter.xml index e6dd45759..d1ecfd32d 100644 --- a/OpenKeychain/src/main/res/xml/usb_device_filter.xml +++ b/OpenKeychain/src/main/res/xml/usb_device_filter.xml @@ -7,36 +7,31 @@ Note that values are decimal. --> - - - - - - - - - + - - + + - - - - - - - - + + + + + + + - - + + + + + + - - - - - - - From 14b74b75903caab25705df47f8e30796f87a4469 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 23 Oct 2017 16:31:22 +0200 Subject: [PATCH 2/8] add token transport and type to SecurityTokenInfo --- .../keychain/securitytoken/NfcTransport.java | 12 +++++ .../SecurityTokenConnection.java | 12 ++++- .../securitytoken/SecurityTokenInfo.java | 25 ++++++++--- .../keychain/securitytoken/Transport.java | 7 +++ .../securitytoken/usb/UsbTransport.java | 45 +++++++++++++++++++ .../ui/base/BaseSecurityTokenActivity.java | 1 + 6 files changed, 95 insertions(+), 7 deletions(-) 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 808b92410..222228938 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/NfcTransport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/NfcTransport.java @@ -22,6 +22,8 @@ import android.util.Log; import org.bouncycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TokenType; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TransportType; import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity; import java.io.IOException; @@ -103,6 +105,16 @@ public class NfcTransport implements Transport { mIsoCard.connect(); } + @Override + public TransportType getTransportType() { + return TransportType.NFC; + } + + @Override + public TokenType getTokenType() { + return null; + } + @Override public boolean equals(final Object o) { if (this == o) return true; 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 2a52f7586..c14b016c3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java @@ -48,6 +48,8 @@ import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TokenType; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TransportType; import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Passphrase; @@ -948,7 +950,15 @@ public class SecurityTokenConnection { String url = getUrl(); byte[] pwInfo = getPwStatusBytes(); - return SecurityTokenInfo.create(fingerprints, aid, userId, url, pwInfo[4], pwInfo[6]); + TransportType transportType = mTransport.getTransportType(); + TokenType tokenType; + if (transportType == TransportType.USB) { + tokenType = mTransport.getTokenType(); + } else { + tokenType = isFidesmoToken() ? TokenType.FIDESMO : TokenType.UNKNOWN; + } + + return SecurityTokenInfo.create(transportType, tokenType, fingerprints, aid, userId, url, pwInfo[4], pwInfo[6]); } public static double parseOpenPgpVersion(final byte[] aid) { 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 9a2cb6b37..a1ceef774 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java @@ -18,6 +18,9 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; public abstract class SecurityTokenInfo implements Parcelable { private static final byte[] EMPTY_ARRAY = new byte[20]; + public abstract TransportType getTransportType(); + public abstract TokenType getTokenType(); + public abstract List getFingerprints(); @Nullable public abstract byte[] getAid(); @@ -32,7 +35,8 @@ public abstract class SecurityTokenInfo implements Parcelable { return getFingerprints().isEmpty(); } - public static SecurityTokenInfo create(byte[][] fingerprints, byte[] aid, String userId, String url, + public static SecurityTokenInfo create(TransportType transportType, TokenType tokenType, byte[][] fingerprints, + byte[] aid, String userId, String url, int verifyRetries, int verifyAdminRetries) { ArrayList fingerprintList = new ArrayList<>(fingerprints.length); for (byte[] fingerprint : fingerprints) { @@ -40,14 +44,15 @@ public abstract class SecurityTokenInfo implements Parcelable { fingerprintList.add(fingerprint); } } - return new AutoValue_SecurityTokenInfo(fingerprintList, aid, userId, url, verifyRetries, verifyAdminRetries); + return new AutoValue_SecurityTokenInfo( + transportType, tokenType, fingerprintList, aid, userId, url, verifyRetries, verifyAdminRetries); } public static SecurityTokenInfo newInstanceDebugKeyserver() { if (!BuildConfig.DEBUG) { throw new UnsupportedOperationException("This operation is only available in debug builds!"); } - return SecurityTokenInfo.create( + return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN, new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("1efdb4845ca242ca6977fddb1f788094fd3b430a") }, Hex.decode("010203040506"), "yubinu2@mugenguild.com", null, 3, 3); } @@ -56,7 +61,7 @@ public abstract class SecurityTokenInfo implements Parcelable { if (!BuildConfig.DEBUG) { throw new UnsupportedOperationException("This operation is only available in debug builds!"); } - return SecurityTokenInfo.create( + return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN, new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") }, Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 3, 3); } @@ -65,7 +70,7 @@ public abstract class SecurityTokenInfo implements Parcelable { if (!BuildConfig.DEBUG) { throw new UnsupportedOperationException("This operation is only available in debug builds!"); } - return SecurityTokenInfo.create( + return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN, new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") }, Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 3); } @@ -74,9 +79,17 @@ public abstract class SecurityTokenInfo implements Parcelable { if (!BuildConfig.DEBUG) { throw new UnsupportedOperationException("This operation is only available in debug builds!"); } - return SecurityTokenInfo.create( + return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN, new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") }, Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 0); } + public enum TransportType { + NFC, USB + } + + public enum TokenType { + YUBIKEY_NEO, YUBIKEY_4, FIDESMO, NITROKEY_PRO, NITROKEY_STORAGE, NITROKEY_START, GNUK, LEDGER_NANO_S, UNKNOWN + } + } 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 2d82842dc..033ac1dc4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/Transport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/Transport.java @@ -17,6 +17,9 @@ package org.sufficientlysecure.keychain.securitytoken; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TokenType; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TransportType; + import java.io.IOException; /** @@ -55,4 +58,8 @@ public interface Transport { * @throws IOException */ void connect() throws IOException; + + TransportType getTransportType(); + + TokenType getTokenType(); } 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 bf814b6d6..a92612ef0 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 @@ -28,6 +28,8 @@ import android.support.annotation.Nullable; import android.util.Pair; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TokenType; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TransportType; import org.sufficientlysecure.keychain.securitytoken.Transport; import org.sufficientlysecure.keychain.securitytoken.CommandApdu; import org.sufficientlysecure.keychain.securitytoken.ResponseApdu; @@ -201,6 +203,49 @@ public class UsbTransport implements Transport { return usbDevice != null ? usbDevice.hashCode() : 0; } + @Override + public TransportType getTransportType() { + return TransportType.USB; + } + + @Override + public TokenType getTokenType() { + switch (usbDevice.getVendorId()) { + case 4176: { + switch (usbDevice.getProductId()) { + case 273: + case 274: + case 277: + case 278: + return TokenType.YUBIKEY_NEO; + case 1028: + case 1029: + case 1030: + case 1031: + return TokenType.YUBIKEY_4; + } + break; + } + case 8352: { + switch (usbDevice.getProductId()) { + case 16648: + return TokenType.NITROKEY_PRO; + case 16913: + return TokenType.NITROKEY_START; + case 16649: + return TokenType.NITROKEY_STORAGE; + } + break; + } + case 9035: { + return TokenType.GNUK; + } + case 11415: { + return TokenType.LEDGER_NANO_S; + } + } + throw new IllegalStateException("Unhandled usb token type!"); + } /** * Get first class 11 (Chip/Smartcard) interface of the device 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 4e8ba2e10..f39d6bcd2 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.UsbConnectionDispatcher; import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransport; From e7705eaca8fcfc1ee7c2f55b176260b7971f2950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 23 Oct 2017 22:52:29 +0200 Subject: [PATCH 3/8] Use constants for vendor and product IDs --- .../securitytoken/usb/UsbTransport.java | 49 +++++++++++++------ .../src/main/res/xml/usb_device_filter.xml | 12 ++--- 2 files changed, 38 insertions(+), 23 deletions(-) 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 a92612ef0..80c7e7252 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 @@ -56,6 +56,25 @@ public class UsbTransport implements Transport { private static final int MASK_SHORT_APDU = 0x20000; private static final int MASK_EXTENDED_APDU = 0x40000; + // https://github.com/Yubico/yubikey-personalization/blob/master/ykcore/ykdef.h + private static final int VENDOR_YUBICO = 4176; + private static final int PRODUCT_YUBIKEY_NEO_OTP_CCID = 273; + private static final int PRODUCT_YUBIKEY_NEO_CCID = 274; + private static final int PRODUCT_YUBIKEY_NEO_U2F_CCID = 277; + private static final int PRODUCT_YUBIKEY_NEO_OTP_U2F_CCID = 278; + private static final int PRODUCT_YUBIKEY_4_CCID = 1028; + private static final int PRODUCT_YUBIKEY_4_OTP_CCID = 1029; + private static final int PRODUCT_YUBIKEY_4_U2F_CCID = 1030; + private static final int PRODUCT_YUBIKEY_4_OTP_U2F_CCID = 1031; + + // https://www.nitrokey.com/de/documentation/installation#p:nitrokey-pro&os:linux + private static final int VENDOR_NITROKEY = 8352; + private static final int PRODUCT_NITROKEY_PRO = 16648; + private static final int PRODUCT_NITROKEY_START = 16913; + private static final int PRODUCT_NITROKEY_STORAGE = 16649; + + private static final int VENDOR_FSIJ = 9035; + private static final int VENDOR_LEDGER = 11415; private final UsbDevice usbDevice; private final UsbManager usbManager; @@ -211,36 +230,36 @@ public class UsbTransport implements Transport { @Override public TokenType getTokenType() { switch (usbDevice.getVendorId()) { - case 4176: { + case VENDOR_YUBICO: { switch (usbDevice.getProductId()) { - case 273: - case 274: - case 277: - case 278: + case PRODUCT_YUBIKEY_NEO_OTP_CCID: + case PRODUCT_YUBIKEY_NEO_CCID: + case PRODUCT_YUBIKEY_NEO_U2F_CCID: + case PRODUCT_YUBIKEY_NEO_OTP_U2F_CCID: return TokenType.YUBIKEY_NEO; - case 1028: - case 1029: - case 1030: - case 1031: + case PRODUCT_YUBIKEY_4_CCID: + case PRODUCT_YUBIKEY_4_OTP_CCID: + case PRODUCT_YUBIKEY_4_U2F_CCID: + case PRODUCT_YUBIKEY_4_OTP_U2F_CCID: return TokenType.YUBIKEY_4; } break; } - case 8352: { + case VENDOR_NITROKEY: { switch (usbDevice.getProductId()) { - case 16648: + case PRODUCT_NITROKEY_PRO: return TokenType.NITROKEY_PRO; - case 16913: + case PRODUCT_NITROKEY_START: return TokenType.NITROKEY_START; - case 16649: + case PRODUCT_NITROKEY_STORAGE: return TokenType.NITROKEY_STORAGE; } break; } - case 9035: { + case VENDOR_FSIJ: { return TokenType.GNUK; } - case 11415: { + case VENDOR_LEDGER: { return TokenType.LEDGER_NANO_S; } } diff --git a/OpenKeychain/src/main/res/xml/usb_device_filter.xml b/OpenKeychain/src/main/res/xml/usb_device_filter.xml index d1ecfd32d..4686b6da9 100644 --- a/OpenKeychain/src/main/res/xml/usb_device_filter.xml +++ b/OpenKeychain/src/main/res/xml/usb_device_filter.xml @@ -1,12 +1,4 @@ - - From 0920d9757228930ddc44ae802b6a18fb0f4e96a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 25 Oct 2017 14:05:52 +0200 Subject: [PATCH 4/8] Check for security token support --- .../keychain/securitytoken/NfcTransport.java | 10 +++++- .../SecurityTokenConnection.java | 13 +++++-- .../securitytoken/SecurityTokenInfo.java | 35 ++++++++++++++++--- .../UnsupportedSecurityTokenException.java | 15 ++++++++ .../securitytoken/usb/UsbTransport.java | 8 ++++- .../ui/base/BaseSecurityTokenActivity.java | 23 ++++++------ .../ui/token/ManageSecurityTokenContract.java | 1 + .../ui/token/ManageSecurityTokenFragment.java | 5 +++ .../token/ManageSecurityTokenPresenter.java | 8 +++++ .../create_security_token_import_fragment.xml | 25 +++++++++++++ OpenKeychain/src/main/res/values/strings.xml | 2 ++ 11 files changed, 126 insertions(+), 19 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UnsupportedSecurityTokenException.java 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 222228938..e4d811034 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/NfcTransport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/NfcTransport.java @@ -37,6 +37,14 @@ public class NfcTransport implements Transport { private final Tag mTag; private IsoCard mIsoCard; + public static class IsoDepNotSupportedException extends IOException { + + IsoDepNotSupportedException(String detailMessage) { + super(detailMessage); + } + + } + public NfcTransport(Tag tag) { this.mTag = tag; } @@ -98,7 +106,7 @@ public class NfcTransport implements Transport { public void connect() throws IOException { mIsoCard = AndroidCard.get(mTag); if (mIsoCard == null) { - throw new BaseSecurityTokenActivity.IsoDepNotSupportedException("Tag does not support ISO-DEP (ISO 14443-4)"); + throw new IsoDepNotSupportedException("Tag does not support ISO-DEP (ISO 14443-4)"); } mIsoCard.setTimeout(TIMEOUT); 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 c14b016c3..30df05737 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java @@ -955,10 +955,18 @@ public class SecurityTokenConnection { if (transportType == TransportType.USB) { tokenType = mTransport.getTokenType(); } else { - tokenType = isFidesmoToken() ? TokenType.FIDESMO : TokenType.UNKNOWN; + tokenType = isFidesmoToken() ? TokenType.FIDESMO : TokenType.UNKNOWN_NFC; } - return SecurityTokenInfo.create(transportType, tokenType, fingerprints, aid, userId, url, pwInfo[4], pwInfo[6]); + // 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]); + + if (! info.isSecurityTokenSupported()) { + throw new UnsupportedSecurityTokenException(); + } + + return info; } public static double parseOpenPgpVersion(final byte[] aid) { @@ -966,4 +974,5 @@ 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 a1ceef774..c09d958d1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java @@ -3,6 +3,7 @@ package org.sufficientlysecure.keychain.securitytoken; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import android.os.Parcelable; @@ -52,7 +53,7 @@ public abstract class SecurityTokenInfo implements Parcelable { if (!BuildConfig.DEBUG) { throw new UnsupportedOperationException("This operation is only available in debug builds!"); } - return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN, + return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN_NFC, new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("1efdb4845ca242ca6977fddb1f788094fd3b430a") }, Hex.decode("010203040506"), "yubinu2@mugenguild.com", null, 3, 3); } @@ -61,7 +62,7 @@ public abstract class SecurityTokenInfo implements Parcelable { if (!BuildConfig.DEBUG) { throw new UnsupportedOperationException("This operation is only available in debug builds!"); } - return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN, + return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN_NFC, new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") }, Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 3, 3); } @@ -70,7 +71,7 @@ public abstract class SecurityTokenInfo implements Parcelable { if (!BuildConfig.DEBUG) { throw new UnsupportedOperationException("This operation is only available in debug builds!"); } - return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN, + return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN_NFC, new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") }, Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 3); } @@ -79,7 +80,7 @@ public abstract class SecurityTokenInfo implements Parcelable { if (!BuildConfig.DEBUG) { throw new UnsupportedOperationException("This operation is only available in debug builds!"); } - return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN, + return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN_NFC, new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") }, Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 0); } @@ -89,7 +90,31 @@ public abstract class SecurityTokenInfo implements Parcelable { } public enum TokenType { - YUBIKEY_NEO, YUBIKEY_4, FIDESMO, NITROKEY_PRO, NITROKEY_STORAGE, NITROKEY_START, GNUK, LEDGER_NANO_S, UNKNOWN + YUBIKEY_NEO, YUBIKEY_4, FIDESMO, NITROKEY_PRO, NITROKEY_STORAGE, NITROKEY_START, GNUK, LEDGER_NANO_S, UNKNOWN_NFC, UNKNOWN_USB + } + + private static final HashSet SUPPORTED_SECURITY_TOKENS = new HashSet<>(Arrays.asList( + TokenType.UNKNOWN_NFC, + TokenType.FIDESMO, + TokenType.YUBIKEY_NEO, + TokenType.YUBIKEY_4, + TokenType.NITROKEY_PRO, + TokenType.NITROKEY_STORAGE + )); + + private static final HashSet SUPPORTED_PUT_KEY = new HashSet<>(Arrays.asList( + TokenType.UNKNOWN_NFC, + TokenType.FIDESMO, + TokenType.YUBIKEY_NEO, + TokenType.YUBIKEY_4 // Not clear, will be tested: https://github.com/open-keychain/open-keychain/issues/2069 + )); + + public boolean isSecurityTokenSupported() { + return SUPPORTED_SECURITY_TOKENS.contains(getTokenType()); + } + + public boolean isPutKeySupported() { + return SUPPORTED_PUT_KEY.contains(getTokenType()); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UnsupportedSecurityTokenException.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UnsupportedSecurityTokenException.java new file mode 100644 index 000000000..184f7296a --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UnsupportedSecurityTokenException.java @@ -0,0 +1,15 @@ +package org.sufficientlysecure.keychain.securitytoken; + +import java.io.IOException; + +public class UnsupportedSecurityTokenException extends IOException { + + UnsupportedSecurityTokenException() { + super(); + } + + UnsupportedSecurityTokenException(String detailMessage) { + super(detailMessage); + } + +} \ No newline at end of file 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 80c7e7252..250ff5520 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 @@ -101,6 +101,7 @@ public class UsbTransport implements Transport { /** * Check if device is was connected to and still is connected + * * @return true if device is connected */ @Override @@ -112,6 +113,7 @@ public class UsbTransport implements Transport { /** * Check if Transport supports persistent connections e.g connections which can * handle multiple operations in one session + * * @return true if transport supports persistent connections */ @Override @@ -199,6 +201,7 @@ public class UsbTransport implements Transport { /** * Transmit and receive data + * * @param data data to transmit * @return received data */ @@ -263,7 +266,10 @@ public class UsbTransport implements Transport { return TokenType.LEDGER_NANO_S; } } - throw new IllegalStateException("Unhandled usb token type!"); + + Log.d(Constants.TAG, "Unknown USB token. Vendor ID: " + usbDevice.getVendorId() + + ", Product ID: " + usbDevice.getProductId()); + return TokenType.UNKNOWN_USB; } /** 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 f39d6bcd2..2f5da22de 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,10 +45,11 @@ 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; import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransport; +import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; @@ -238,11 +239,21 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity return; } - if (e instanceof IsoDepNotSupportedException) { + if (e instanceof NfcTransport.IsoDepNotSupportedException) { onSecurityTokenError(getString(R.string.security_token_error_iso_dep_not_supported)); return; } + if (e instanceof UsbTransportException) { + onSecurityTokenError(getString(R.string.security_token_error, e.getMessage())); + return; + } + + if (e instanceof UnsupportedSecurityTokenException) { + onSecurityTokenError(getString(R.string.security_token_not_supported)); + return; + } + short status; if (e instanceof CardException) { status = ((CardException) e).getResponseCode(); @@ -442,14 +453,6 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity doSecurityTokenInBackground(stConnection); } - public static class IsoDepNotSupportedException extends IOException { - - public IsoDepNotSupportedException(String detailMessage) { - super(detailMessage); - } - - } - /** * Ask user if she wants to install PGP onto her Fidesmo token */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenContract.java index 8fe8d4333..ae110d8bc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenContract.java @@ -79,6 +79,7 @@ class ManageSecurityTokenContract { void showActionRetryOrFromFile(); void showActionLocked(int unlockAttempts); void showActionEmptyToken(); + void showActionUnsupportedToken(); void hideAction(); void operationImportKey(byte[] importKeyData); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenFragment.java index e62675bef..198b4ba09 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenFragment.java @@ -263,6 +263,11 @@ public class ManageSecurityTokenFragment extends Fragment implements ManageSecur actionAnimator.setDisplayedChildId(R.id.token_layout_empty); } + @Override + public void showActionUnsupportedToken() { + actionAnimator.setDisplayedChildId(R.id.token_layout_unsupported); + } + @Override public void hideAction() { actionAnimator.setDisplayedChild(0); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenPresenter.java index 2eeef8bca..a52a0ad87 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenPresenter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenPresenter.java @@ -164,6 +164,14 @@ class ManageSecurityTokenPresenter implements ManageSecurityTokenMvpPresenter { private void performKeyCheck() { boolean keyIsEmpty = tokenInfo.isEmpty(); + boolean putKeyIsSupported = tokenInfo.isPutKeySupported(); + + if (keyIsEmpty && !putKeyIsSupported) { + view.statusLineOk(); + view.showActionUnsupportedToken(); + return; + } + if (keyIsEmpty) { boolean tokenIsAdminLocked = tokenInfo.getVerifyAdminRetries() == 0; if (tokenIsAdminLocked) { diff --git a/OpenKeychain/src/main/res/layout/create_security_token_import_fragment.xml b/OpenKeychain/src/main/res/layout/create_security_token_import_fragment.xml index 9a35ad763..a4a9383b6 100644 --- a/OpenKeychain/src/main/res/layout/create_security_token_import_fragment.xml +++ b/OpenKeychain/src/main/res/layout/create_security_token_import_fragment.xml @@ -370,6 +370,31 @@ + + + + + + + + diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 070eadd4c..4326864fd 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -1580,6 +1580,7 @@ "Security Token reported invalid %s byte." "Security Token has been taken off too early. Keep the Security Token at the back until the operation finishes." "Security Token does not support the required communication standard (ISO-DEP, ISO 14443-4)" + "This Security Token is not supported by OpenKeychain." "Try again" Delete original file @@ -1895,6 +1896,7 @@ Key found! Ready for use! Token is empty + "Please set up this Security Token using GnuPG. OpenKeychain currently only supports singing/decrypting with this Security Token." Retry Search Load from File Reset Security Token From ed4e21957c49e07c9679854d59f1c53a5cc35dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 25 Oct 2017 15:46:46 +0200 Subject: [PATCH 5/8] Better error handling for generic IOException --- .../ui/base/BaseSecurityTokenActivity.java | 231 +++++++++--------- OpenKeychain/src/main/res/values/strings.xml | 1 + 2 files changed, 115 insertions(+), 117 deletions(-) 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 2f5da22de..756295f0e 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 @@ -233,6 +233,7 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity } private void handleSecurityTokenError(SecurityTokenConnection stConnection, IOException e) { + Log.d(Constants.TAG, "Exception in handleSecurityTokenError", e); if (e instanceof TagLostException) { onSecurityTokenError(getString(R.string.security_token_error_tag_lost)); @@ -254,140 +255,136 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity return; } - short status; if (e instanceof CardException) { - status = ((CardException) e).getResponseCode(); - } else { - status = -1; - } + short status = ((CardException) e).getResponseCode(); - // Wrong PIN, a status of 63CX indicates X attempts remaining. - // NOTE: Used in ykneo-openpgp version < 1.0.10, changed to 0x6982 in 1.0.11 - // https://github.com/Yubico/ykneo-openpgp/commit/90c2b91e86fb0e43ee234dd258834e75e3416410 - if ((status & (short) 0xFFF0) == 0x63C0) { - int tries = status & 0x000F; + // Wrong PIN, a status of 63CX indicates X attempts remaining. + // NOTE: Used in ykneo-openpgp version < 1.0.10, changed to 0x6982 in 1.0.11 + // https://github.com/Yubico/ykneo-openpgp/commit/90c2b91e86fb0e43ee234dd258834e75e3416410 + if ((status & (short) 0xFFF0) == 0x63C0) { + int tries = status & 0x000F; - SecurityTokenInfo tokeninfo = null; - try { - tokeninfo = stConnection.getTokenInfo(); - } catch (IOException e2) { - // don't care - } - // hook to do something different when PIN is wrong - onSecurityTokenPinError( - getResources().getQuantityString(R.plurals.security_token_error_pin, tries, tries), tokeninfo); - return; - } - - Log.d(Constants.TAG, "security token exception", e); - - // Otherwise, all status codes are fixed values. - switch (status) { - - // These error conditions are likely to be experienced by an end user. - - /* OpenPGP Card Spec: Security status not satisfied, PW wrong, - PW not checked (command not allowed), Secure messaging incorrect (checksum and/or cryptogram) */ - // NOTE: Used in ykneo-openpgp >= 1.0.11 for wrong PIN - case 0x6982: { SecurityTokenInfo tokeninfo = null; try { tokeninfo = stConnection.getTokenInfo(); } catch (IOException e2) { // don't care } + // hook to do something different when PIN is wrong + onSecurityTokenPinError( + getResources().getQuantityString(R.plurals.security_token_error_pin, tries, tries), tokeninfo); + return; + } - // hook to do something different when PIN is wrong - onSecurityTokenPinError(getString(R.string.security_token_error_security_not_satisfied), tokeninfo); - break; - } - /* OpenPGP Card Spec: Selected file in termination state */ - case 0x6285: { - onSecurityTokenError(getString(R.string.security_token_error_terminated)); - break; - } - /* OpenPGP Card Spec: Wrong length (Lc and/or Le) */ - // NOTE: Used in ykneo-openpgp < 1.0.10 for too short PIN, changed in 1.0.11 to 0x6A80 for too short PIN - // https://github.com/Yubico/ykneo-openpgp/commit/b49ce8241917e7c087a4dab7b2c755420ff4500f - case 0x6700: { - // hook to do something different when PIN is wrong - onSecurityTokenPinError(getString(R.string.security_token_error_wrong_length), null); - break; - } - /* OpenPGP Card Spec: Incorrect parameters in the data field */ - // NOTE: Used in ykneo-openpgp >= 1.0.11 for too short PIN - case 0x6A80: { - // hook to do something different when PIN is wrong - onSecurityTokenPinError(getString(R.string.security_token_error_bad_data), null); - break; - } - /* OpenPGP Card Spec: Authentication method blocked, PW blocked (error counter zero) */ - case 0x6983: { - onSecurityTokenError(getString(R.string.security_token_error_authentication_blocked)); - break; - } - /* OpenPGP Card Spec: Condition of use not satisfied */ - case 0x6985: { - onSecurityTokenError(getString(R.string.security_token_error_conditions_not_satisfied)); - break; - } - /* OpenPGP Card Spec: SM data objects incorrect (e.g. wrong TLV-structure in command data) */ - // NOTE: 6A88 is "Not Found" in the spec, but ykneo-openpgp also returns 6A83 for this in some cases. - case 0x6A88: - case 0x6A83: { - onSecurityTokenError(getString(R.string.security_token_error_data_not_found)); - break; - } - // 6F00 is a JavaCard proprietary status code, SW_UNKNOWN, and usually represents an - // unhandled exception on the security token. - case 0x6F00: { - onSecurityTokenError(getString(R.string.security_token_error_unknown)); - break; - } - // 6A82 app not installed on security token! - case 0x6A82: { - if (stConnection.isFidesmoToken()) { - // Check if the Fidesmo app is installed - if (isAndroidAppInstalled(FIDESMO_APP_PACKAGE)) { - promptFidesmoPgpInstall(); - } else { - promptFidesmoAppInstall(); + // Otherwise, all status codes are fixed values. + switch (status) { + /* OpenPGP Card Spec: Security status not satisfied, PW wrong, + PW not checked (command not allowed), Secure messaging incorrect (checksum and/or cryptogram) */ + // NOTE: Used in ykneo-openpgp >= 1.0.11 for wrong PIN + case 0x6982: { + SecurityTokenInfo tokeninfo = null; + try { + tokeninfo = stConnection.getTokenInfo(); + } catch (IOException e2) { + // don't care } - } else { // Other (possibly) compatible hardware - onSecurityTokenError(getString(R.string.security_token_error_pgp_app_not_installed)); + + // hook to do something different when PIN is wrong + onSecurityTokenPinError(getString(R.string.security_token_error_security_not_satisfied), tokeninfo); + break; + } + /* OpenPGP Card Spec: Selected file in termination state */ + case 0x6285: { + onSecurityTokenError(getString(R.string.security_token_error_terminated)); + break; + } + /* OpenPGP Card Spec: Wrong length (Lc and/or Le) */ + // NOTE: Used in ykneo-openpgp < 1.0.10 for too short PIN, changed in 1.0.11 to 0x6A80 for too short PIN + // https://github.com/Yubico/ykneo-openpgp/commit/b49ce8241917e7c087a4dab7b2c755420ff4500f + case 0x6700: { + // hook to do something different when PIN is wrong + onSecurityTokenPinError(getString(R.string.security_token_error_wrong_length), null); + break; + } + /* OpenPGP Card Spec: Incorrect parameters in the data field */ + // NOTE: Used in ykneo-openpgp >= 1.0.11 for too short PIN + case 0x6A80: { + // hook to do something different when PIN is wrong + onSecurityTokenPinError(getString(R.string.security_token_error_bad_data), null); + break; + } + /* OpenPGP Card Spec: Authentication method blocked, PW blocked (error counter zero) */ + case 0x6983: { + onSecurityTokenError(getString(R.string.security_token_error_authentication_blocked)); + break; + } + /* OpenPGP Card Spec: Condition of use not satisfied */ + case 0x6985: { + onSecurityTokenError(getString(R.string.security_token_error_conditions_not_satisfied)); + break; + } + /* OpenPGP Card Spec: SM data objects incorrect (e.g. wrong TLV-structure in command data) */ + // NOTE: 6A88 is "Not Found" in the spec, but ykneo-openpgp also returns 6A83 for this in some cases. + case 0x6A88: + case 0x6A83: { + onSecurityTokenError(getString(R.string.security_token_error_data_not_found)); + break; + } + // 6F00 is a JavaCard proprietary status code, SW_UNKNOWN, and usually represents an + // unhandled exception on the security token. + case 0x6F00: { + onSecurityTokenError(getString(R.string.security_token_error_unknown)); + break; + } + // 6A82 app not installed on security token! + case 0x6A82: { + if (stConnection.isFidesmoToken()) { + // Check if the Fidesmo app is installed + if (isAndroidAppInstalled(FIDESMO_APP_PACKAGE)) { + promptFidesmoPgpInstall(); + } else { + promptFidesmoAppInstall(); + } + } + break; } - break; - } - // These errors should not occur in everyday use; if they are returned, it means we - // made a mistake sending data to the token, or the token is misbehaving. + // These errors should not occur in everyday use; if they are returned, it means we + // made a mistake sending data to the token, or the token is misbehaving. - /* OpenPGP Card Spec: Last command of the chain expected */ - case 0x6883: { - onSecurityTokenError(getString(R.string.security_token_error_chaining_error)); - break; - } - /* OpenPGP Card Spec: Wrong parameters P1-P2 */ - case 0x6B00: { - onSecurityTokenError(getString(R.string.security_token_error_header, "P1/P2")); - break; - } - /* OpenPGP Card Spec: Instruction (INS) not supported */ - case 0x6D00: { - onSecurityTokenError(getString(R.string.security_token_error_header, "INS")); - break; - } - /* OpenPGP Card Spec: Class (CLA) not supported */ - case 0x6E00: { - onSecurityTokenError(getString(R.string.security_token_error_header, "CLA")); - break; - } - default: { - onSecurityTokenError(getString(R.string.security_token_error, e.getMessage())); - break; + /* OpenPGP Card Spec: Last command of the chain expected */ + case 0x6883: { + onSecurityTokenError(getString(R.string.security_token_error_chaining_error)); + break; + } + /* OpenPGP Card Spec: Wrong parameters P1-P2 */ + case 0x6B00: { + onSecurityTokenError(getString(R.string.security_token_error_header, "P1/P2")); + break; + } + /* OpenPGP Card Spec: Instruction (INS) not supported */ + case 0x6D00: { + onSecurityTokenError(getString(R.string.security_token_error_header, "INS")); + break; + } + /* OpenPGP Card Spec: Class (CLA) not supported */ + case 0x6E00: { + onSecurityTokenError(getString(R.string.security_token_error_header, "CLA")); + break; + } + default: { + onSecurityTokenError(getString(R.string.security_token_error, e.getMessage())); + break; + } } } + // fallback for generic IOException + if (e.getMessage() != null) { + onSecurityTokenError(getString(R.string.security_token_error, e.getMessage())); + } else { + onSecurityTokenError(getString(R.string.security_token_error_generic)); + } } /** diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 4326864fd..44be5ffd9 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -1580,6 +1580,7 @@ "Security Token reported invalid %s byte." "Security Token has been taken off too early. Keep the Security Token at the back until the operation finishes." "Security Token does not support the required communication standard (ISO-DEP, ISO 14443-4)" + "Generic Error: Most probably, the Security Token has been taken off too early." "This Security Token is not supported by OpenKeychain." "Try again" Delete original file From 8649332bdcf25b357a6dc1222131d3922cfd7737 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 26 Oct 2017 17:00:48 +0200 Subject: [PATCH 6/8] don't distinguish unknown tokens by transport --- .../SecurityTokenConnection.java | 2 +- .../securitytoken/SecurityTokenInfo.java | 22 +++++++++++-------- .../securitytoken/usb/UsbTransport.java | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) 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 30df05737..495ffddb8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java @@ -955,7 +955,7 @@ public class SecurityTokenConnection { if (transportType == TransportType.USB) { tokenType = mTransport.getTokenType(); } else { - tokenType = isFidesmoToken() ? TokenType.FIDESMO : TokenType.UNKNOWN_NFC; + tokenType = isFidesmoToken() ? TokenType.FIDESMO : TokenType.UNKNOWN; } // TODO: bail out earlier on unsupported tokens to not execute other commands 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 c09d958d1..11d74b7ec 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java @@ -53,7 +53,7 @@ public abstract class SecurityTokenInfo implements Parcelable { if (!BuildConfig.DEBUG) { throw new UnsupportedOperationException("This operation is only available in debug builds!"); } - return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN_NFC, + return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN, new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("1efdb4845ca242ca6977fddb1f788094fd3b430a") }, Hex.decode("010203040506"), "yubinu2@mugenguild.com", null, 3, 3); } @@ -62,7 +62,7 @@ public abstract class SecurityTokenInfo implements Parcelable { if (!BuildConfig.DEBUG) { throw new UnsupportedOperationException("This operation is only available in debug builds!"); } - return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN_NFC, + return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN, new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") }, Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 3, 3); } @@ -71,7 +71,7 @@ public abstract class SecurityTokenInfo implements Parcelable { if (!BuildConfig.DEBUG) { throw new UnsupportedOperationException("This operation is only available in debug builds!"); } - return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN_NFC, + return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN, new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") }, Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 3); } @@ -80,7 +80,7 @@ public abstract class SecurityTokenInfo implements Parcelable { if (!BuildConfig.DEBUG) { throw new UnsupportedOperationException("This operation is only available in debug builds!"); } - return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN_NFC, + return SecurityTokenInfo.create(TransportType.NFC, TokenType.UNKNOWN, new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") }, Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 0); } @@ -90,11 +90,10 @@ public abstract class SecurityTokenInfo implements Parcelable { } public enum TokenType { - YUBIKEY_NEO, YUBIKEY_4, FIDESMO, NITROKEY_PRO, NITROKEY_STORAGE, NITROKEY_START, GNUK, LEDGER_NANO_S, UNKNOWN_NFC, UNKNOWN_USB + 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.UNKNOWN_NFC, TokenType.FIDESMO, TokenType.YUBIKEY_NEO, TokenType.YUBIKEY_4, @@ -103,18 +102,23 @@ public abstract class SecurityTokenInfo implements Parcelable { )); private static final HashSet SUPPORTED_PUT_KEY = new HashSet<>(Arrays.asList( - TokenType.UNKNOWN_NFC, TokenType.FIDESMO, TokenType.YUBIKEY_NEO, TokenType.YUBIKEY_4 // Not clear, will be tested: https://github.com/open-keychain/open-keychain/issues/2069 )); public boolean isSecurityTokenSupported() { - return SUPPORTED_SECURITY_TOKENS.contains(getTokenType()); + boolean isKnownSupported = SUPPORTED_SECURITY_TOKENS.contains(getTokenType()); + boolean isNfcTransport = getTransportType() == TransportType.NFC; + + return isKnownSupported || isNfcTransport; } public boolean isPutKeySupported() { - return SUPPORTED_PUT_KEY.contains(getTokenType()); + boolean isKnownSupported = SUPPORTED_PUT_KEY.contains(getTokenType()); + boolean isNfcTransport = getTransportType() == TransportType.NFC; + + return isKnownSupported || isNfcTransport; } } 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 250ff5520..ab8804a88 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 @@ -269,7 +269,7 @@ public class UsbTransport implements Transport { Log.d(Constants.TAG, "Unknown USB token. Vendor ID: " + usbDevice.getVendorId() + ", Product ID: " + usbDevice.getProductId()); - return TokenType.UNKNOWN_USB; + return TokenType.UNKNOWN; } /** From b6236bde59ea1bcd7dedc4a5f0bcd6556948cfa1 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 26 Oct 2017 17:18:13 +0200 Subject: [PATCH 7/8] determine token type during connect --- .../keychain/securitytoken/NfcTransport.java | 5 +- .../SecurityTokenConnection.java | 66 ++++++++++--------- .../securitytoken/SecurityTokenInfo.java | 10 ++- .../keychain/securitytoken/Transport.java | 7 +- .../securitytoken/usb/UsbTransport.java | 9 +-- .../ui/base/BaseSecurityTokenActivity.java | 3 +- 6 files changed, 54 insertions(+), 46 deletions(-) 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(); From 95034e36b19588f9e088948fb88503ea668b4621 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 26 Oct 2017 17:44:13 +0200 Subject: [PATCH 8/8] fix unit test --- .../SecurityTokenConnection.java | 3 ++- .../SecurityTokenConnectionTest.java | 20 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) 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 5fe3f57cf..70fff4866 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java @@ -210,7 +210,8 @@ public class SecurityTokenConnection { } } - private void determineTokenType() throws IOException { + @VisibleForTesting + void determineTokenType() throws IOException { tokenType = mTransport.getTokenTypeIfAvailable(); if (tokenType != null) { return; diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnectionTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnectionTest.java index 76ceb5b47..e79b3add3 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnectionTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnectionTest.java @@ -8,9 +8,12 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; +import org.mockito.internal.verification.VerificationModeFactory; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadows.ShadowLog; import org.sufficientlysecure.keychain.KeychainTestRunner; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TokenType; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TransportType; import org.sufficientlysecure.keychain.util.Passphrase; import static org.mockito.Matchers.eq; @@ -23,19 +26,25 @@ import static org.mockito.Mockito.when; @RunWith(KeychainTestRunner.class) public class SecurityTokenConnectionTest { + private Transport transport; + @Before public void setUp() throws Exception { ShadowLog.stream = System.out; + + transport = mock(Transport.class); + when(transport.getTransportType()).thenReturn(TransportType.USB); + when(transport.getTokenTypeIfAvailable()).thenReturn(TokenType.YUBIKEY_NEO); } @Test public void test_connectToDevice() throws Exception { - Transport transport = mock(Transport.class); SecurityTokenConnection securityTokenConnection = new SecurityTokenConnection(transport, new Passphrase("123456"), new OpenPgpCommandApduFactory()); - - String[] dialog = { "00a4040006d27600012401", "9000", - "00ca006e00", + String[] dialog = { + "00a4040006d27600012401", // select openpgp applet + "9000", + "00ca006e00", // get application related data "6e81de4f10d27600012401020000060364311500005f520f0073000080000000000000000000007381b7c00af" + "00000ff04c000ff00ffc106010800001103c206010800001103c306010800001103c407007f7f7f03030" + "3c53c4ec5fee25c4e89654d58cad8492510a89d3c3d8468da7b24e15bfc624c6a792794f15b7599915f7" + @@ -55,7 +64,6 @@ public class SecurityTokenConnectionTest { @Test public void test_getTokenInfo() throws Exception { - Transport transport = mock(Transport.class); SecurityTokenConnection securityTokenConnection = new SecurityTokenConnection(transport, new Passphrase("123456"), new OpenPgpCommandApduFactory()); OpenPgpCapabilities openPgpCapabilities = new OpenPgpCapabilities( @@ -68,6 +76,7 @@ public class SecurityTokenConnectionTest { "000000000cd0c59cd0f2a59cd0af059cd0c95" )); securityTokenConnection.setConnectionCapabilities(openPgpCapabilities); + securityTokenConnection.determineTokenType(); String[] dialog = { "00ca006500", @@ -98,6 +107,5 @@ public class SecurityTokenConnectionTest { CommandApdu command = CommandApdu.fromBytes(Hex.decode(dialog[i])); inOrder.verify(transport).transceive(eq(command)); } - inOrder.verifyNoMoreInteractions(); } } \ No newline at end of file