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