determine token type during connect

This commit is contained in:
Vincent Breitmoser
2017-10-26 17:18:13 +02:00
parent 8649332bdc
commit b6236bde59
6 changed files with 54 additions and 46 deletions

View File

@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.securitytoken; package org.sufficientlysecure.keychain.securitytoken;
import android.nfc.Tag; import android.nfc.Tag;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.encoders.Hex;
@@ -118,8 +119,10 @@ public class NfcTransport implements Transport {
return TransportType.NFC; return TransportType.NFC;
} }
@Nullable
@Override @Override
public TokenType getTokenType() { public TokenType getTokenTypeIfAvailable() {
// Sadly, the NFC transport has no direct information about the token type.
return null; return null;
} }

View File

@@ -76,8 +76,7 @@ import java.util.List;
public class SecurityTokenConnection { public class SecurityTokenConnection {
private static final int APDU_SW1_RESPONSE_AVAILABLE = 0x61; private static final int APDU_SW1_RESPONSE_AVAILABLE = 0x61;
// Fidesmo constants private static final String AID_PREFIX_FIDESMO = "A000000617";
private static final String FIDESMO_APPS_AID_PREFIX = "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}; 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 Passphrase mPin;
private final OpenPgpCommandApduFactory commandFactory; private final OpenPgpCommandApduFactory commandFactory;
private TokenType tokenType;
private CardCapabilities mCardCapabilities; private CardCapabilities mCardCapabilities;
private OpenPgpCapabilities mOpenPgpCapabilities; private OpenPgpCapabilities mOpenPgpCapabilities;
private SecureMessaging mSecureMessaging; private SecureMessaging mSecureMessaging;
@@ -184,8 +184,8 @@ public class SecurityTokenConnection {
mTransport.connect(); mTransport.connect();
// Connect on smartcard layer determineTokenType();
// Command APDU (page 51) for SELECT FILE command (page 29)
CommandApdu select = commandFactory.createSelectFileOpenPgpCommand(); CommandApdu select = commandFactory.createSelectFileOpenPgpCommand();
ResponseApdu response = communicate(select); // activate connection 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 @VisibleForTesting
void setConnectionCapabilities(OpenPgpCapabilities openPgpCapabilities) throws IOException { void setConnectionCapabilities(OpenPgpCapabilities openPgpCapabilities) throws IOException {
this.mOpenPgpCapabilities = openPgpCapabilities; this.mOpenPgpCapabilities = openPgpCapabilities;
@@ -797,20 +821,6 @@ public class SecurityTokenConnection {
return lastResponse; 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), * 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. * this command also has the effect of resetting the digital signature counter.
@@ -919,6 +929,10 @@ public class SecurityTokenConnection {
return mTransport.isConnected(); return mTransport.isConnected();
} }
public TokenType getTokenType() {
return tokenType;
}
public void clearSecureMessaging() { public void clearSecureMessaging() {
if (mSecureMessaging != null) { if (mSecureMessaging != null) {
mSecureMessaging.clearSession(); mSecureMessaging.clearSession();
@@ -931,10 +945,6 @@ public class SecurityTokenConnection {
mSecureMessaging = sm; mSecureMessaging = sm;
} }
OpenPgpCapabilities getOpenPgpCapabilities() {
return mOpenPgpCapabilities;
}
public SecurityTokenInfo getTokenInfo() throws IOException { public SecurityTokenInfo getTokenInfo() throws IOException {
byte[] rawFingerprints = getFingerprints(); byte[] rawFingerprints = getFingerprints();
@@ -951,16 +961,9 @@ public class SecurityTokenConnection {
byte[] pwInfo = getPwStatusBytes(); byte[] pwInfo = getPwStatusBytes();
TransportType transportType = mTransport.getTransportType(); 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()) { if (! info.isSecurityTokenSupported()) {
throw new UnsupportedSecurityTokenException(); throw new UnsupportedSecurityTokenException();
@@ -974,5 +977,4 @@ public class SecurityTokenConnection {
while (minv > 0) minv /= 10.0; while (minv > 0) minv /= 10.0;
return aid[6] + minv; return aid[6] + minv;
} }
} }

View File

@@ -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 YUBIKEY_NEO, YUBIKEY_4, FIDESMO, NITROKEY_PRO, NITROKEY_STORAGE, NITROKEY_START, GNUK, LEDGER_NANO_S, UNKNOWN
} }
private static final HashSet<TokenType> SUPPORTED_SECURITY_TOKENS = new HashSet<>(Arrays.asList( private static final HashSet<TokenType> SUPPORTED_USB_TOKENS = new HashSet<>(Arrays.asList(
TokenType.FIDESMO,
TokenType.YUBIKEY_NEO, TokenType.YUBIKEY_NEO,
TokenType.YUBIKEY_4, TokenType.YUBIKEY_4,
TokenType.NITROKEY_PRO, TokenType.NITROKEY_PRO,
TokenType.NITROKEY_STORAGE TokenType.NITROKEY_STORAGE
)); ));
private static final HashSet<TokenType> SUPPORTED_PUT_KEY = new HashSet<>(Arrays.asList( private static final HashSet<TokenType> SUPPORTED_USB_PUT_KEY = new HashSet<>(Arrays.asList(
TokenType.FIDESMO,
TokenType.YUBIKEY_NEO, TokenType.YUBIKEY_NEO,
TokenType.YUBIKEY_4 // Not clear, will be tested: https://github.com/open-keychain/open-keychain/issues/2069 TokenType.YUBIKEY_4 // Not clear, will be tested: https://github.com/open-keychain/open-keychain/issues/2069
)); ));
public boolean isSecurityTokenSupported() { public boolean isSecurityTokenSupported() {
boolean isKnownSupported = SUPPORTED_SECURITY_TOKENS.contains(getTokenType()); boolean isKnownSupported = SUPPORTED_USB_TOKENS.contains(getTokenType());
boolean isNfcTransport = getTransportType() == TransportType.NFC; boolean isNfcTransport = getTransportType() == TransportType.NFC;
return isKnownSupported || isNfcTransport; return isKnownSupported || isNfcTransport;
} }
public boolean isPutKeySupported() { public boolean isPutKeySupported() {
boolean isKnownSupported = SUPPORTED_PUT_KEY.contains(getTokenType()); boolean isKnownSupported = SUPPORTED_USB_PUT_KEY.contains(getTokenType());
boolean isNfcTransport = getTransportType() == TransportType.NFC; boolean isNfcTransport = getTransportType() == TransportType.NFC;
return isKnownSupported || isNfcTransport; return isKnownSupported || isNfcTransport;

View File

@@ -22,6 +22,9 @@ import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.Transport
import java.io.IOException; import java.io.IOException;
import android.support.annotation.Nullable;
/** /**
* Abstraction for transmitting APDU commands * Abstraction for transmitting APDU commands
*/ */
@@ -60,6 +63,6 @@ public interface Transport {
void connect() throws IOException; void connect() throws IOException;
TransportType getTransportType(); TransportType getTransportType();
@Nullable
TokenType getTokenType(); TokenType getTokenTypeIfAvailable();
} }

View File

@@ -230,8 +230,9 @@ public class UsbTransport implements Transport {
return TransportType.USB; return TransportType.USB;
} }
@Nullable
@Override @Override
public TokenType getTokenType() { public TokenType getTokenTypeIfAvailable() {
switch (usbDevice.getVendorId()) { switch (usbDevice.getVendorId()) {
case VENDOR_YUBICO: { case VENDOR_YUBICO: {
switch (usbDevice.getProductId()) { switch (usbDevice.getProductId()) {
@@ -267,9 +268,9 @@ public class UsbTransport implements Transport {
} }
} }
Log.d(Constants.TAG, "Unknown USB token. Vendor ID: " + usbDevice.getVendorId() Log.d(Constants.TAG, "Unknown USB token. Vendor ID: " + usbDevice.getVendorId() + ", Product ID: " +
+ ", Product ID: " + usbDevice.getProductId()); usbDevice.getProductId());
return TokenType.UNKNOWN; return null;
} }
/** /**

View File

@@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.securitytoken.CardException;
import org.sufficientlysecure.keychain.securitytoken.NfcTransport; import org.sufficientlysecure.keychain.securitytoken.NfcTransport;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection; import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo; import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TokenType;
import org.sufficientlysecure.keychain.securitytoken.Transport; import org.sufficientlysecure.keychain.securitytoken.Transport;
import org.sufficientlysecure.keychain.securitytoken.UnsupportedSecurityTokenException; import org.sufficientlysecure.keychain.securitytoken.UnsupportedSecurityTokenException;
import org.sufficientlysecure.keychain.securitytoken.UsbConnectionDispatcher; import org.sufficientlysecure.keychain.securitytoken.UsbConnectionDispatcher;
@@ -338,7 +339,7 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
} }
// 6A82 app not installed on security token! // 6A82 app not installed on security token!
case 0x6A82: { case 0x6A82: {
if (stConnection.isFidesmoToken()) { if (stConnection.getTokenType() == TokenType.FIDESMO) {
// Check if the Fidesmo app is installed // Check if the Fidesmo app is installed
if (isAndroidAppInstalled(FIDESMO_APP_PACKAGE)) { if (isAndroidAppInstalled(FIDESMO_APP_PACKAGE)) {
promptFidesmoPgpInstall(); promptFidesmoPgpInstall();