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 cefccd6e2..9b15b4ba7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java @@ -90,6 +90,7 @@ public class SecurityTokenConnection { public void connectIfNecessary(Context context) throws IOException { if (isConnected()) { + refreshConnectionCapabilities(); return; } 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 b11586d4d..ecdb885e5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java @@ -128,7 +128,8 @@ public abstract class SecurityTokenInfo implements Parcelable { TokenType.NITROKEY_START_OLD, TokenType.NITROKEY_START_1_25_AND_NEWER, TokenType.GNUK_OLD, - TokenType.GNUK_1_25_AND_NEWER + TokenType.GNUK_1_25_AND_NEWER, + TokenType.LEDGER_NANO_S ))); private static final Set SUPPORTED_USB_SETUP = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidDescription.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidDescription.java index 7c9db08b5..b0654faa2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidDescription.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/CcidDescription.java @@ -25,6 +25,7 @@ import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; import com.google.auto.value.AutoValue; +import org.sufficientlysecure.keychain.securitytoken.usb.tpdu.T0ShortApduProtocol; import org.sufficientlysecure.keychain.securitytoken.usb.tpdu.T1ShortApduProtocol; import org.sufficientlysecure.keychain.securitytoken.usb.tpdu.T1TpduProtocol; @@ -39,7 +40,7 @@ abstract class CcidDescription { private static final int FEATURE_AUTOMATIC_PPS = 0x00080; private static final int FEATURE_EXCHANGE_LEVEL_TPDU = 0x10000; - private static final int FEATURE_EXCHAGE_LEVEL_SHORT_APDU = 0x20000; + private static final int FEATURE_EXCHANGE_LEVEL_SHORT_APDU = 0x20000; private static final int FEATURE_EXCHAGE_LEVEL_EXTENDED_APDU = 0x40000; // bVoltageSupport Masks @@ -49,6 +50,7 @@ abstract class CcidDescription { private static final int SLOT_OFFSET = 4; private static final int FEATURES_OFFSET = 40; + private static final short MASK_T0_PROTO = 1; private static final short MASK_T1_PROTO = 2; public abstract byte getMaxSlotIndex(); @@ -118,18 +120,29 @@ abstract class CcidDescription { CcidTransportProtocol getSuitableTransportProtocol() throws UsbTransportException { boolean hasT1Protocol = (getProtocols() & MASK_T1_PROTO) != 0; - if (!hasT1Protocol) { - throw new UsbTransportException("T=0 protocol is not supported!"); + if (hasT1Protocol) { + if (hasFeature(CcidDescription.FEATURE_EXCHANGE_LEVEL_TPDU)) { + return new T1TpduProtocol(); + } else if (hasFeature(CcidDescription.FEATURE_EXCHANGE_LEVEL_SHORT_APDU) || + hasFeature(CcidDescription.FEATURE_EXCHAGE_LEVEL_EXTENDED_APDU)) { + return new T1ShortApduProtocol(); + } else { + throw new UsbTransportException("Character level exchange is not supported for T=1"); + } } - if (hasFeature(CcidDescription.FEATURE_EXCHANGE_LEVEL_TPDU)) { - return new T1TpduProtocol(); - } else if (hasFeature(CcidDescription.FEATURE_EXCHAGE_LEVEL_SHORT_APDU) || - hasFeature(CcidDescription.FEATURE_EXCHAGE_LEVEL_EXTENDED_APDU)) { - return new T1ShortApduProtocol(); - } else { - throw new UsbTransportException("Character level exchange is not supported"); + boolean hasT0Protocol = (getProtocols() & MASK_T0_PROTO) != 0; + if (hasT0Protocol) { + if (hasFeature(CcidDescription.FEATURE_EXCHANGE_LEVEL_SHORT_APDU)) { + return new T0ShortApduProtocol(); + } else if (hasFeature(CcidDescription.FEATURE_EXCHANGE_LEVEL_TPDU)) { + throw new UsbTransportException("TPDU level exchange is not supported for T=0"); + } else { + throw new UsbTransportException("Character level exchange is not supported for T=0"); + } } + + throw new UsbTransportException("No suitable usb protocol supported"); } boolean hasAutomaticPps() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T0ShortApduProtocol.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T0ShortApduProtocol.java new file mode 100644 index 000000000..63a15b20a --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/tpdu/T0ShortApduProtocol.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 Schürmann & Breitmoser GbR + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.securitytoken.usb.tpdu; + + +import android.support.annotation.NonNull; + +import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransceiver; +import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransceiver.CcidDataBlock; +import org.sufficientlysecure.keychain.securitytoken.usb.CcidTransportProtocol; +import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException; + + +public class T0ShortApduProtocol implements CcidTransportProtocol { + private CcidTransceiver ccidTransceiver; + + public void connect(@NonNull CcidTransceiver transceiver) throws UsbTransportException { + ccidTransceiver = transceiver; + ccidTransceiver.iccPowerOn(); + } + + @Override + public byte[] transceive(@NonNull byte[] apdu) throws UsbTransportException { + CcidDataBlock response = ccidTransceiver.sendXfrBlock(apdu); + return response.getData(); + } +}