Check for security token support
This commit is contained in:
committed by
Vincent Breitmoser
parent
e7705eaca8
commit
0920d97572
@@ -37,6 +37,14 @@ public class NfcTransport implements Transport {
|
|||||||
private final Tag mTag;
|
private final Tag mTag;
|
||||||
private IsoCard mIsoCard;
|
private IsoCard mIsoCard;
|
||||||
|
|
||||||
|
public static class IsoDepNotSupportedException extends IOException {
|
||||||
|
|
||||||
|
IsoDepNotSupportedException(String detailMessage) {
|
||||||
|
super(detailMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public NfcTransport(Tag tag) {
|
public NfcTransport(Tag tag) {
|
||||||
this.mTag = tag;
|
this.mTag = tag;
|
||||||
}
|
}
|
||||||
@@ -98,7 +106,7 @@ public class NfcTransport implements Transport {
|
|||||||
public void connect() throws IOException {
|
public void connect() throws IOException {
|
||||||
mIsoCard = AndroidCard.get(mTag);
|
mIsoCard = AndroidCard.get(mTag);
|
||||||
if (mIsoCard == null) {
|
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);
|
mIsoCard.setTimeout(TIMEOUT);
|
||||||
|
|||||||
@@ -955,10 +955,18 @@ public class SecurityTokenConnection {
|
|||||||
if (transportType == TransportType.USB) {
|
if (transportType == TransportType.USB) {
|
||||||
tokenType = mTransport.getTokenType();
|
tokenType = mTransport.getTokenType();
|
||||||
} else {
|
} 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) {
|
public static double parseOpenPgpVersion(final byte[] aid) {
|
||||||
@@ -966,4 +974,5 @@ public class SecurityTokenConnection {
|
|||||||
while (minv > 0) minv /= 10.0;
|
while (minv > 0) minv /= 10.0;
|
||||||
return aid[6] + minv;
|
return aid[6] + minv;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.sufficientlysecure.keychain.securitytoken;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
@@ -52,7 +53,7 @@ public abstract class SecurityTokenInfo implements Parcelable {
|
|||||||
if (!BuildConfig.DEBUG) {
|
if (!BuildConfig.DEBUG) {
|
||||||
throw new UnsupportedOperationException("This operation is only available in debug builds!");
|
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") },
|
new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("1efdb4845ca242ca6977fddb1f788094fd3b430a") },
|
||||||
Hex.decode("010203040506"), "yubinu2@mugenguild.com", null, 3, 3);
|
Hex.decode("010203040506"), "yubinu2@mugenguild.com", null, 3, 3);
|
||||||
}
|
}
|
||||||
@@ -61,7 +62,7 @@ public abstract class SecurityTokenInfo implements Parcelable {
|
|||||||
if (!BuildConfig.DEBUG) {
|
if (!BuildConfig.DEBUG) {
|
||||||
throw new UnsupportedOperationException("This operation is only available in debug builds!");
|
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") },
|
new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") },
|
||||||
Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 3, 3);
|
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) {
|
if (!BuildConfig.DEBUG) {
|
||||||
throw new UnsupportedOperationException("This operation is only available in debug builds!");
|
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") },
|
new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") },
|
||||||
Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 3);
|
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) {
|
if (!BuildConfig.DEBUG) {
|
||||||
throw new UnsupportedOperationException("This operation is only available in debug builds!");
|
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") },
|
new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") },
|
||||||
Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 0);
|
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 {
|
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<TokenType> 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<TokenType> 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -101,6 +101,7 @@ public class UsbTransport implements Transport {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if device is was connected to and still is connected
|
* Check if device is was connected to and still is connected
|
||||||
|
*
|
||||||
* @return true if device is connected
|
* @return true if device is connected
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@@ -112,6 +113,7 @@ public class UsbTransport implements Transport {
|
|||||||
/**
|
/**
|
||||||
* Check if Transport supports persistent connections e.g connections which can
|
* Check if Transport supports persistent connections e.g connections which can
|
||||||
* handle multiple operations in one session
|
* handle multiple operations in one session
|
||||||
|
*
|
||||||
* @return true if transport supports persistent connections
|
* @return true if transport supports persistent connections
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@@ -199,6 +201,7 @@ public class UsbTransport implements Transport {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Transmit and receive data
|
* Transmit and receive data
|
||||||
|
*
|
||||||
* @param data data to transmit
|
* @param data data to transmit
|
||||||
* @return received data
|
* @return received data
|
||||||
*/
|
*/
|
||||||
@@ -263,7 +266,10 @@ public class UsbTransport implements Transport {
|
|||||||
return TokenType.LEDGER_NANO_S;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -45,10 +45,11 @@ 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.UsbConnectionDispatcher;
|
import org.sufficientlysecure.keychain.securitytoken.UsbConnectionDispatcher;
|
||||||
import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransport;
|
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.PassphraseCacheService;
|
||||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||||
@@ -238,11 +239,21 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e instanceof IsoDepNotSupportedException) {
|
if (e instanceof NfcTransport.IsoDepNotSupportedException) {
|
||||||
onSecurityTokenError(getString(R.string.security_token_error_iso_dep_not_supported));
|
onSecurityTokenError(getString(R.string.security_token_error_iso_dep_not_supported));
|
||||||
return;
|
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;
|
short status;
|
||||||
if (e instanceof CardException) {
|
if (e instanceof CardException) {
|
||||||
status = ((CardException) e).getResponseCode();
|
status = ((CardException) e).getResponseCode();
|
||||||
@@ -442,14 +453,6 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
|
|||||||
doSecurityTokenInBackground(stConnection);
|
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
|
* Ask user if she wants to install PGP onto her Fidesmo token
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ class ManageSecurityTokenContract {
|
|||||||
void showActionRetryOrFromFile();
|
void showActionRetryOrFromFile();
|
||||||
void showActionLocked(int unlockAttempts);
|
void showActionLocked(int unlockAttempts);
|
||||||
void showActionEmptyToken();
|
void showActionEmptyToken();
|
||||||
|
void showActionUnsupportedToken();
|
||||||
void hideAction();
|
void hideAction();
|
||||||
|
|
||||||
void operationImportKey(byte[] importKeyData);
|
void operationImportKey(byte[] importKeyData);
|
||||||
|
|||||||
@@ -263,6 +263,11 @@ public class ManageSecurityTokenFragment extends Fragment implements ManageSecur
|
|||||||
actionAnimator.setDisplayedChildId(R.id.token_layout_empty);
|
actionAnimator.setDisplayedChildId(R.id.token_layout_empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showActionUnsupportedToken() {
|
||||||
|
actionAnimator.setDisplayedChildId(R.id.token_layout_unsupported);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hideAction() {
|
public void hideAction() {
|
||||||
actionAnimator.setDisplayedChild(0);
|
actionAnimator.setDisplayedChild(0);
|
||||||
|
|||||||
@@ -164,6 +164,14 @@ class ManageSecurityTokenPresenter implements ManageSecurityTokenMvpPresenter {
|
|||||||
|
|
||||||
private void performKeyCheck() {
|
private void performKeyCheck() {
|
||||||
boolean keyIsEmpty = tokenInfo.isEmpty();
|
boolean keyIsEmpty = tokenInfo.isEmpty();
|
||||||
|
boolean putKeyIsSupported = tokenInfo.isPutKeySupported();
|
||||||
|
|
||||||
|
if (keyIsEmpty && !putKeyIsSupported) {
|
||||||
|
view.statusLineOk();
|
||||||
|
view.showActionUnsupportedToken();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (keyIsEmpty) {
|
if (keyIsEmpty) {
|
||||||
boolean tokenIsAdminLocked = tokenInfo.getVerifyAdminRetries() == 0;
|
boolean tokenIsAdminLocked = tokenInfo.getVerifyAdminRetries() == 0;
|
||||||
if (tokenIsAdminLocked) {
|
if (tokenIsAdminLocked) {
|
||||||
|
|||||||
@@ -370,6 +370,31 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:id="@+id/token_layout_unsupported">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?listPreferredItemHeight"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="@string/token_result_empty"
|
||||||
|
style="?android:textAppearanceLarge"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:text="@string/token_put_key_unsupported"
|
||||||
|
style="?android:textAppearanceMedium"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
|
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -1580,6 +1580,7 @@
|
|||||||
<string name="security_token_error_header">"Security Token reported invalid %s byte."</string>
|
<string name="security_token_error_header">"Security Token reported invalid %s byte."</string>
|
||||||
<string name="security_token_error_tag_lost">"Security Token has been taken off too early. Keep the Security Token at the back until the operation finishes."</string>
|
<string name="security_token_error_tag_lost">"Security Token has been taken off too early. Keep the Security Token at the back until the operation finishes."</string>
|
||||||
<string name="security_token_error_iso_dep_not_supported">"Security Token does not support the required communication standard (ISO-DEP, ISO 14443-4)"</string>
|
<string name="security_token_error_iso_dep_not_supported">"Security Token does not support the required communication standard (ISO-DEP, ISO 14443-4)"</string>
|
||||||
|
<string name="security_token_not_supported">"This Security Token is not supported by OpenKeychain."</string>
|
||||||
<string name="security_token_error_try_again">"Try again"</string>
|
<string name="security_token_error_try_again">"Try again"</string>
|
||||||
<string name="btn_delete_original">Delete original file</string>
|
<string name="btn_delete_original">Delete original file</string>
|
||||||
|
|
||||||
@@ -1895,6 +1896,7 @@
|
|||||||
<string name="token_result_key_found">Key found!</string>
|
<string name="token_result_key_found">Key found!</string>
|
||||||
<string name="token_result_token_ok">Ready for use!</string>
|
<string name="token_result_token_ok">Ready for use!</string>
|
||||||
<string name="token_result_empty">Token is empty</string>
|
<string name="token_result_empty">Token is empty</string>
|
||||||
|
<string name="token_put_key_unsupported">"Please set up this Security Token using GnuPG. OpenKeychain currently only supports singing/decrypting with this Security Token."</string>
|
||||||
<string name="token_action_retry">Retry Search</string>
|
<string name="token_action_retry">Retry Search</string>
|
||||||
<string name="token_action_load_from_file">Load from File</string>
|
<string name="token_action_load_from_file">Load from File</string>
|
||||||
<string name="token_action_reset">Reset Security Token</string>
|
<string name="token_action_reset">Reset Security Token</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user