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;
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;
}

View File

@@ -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;
}
}

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
}
private static final HashSet<TokenType> SUPPORTED_SECURITY_TOKENS = new HashSet<>(Arrays.asList(
TokenType.FIDESMO,
private static final HashSet<TokenType> SUPPORTED_USB_TOKENS = new HashSet<>(Arrays.asList(
TokenType.YUBIKEY_NEO,
TokenType.YUBIKEY_4,
TokenType.NITROKEY_PRO,
TokenType.NITROKEY_STORAGE
));
private static final HashSet<TokenType> SUPPORTED_PUT_KEY = new HashSet<>(Arrays.asList(
TokenType.FIDESMO,
private static final HashSet<TokenType> 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;

View File

@@ -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();
}

View File

@@ -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;
}
/**

View File

@@ -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();