explicitly pass around instance of SecurityTokenConnection

This commit is contained in:
Vincent Breitmoser
2017-10-09 02:46:52 +02:00
parent e8f72718e9
commit 46b69d45c4
7 changed files with 132 additions and 148 deletions

View File

@@ -87,30 +87,34 @@ public class SecurityTokenConnection {
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};
private static SecurityTokenConnection sCachedInstance;
private final JcaKeyFingerprintCalculator fingerprintCalculator = new JcaKeyFingerprintCalculator(); private final JcaKeyFingerprintCalculator fingerprintCalculator = new JcaKeyFingerprintCalculator();
private Transport mTransport; @NonNull
private final Transport mTransport;
@NonNull
private final Passphrase mPin;
private CardCapabilities mCardCapabilities; private CardCapabilities mCardCapabilities;
private OpenPgpCapabilities mOpenPgpCapabilities; private OpenPgpCapabilities mOpenPgpCapabilities;
private SecureMessaging mSecureMessaging; private SecureMessaging mSecureMessaging;
private Passphrase mPin;
private Passphrase mAdminPin;
private boolean mPw1ValidatedForSignature; private boolean mPw1ValidatedForSignature;
private boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming? private boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming?
private boolean mPw3Validated; private boolean mPw3Validated;
private SecurityTokenConnection() { public static SecurityTokenConnection getInstanceForTransport(Transport transport, Passphrase pin) {
if (sCachedInstance == null || !sCachedInstance.isPersistentConnectionAllowed() ||
!sCachedInstance.isConnected() || !sCachedInstance.mTransport.equals(transport)) {
sCachedInstance = new SecurityTokenConnection(transport, pin);
}
return sCachedInstance;
} }
public static double parseOpenPgpVersion(final byte[] aid) { private SecurityTokenConnection(@NonNull Transport transport, @NonNull Passphrase pin) {
float minv = aid[7]; this.mTransport = transport;
while (minv > 0) minv /= 10.0; this.mPin = pin;
return aid[6] + minv;
}
public static SecurityTokenConnection getInstance() {
return LazyHolder.SECURITY_TOKEN_HELPER;
} }
private String getHolderName(byte[] name) { private String getHolderName(byte[] name) {
@@ -126,23 +130,7 @@ public class SecurityTokenConnection {
} }
} }
public Passphrase getPin() { public void changeKey(CanonicalizedSecretKey secretKey, Passphrase passphrase, Passphrase adminPin) throws IOException {
return mPin;
}
public void setPin(final Passphrase pin) {
this.mPin = pin;
}
public Passphrase getAdminPin() {
return mAdminPin;
}
public void setAdminPin(final Passphrase adminPin) {
this.mAdminPin = adminPin;
}
public void changeKey(CanonicalizedSecretKey secretKey, Passphrase passphrase) throws IOException {
long keyGenerationTimestamp = secretKey.getCreationTime().getTime() / 1000; long keyGenerationTimestamp = secretKey.getCreationTime().getTime() / 1000;
byte[] timestampBytes = ByteBuffer.allocate(4).putInt((int) keyGenerationTimestamp).array(); byte[] timestampBytes = ByteBuffer.allocate(4).putInt((int) keyGenerationTimestamp).array();
KeyType keyType = KeyType.from(secretKey); KeyType keyType = KeyType.from(secretKey);
@@ -160,9 +148,9 @@ public class SecurityTokenConnection {
keyType.toString())); keyType.toString()));
} }
putKey(keyType, secretKey, passphrase); putKey(keyType, secretKey, passphrase, adminPin);
putData(keyType.getFingerprintObjectId(), secretKey.getFingerprint()); putData(adminPin, keyType.getFingerprintObjectId(), secretKey.getFingerprint());
putData(keyType.getTimestampObjectId(), timestampBytes); putData(adminPin, keyType.getTimestampObjectId(), timestampBytes);
} }
private boolean isSlotEmpty(KeyType keyType) throws IOException { private boolean isSlotEmpty(KeyType keyType) throws IOException {
@@ -179,12 +167,18 @@ public class SecurityTokenConnection {
return java.util.Arrays.equals(getKeyFingerprint(keyType), fingerprint); return java.util.Arrays.equals(getKeyFingerprint(keyType), fingerprint);
} }
public void connectIfNecessary(Context context) throws IOException {
if (isConnected()) {
return;
}
connectToDevice(context);
}
/** /**
* Connect to device and select pgp applet * Connect to device and select pgp applet
*
* @throws IOException
*/ */
public void connectToDevice(final Context ctx) throws IOException { private void connectToDevice(Context context) throws IOException {
// Connect on transport layer // Connect on transport layer
mCardCapabilities = new CardCapabilities(); mCardCapabilities = new CardCapabilities();
@@ -208,7 +202,7 @@ public class SecurityTokenConnection {
if (mOpenPgpCapabilities.isHasSCP11bSM()) { if (mOpenPgpCapabilities.isHasSCP11bSM()) {
try { try {
SCP11bSecureMessaging.establish(this, ctx); SCP11bSecureMessaging.establish(this, context);
} catch (SecureMessagingException e) { } catch (SecureMessagingException e) {
mSecureMessaging = null; mSecureMessaging = null;
Log.e(Constants.TAG, "failed to establish secure messaging", e); Log.e(Constants.TAG, "failed to establish secure messaging", e);
@@ -217,9 +211,9 @@ public class SecurityTokenConnection {
} }
public void resetPin(String newPinStr) throws IOException { public void resetPin(Passphrase adminPin, String newPinStr) throws IOException {
if (!mPw3Validated) { if (!mPw3Validated) {
verifyPin(0x83); // (Verify PW1 with mode 82 for decryption) verifyAdminPin(adminPin);
} }
byte[] newPin = newPinStr.getBytes(); byte[] newPin = newPinStr.getBytes();
@@ -246,7 +240,7 @@ public class SecurityTokenConnection {
* @param pw For PW1, this is 0x81. For PW3 (Admin PIN), mode is 0x83. * @param pw For PW1, this is 0x81. For PW3 (Admin PIN), mode is 0x83.
* @param newPin The new PW1 or PW3. * @param newPin The new PW1 or PW3.
*/ */
public void modifyPin(int pw, byte[] newPin) throws IOException { public void modifyPin(int pw, byte[] newPin, Passphrase adminPin) throws IOException {
final int MAX_PW1_LENGTH_INDEX = 1; final int MAX_PW1_LENGTH_INDEX = 1;
final int MAX_PW3_LENGTH_INDEX = 3; final int MAX_PW3_LENGTH_INDEX = 3;
@@ -266,7 +260,10 @@ public class SecurityTokenConnection {
byte[] pin; byte[] pin;
if (pw == 0x83) { if (pw == 0x83) {
pin = mAdminPin.toStringUnsafe().getBytes(); if (adminPin == null) {
throw new IllegalArgumentException("Changing the admin pin requires admin pin argument!");
}
pin = adminPin.toStringUnsafe().getBytes();
} else { } else {
pin = mPin.toStringUnsafe().getBytes(); pin = mPin.toStringUnsafe().getBytes();
} }
@@ -422,28 +419,30 @@ public class SecurityTokenConnection {
* For PW3 (Admin PIN), mode is 0x83. * For PW3 (Admin PIN), mode is 0x83.
*/ */
private void verifyPin(int mode) throws IOException { private void verifyPin(int mode) throws IOException {
if (mPin != null || mode == 0x83) { byte[] pin = mPin.toStringUnsafe().getBytes();
byte[] pin; ResponseAPDU response = tryPin(mode, pin);// login
if (mode == 0x83) { if (response.getSW() != APDU_SW_SUCCESS) {
pin = mAdminPin.toStringUnsafe().getBytes(); throw new CardException("Bad PIN!", response.getSW());
} else {
pin = mPin.toStringUnsafe().getBytes();
}
ResponseAPDU response = tryPin(mode, pin);// login
if (response.getSW() != APDU_SW_SUCCESS) {
throw new CardException("Bad PIN!", response.getSW());
}
if (mode == 0x81) {
mPw1ValidatedForSignature = true;
} else if (mode == 0x82) {
mPw1ValidatedForDecrypt = true;
} else if (mode == 0x83) {
mPw3Validated = true;
}
} }
if (mode == 0x81) {
mPw1ValidatedForSignature = true;
} else if (mode == 0x82) {
mPw1ValidatedForDecrypt = true;
}
}
/**
* Verifies the user's PW1 or PW3 with the appropriate mode.
*/
private void verifyAdminPin(Passphrase adminPin) throws IOException {
ResponseAPDU response = tryPin(0x83, adminPin.toStringUnsafe().getBytes());
if (response.getSW() != APDU_SW_SUCCESS) {
throw new CardException("Bad PIN!", response.getSW());
}
mPw3Validated = true;
} }
/** /**
@@ -454,16 +453,17 @@ public class SecurityTokenConnection {
* @param dataObject The data object to be stored. * @param dataObject The data object to be stored.
* @param data The data to store in the object * @param data The data to store in the object
*/ */
private void putData(int dataObject, byte[] data) throws IOException { private void putData(Passphrase adminPin, int dataObject, byte[] data) throws IOException {
if (data.length > 254) { if (data.length > 254) {
throw new IOException("Cannot PUT DATA with length > 254"); throw new IOException("Cannot PUT DATA with length > 254");
} }
// TODO use admin pin regardless, if we have it?
if (dataObject == 0x0101 || dataObject == 0x0103) { if (dataObject == 0x0101 || dataObject == 0x0103) {
if (!mPw1ValidatedForDecrypt) { if (!mPw1ValidatedForDecrypt) {
verifyPin(0x82); // (Verify PW1 for non-signing operations) verifyPin(0x82); // (Verify PW1 for non-signing operations)
} }
} else if (!mPw3Validated) { } else if (!mPw3Validated) {
verifyPin(0x83); // (Verify PW3) verifyAdminPin(adminPin);
} }
CommandAPDU command = new CommandAPDU(0x00, 0xDA, (dataObject & 0xFF00) >> 8, dataObject & 0xFF, data); CommandAPDU command = new CommandAPDU(0x00, 0xDA, (dataObject & 0xFF00) >> 8, dataObject & 0xFF, data);
@@ -475,7 +475,7 @@ public class SecurityTokenConnection {
} }
private void setKeyAttributes(final KeyType slot, final CanonicalizedSecretKey secretKey) private void setKeyAttributes(Passphrase adminPin, final KeyType slot, final CanonicalizedSecretKey secretKey)
throws IOException { throws IOException {
if (mOpenPgpCapabilities.isAttributesChangable()) { if (mOpenPgpCapabilities.isAttributesChangable()) {
@@ -493,7 +493,7 @@ public class SecurityTokenConnection {
try { try {
putData(tag, SecurityTokenUtils.attributesFromSecretKey(slot, secretKey)); putData(adminPin, tag, SecurityTokenUtils.attributesFromSecretKey(slot, secretKey));
mOpenPgpCapabilities.updateWithData(getData(0x00, tag)); mOpenPgpCapabilities.updateWithData(getData(0x00, tag));
@@ -512,14 +512,14 @@ public class SecurityTokenConnection {
* 0xB8: Decipherment Key * 0xB8: Decipherment Key
* 0xA4: Authentication Key * 0xA4: Authentication Key
*/ */
private void putKey(KeyType slot, CanonicalizedSecretKey secretKey, Passphrase passphrase) private void putKey(KeyType slot, CanonicalizedSecretKey secretKey, Passphrase passphrase, Passphrase adminPin)
throws IOException { throws IOException {
RSAPrivateCrtKey crtSecretKey; RSAPrivateCrtKey crtSecretKey;
ECPrivateKey ecSecretKey; ECPrivateKey ecSecretKey;
ECPublicKey ecPublicKey; ECPublicKey ecPublicKey;
if (!mPw3Validated) { if (!mPw3Validated) {
verifyPin(0x83); // (Verify PW3 with mode 83) verifyAdminPin(adminPin);
} }
// Now we're ready to communicate with the token. // Now we're ready to communicate with the token.
@@ -528,7 +528,7 @@ public class SecurityTokenConnection {
try { try {
secretKey.unlock(passphrase); secretKey.unlock(passphrase);
setKeyAttributes(slot, secretKey); setKeyAttributes(adminPin, slot, secretKey);
switch (mOpenPgpCapabilities.getFormatForKeyType(slot).keyFormatType()) { switch (mOpenPgpCapabilities.getFormatForKeyType(slot).keyFormatType()) {
case RSAKeyFormatType: case RSAKeyFormatType:
@@ -836,15 +836,6 @@ public class SecurityTokenConnection {
return lastResponse; return lastResponse;
} }
public Transport getTransport() {
return mTransport;
}
public void setTransport(Transport mTransport) {
clearSecureMessaging();
this.mTransport = mTransport;
}
public boolean isFidesmoToken() { public boolean isFidesmoToken() {
if (isConnected()) { // Check if we can still talk to the card if (isConnected()) { // Check if we can still talk to the card
try { try {
@@ -873,13 +864,13 @@ public class SecurityTokenConnection {
* @return the public key data objects, in TLV format. For RSA this will be the public modulus * @return the public key data objects, in TLV format. For RSA this will be the public modulus
* (0x81) and exponent (0x82). These may come out of order; proper TLV parsing is required. * (0x81) and exponent (0x82). These may come out of order; proper TLV parsing is required.
*/ */
public byte[] generateKey(int slot) throws IOException { public byte[] generateKey(Passphrase adminPin, int slot) throws IOException {
if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) { if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) {
throw new IOException("Invalid key slot"); throw new IOException("Invalid key slot");
} }
if (!mPw3Validated) { if (!mPw3Validated) {
verifyPin(0x83); // (Verify PW3 with mode 83) verifyAdminPin(adminPin);
} }
CommandAPDU apdu = new CommandAPDU(0x00, 0x47, 0x80, 0x00, new byte[]{(byte) slot, 0x00}, MAX_APDU_NE_EXT); CommandAPDU apdu = new CommandAPDU(0x00, 0x47, 0x80, 0x00, new byte[]{(byte) slot, 0x00}, MAX_APDU_NE_EXT);
@@ -962,14 +953,12 @@ public class SecurityTokenConnection {
} }
public boolean isPersistentConnectionAllowed() { public boolean isPersistentConnectionAllowed() {
return mTransport != null && return mTransport.isPersistentConnectionAllowed() &&
mTransport.isPersistentConnectionAllowed() && (mSecureMessaging == null || !mSecureMessaging.isEstablished());
(mSecureMessaging == null ||
!mSecureMessaging.isEstablished());
} }
public boolean isConnected() { public boolean isConnected() {
return mTransport != null && mTransport.isConnected(); return mTransport.isConnected();
} }
public void clearSecureMessaging() { public void clearSecureMessaging() {
@@ -1006,7 +995,9 @@ public class SecurityTokenConnection {
return SecurityTokenInfo.create(fingerprints, aid, userId, url, pwInfo[4], pwInfo[6]); return SecurityTokenInfo.create(fingerprints, aid, userId, url, pwInfo[4], pwInfo[6]);
} }
private static class LazyHolder { public static double parseOpenPgpVersion(final byte[] aid) {
private static final SecurityTokenConnection SECURITY_TOKEN_HELPER = new SecurityTokenConnection(); float minv = aid[7];
while (minv > 0) minv /= 10.0;
return aid[6] + minv;
} }
} }

View File

@@ -32,6 +32,7 @@ import android.support.v4.app.TaskStackBuilder;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.securitytoken.KeyFormat; import org.sufficientlysecure.keychain.securitytoken.KeyFormat;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo; import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity; import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
import org.sufficientlysecure.keychain.ui.token.ManageSecurityTokenFragment; import org.sufficientlysecure.keychain.ui.token.ManageSecurityTokenFragment;
@@ -133,17 +134,17 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
} }
@Override @Override
protected void doSecurityTokenInBackground() throws IOException { protected void doSecurityTokenInBackground(SecurityTokenConnection stConnection) throws IOException {
if (mCurrentFragment instanceof SecurityTokenListenerFragment) { if (mCurrentFragment instanceof SecurityTokenListenerFragment) {
((SecurityTokenListenerFragment) mCurrentFragment).doSecurityTokenInBackground(); ((SecurityTokenListenerFragment) mCurrentFragment).doSecurityTokenInBackground();
return; return;
} }
tokenInfo = mSecurityTokenConnection.getTokenInfo(); tokenInfo = stConnection.getTokenInfo();
} }
@Override @Override
protected void onSecurityTokenPostExecute() { protected void onSecurityTokenPostExecute(SecurityTokenConnection stConnection) {
handleTokenInfo(tokenInfo); handleTokenInfo(tokenInfo);
} }

View File

@@ -32,6 +32,7 @@ import android.widget.ViewAnimator;
import nordpol.android.NfcGuideView; import nordpol.android.NfcGuideView;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo; import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
import org.sufficientlysecure.keychain.service.input.SecurityTokenChangePinParcel; import org.sufficientlysecure.keychain.service.input.SecurityTokenChangePinParcel;
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity; import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
@@ -138,15 +139,15 @@ public class SecurityTokenChangePinOperationActivity extends BaseSecurityTokenAc
} }
@Override @Override
protected void doSecurityTokenInBackground() throws IOException { protected void doSecurityTokenInBackground(SecurityTokenConnection stConnection) throws IOException {
mSecurityTokenConnection.setAdminPin(new Passphrase(changePinInput.getAdminPin())); Passphrase adminPin = new Passphrase(changePinInput.getAdminPin());
mSecurityTokenConnection.resetPin(changePinInput.getNewPin()); stConnection.resetPin(adminPin, changePinInput.getNewPin());
resultTokenInfo = mSecurityTokenConnection.getTokenInfo(); resultTokenInfo = stConnection.getTokenInfo();
} }
@Override @Override
protected final void onSecurityTokenPostExecute() { protected final void onSecurityTokenPostExecute(final SecurityTokenConnection stConnection) {
Intent result = new Intent(); Intent result = new Intent();
result.putExtra(RESULT_TOKEN_INFO, resultTokenInfo); result.putExtra(RESULT_TOKEN_INFO, resultTokenInfo);
setResult(RESULT_OK, result); setResult(RESULT_OK, result);
@@ -156,17 +157,17 @@ public class SecurityTokenChangePinOperationActivity extends BaseSecurityTokenAc
nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.DONE); nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.DONE);
if (mSecurityTokenConnection.isPersistentConnectionAllowed()) { if (stConnection.isPersistentConnectionAllowed()) {
// Just close // Just close
finish(); finish();
} else { } else {
mSecurityTokenConnection.clearSecureMessaging(); stConnection.clearSecureMessaging();
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
// check all 200ms if Security Token has been taken away // check all 200ms if Security Token has been taken away
while (true) { while (true) {
if (isSecurityTokenConnected()) { if (stConnection.isConnected()) {
try { try {
Thread.sleep(200); Thread.sleep(200);
} catch (InterruptedException ignored) { } catch (InterruptedException ignored) {

View File

@@ -44,6 +44,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.securitytoken.KeyType; import org.sufficientlysecure.keychain.securitytoken.KeyType;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo; import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
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;
@@ -185,12 +186,12 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
} }
@Override @Override
protected void doSecurityTokenInBackground() throws IOException { protected void doSecurityTokenInBackground(SecurityTokenConnection stConnection) throws IOException {
switch (mRequiredInput.mType) { switch (mRequiredInput.mType) {
case SECURITY_TOKEN_DECRYPT: { case SECURITY_TOKEN_DECRYPT: {
long tokenKeyId = KeyFormattingUtils.getKeyIdFromFingerprint( long tokenKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(
mSecurityTokenConnection.getKeyFingerprint(KeyType.ENCRYPT)); stConnection.getKeyFingerprint(KeyType.ENCRYPT));
if (tokenKeyId != mRequiredInput.getSubKeyId()) { if (tokenKeyId != mRequiredInput.getSubKeyId()) {
throw new IOException(getString(R.string.error_wrong_security_token)); throw new IOException(getString(R.string.error_wrong_security_token));
@@ -208,7 +209,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
for (int i = 0; i < mRequiredInput.mInputData.length; i++) { for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
byte[] encryptedSessionKey = mRequiredInput.mInputData[i]; byte[] encryptedSessionKey = mRequiredInput.mInputData[i];
byte[] decryptedSessionKey = mSecurityTokenConnection byte[] decryptedSessionKey = stConnection
.decryptSessionKey(encryptedSessionKey, publicKeyRing.getPublicKey(tokenKeyId)); .decryptSessionKey(encryptedSessionKey, publicKeyRing.getPublicKey(tokenKeyId));
mInputParcel = mInputParcel.withCryptoData(encryptedSessionKey, decryptedSessionKey); mInputParcel = mInputParcel.withCryptoData(encryptedSessionKey, decryptedSessionKey);
} }
@@ -216,7 +217,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
} }
case SECURITY_TOKEN_SIGN: { case SECURITY_TOKEN_SIGN: {
long tokenKeyId = KeyFormattingUtils.getKeyIdFromFingerprint( long tokenKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(
mSecurityTokenConnection.getKeyFingerprint(KeyType.SIGN)); stConnection.getKeyFingerprint(KeyType.SIGN));
if (tokenKeyId != mRequiredInput.getSubKeyId()) { if (tokenKeyId != mRequiredInput.getSubKeyId()) {
throw new IOException(getString(R.string.error_wrong_security_token)); throw new IOException(getString(R.string.error_wrong_security_token));
@@ -227,15 +228,13 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
for (int i = 0; i < mRequiredInput.mInputData.length; i++) { for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
byte[] hash = mRequiredInput.mInputData[i]; byte[] hash = mRequiredInput.mInputData[i];
int algo = mRequiredInput.mSignAlgos[i]; int algo = mRequiredInput.mSignAlgos[i];
byte[] signedHash = mSecurityTokenConnection.calculateSignature(hash, algo); byte[] signedHash = stConnection.calculateSignature(hash, algo);
mInputParcel = mInputParcel.withCryptoData(hash, signedHash); mInputParcel = mInputParcel.withCryptoData(hash, signedHash);
} }
break; break;
} }
case SECURITY_TOKEN_MOVE_KEY_TO_CARD: { case SECURITY_TOKEN_MOVE_KEY_TO_CARD: {
// TODO: assume PIN and Admin PIN to be default for this operation Passphrase adminPin = new Passphrase("12345678");
mSecurityTokenConnection.setPin(new Passphrase("123456"));
mSecurityTokenConnection.setAdminPin(new Passphrase("12345678"));
KeyRepository keyRepository = KeyRepository keyRepository =
KeyRepository.create(this); KeyRepository.create(this);
@@ -257,7 +256,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
long subkeyId = buf.getLong(); long subkeyId = buf.getLong();
CanonicalizedSecretKey key = secretKeyRing.getSecretKey(subkeyId); CanonicalizedSecretKey key = secretKeyRing.getSecretKey(subkeyId);
byte[] tokenSerialNumber = Arrays.copyOf(mSecurityTokenConnection.getAid(), 16); byte[] tokenSerialNumber = Arrays.copyOf(stConnection.getAid(), 16);
Passphrase passphrase; Passphrase passphrase;
try { try {
@@ -267,21 +266,21 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
throw new IOException("Unable to get cached passphrase!"); throw new IOException("Unable to get cached passphrase!");
} }
mSecurityTokenConnection.changeKey(key, passphrase); stConnection.changeKey(key, passphrase, adminPin);
// TODO: Is this really used anywhere? // TODO: Is this really used anywhere?
mInputParcel = mInputParcel.withCryptoData(subkeyBytes, tokenSerialNumber); mInputParcel = mInputParcel.withCryptoData(subkeyBytes, tokenSerialNumber);
} }
// change PINs afterwards // change PINs afterwards
mSecurityTokenConnection.modifyPin(0x81, newPin); stConnection.modifyPin(0x81, newPin, null);
mSecurityTokenConnection.modifyPin(0x83, newAdminPin); stConnection.modifyPin(0x83, newAdminPin, adminPin);
break; break;
} }
case SECURITY_TOKEN_RESET_CARD: { case SECURITY_TOKEN_RESET_CARD: {
mSecurityTokenConnection.resetAndWipeToken(); stConnection.resetAndWipeToken();
mResultTokenInfo = mSecurityTokenConnection.getTokenInfo(); mResultTokenInfo = stConnection.getTokenInfo();
break; break;
} }
@@ -293,7 +292,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
} }
@Override @Override
protected final void onSecurityTokenPostExecute() { protected final void onSecurityTokenPostExecute(final SecurityTokenConnection stConnection) {
handleResult(mInputParcel); handleResult(mInputParcel);
// show finish // show finish
@@ -301,17 +300,17 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.DONE); nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.DONE);
if (mSecurityTokenConnection.isPersistentConnectionAllowed()) { if (stConnection.isPersistentConnectionAllowed()) {
// Just close // Just close
finish(); finish();
} else { } else {
mSecurityTokenConnection.clearSecureMessaging(); stConnection.clearSecureMessaging();
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
// check all 200ms if Security Token has been taken away // check all 200ms if Security Token has been taken away
while (true) { while (true) {
if (isSecurityTokenConnected()) { if (stConnection.isConnected()) {
try { try {
Thread.sleep(200); Thread.sleep(200);
} catch (InterruptedException ignored) { } catch (InterruptedException ignored) {

View File

@@ -15,6 +15,7 @@ import android.view.animation.DecelerateInterpolator;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.securitytoken.NfcSweetspotData; import org.sufficientlysecure.keychain.securitytoken.NfcSweetspotData;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection;
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity; import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
@@ -88,7 +89,7 @@ public class ShowNfcSweetspotActivity extends BaseSecurityTokenActivity {
} }
@Override @Override
protected void onSecurityTokenPostExecute() { protected void onSecurityTokenPostExecute(SecurityTokenConnection stConnection) {
Intent result = new Intent(); Intent result = new Intent();
result.putExtra(EXTRA_TOKEN_INFO, tokenInfo); result.putExtra(EXTRA_TOKEN_INFO, tokenInfo);
setResult(Activity.RESULT_OK, result); setResult(Activity.RESULT_OK, result);

View File

@@ -68,12 +68,12 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
private static final String FIDESMO_APP_PACKAGE = "com.fidesmo.sec.android"; private static final String FIDESMO_APP_PACKAGE = "com.fidesmo.sec.android";
protected SecurityTokenConnection mSecurityTokenConnection = SecurityTokenConnection.getInstance();
protected TagDispatcher mNfcTagDispatcher; protected TagDispatcher mNfcTagDispatcher;
protected UsbConnectionDispatcher mUsbDispatcher; protected UsbConnectionDispatcher mUsbDispatcher;
private boolean mTagHandlingEnabled; private boolean mTagHandlingEnabled;
protected SecurityTokenInfo tokenInfo; protected SecurityTokenInfo tokenInfo;
private Passphrase mCachedPin;
/** /**
* Override to change UI before SecurityToken handling (UI thread) * Override to change UI before SecurityToken handling (UI thread)
@@ -84,15 +84,15 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
/** /**
* Override to implement SecurityToken operations (background thread) * Override to implement SecurityToken operations (background thread)
*/ */
protected void doSecurityTokenInBackground() throws IOException { protected void doSecurityTokenInBackground(SecurityTokenConnection stConnection) throws IOException {
tokenInfo = mSecurityTokenConnection.getTokenInfo(); tokenInfo = stConnection.getTokenInfo();
Log.d(Constants.TAG, "Security Token: " + tokenInfo); Log.d(Constants.TAG, "Security Token: " + tokenInfo);
} }
/** /**
* Override to handle result of SecurityToken operations (UI thread) * Override to handle result of SecurityToken operations (UI thread)
*/ */
protected void onSecurityTokenPostExecute() { protected void onSecurityTokenPostExecute(SecurityTokenConnection stConnection) {
Intent intent = new Intent(this, CreateKeyActivity.class); Intent intent = new Intent(this, CreateKeyActivity.class);
intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_TOKEN_INFO, tokenInfo); intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_TOKEN_INFO, tokenInfo);
startActivity(intent); startActivity(intent);
@@ -138,6 +138,10 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
// Actual Security Token operations are executed in doInBackground to not block the UI thread // Actual Security Token operations are executed in doInBackground to not block the UI thread
if (!mTagHandlingEnabled) if (!mTagHandlingEnabled)
return; return;
final SecurityTokenConnection stConnection =
SecurityTokenConnection.getInstanceForTransport(transport, mCachedPin);
new AsyncTask<Void, Void, IOException>() { new AsyncTask<Void, Void, IOException>() {
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
@@ -148,7 +152,9 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
@Override @Override
protected IOException doInBackground(Void... params) { protected IOException doInBackground(Void... params) {
try { try {
handleSecurityToken(transport, BaseSecurityTokenActivity.this); stConnection.connectIfNecessary(getBaseContext());
handleSecurityToken(stConnection);
} catch (IOException e) { } catch (IOException e) {
return e; return e;
} }
@@ -161,11 +167,11 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
super.onPostExecute(exception); super.onPostExecute(exception);
if (exception != null) { if (exception != null) {
handleSecurityTokenError(exception); handleSecurityTokenError(stConnection, exception);
return; return;
} }
onSecurityTokenPostExecute(); onSecurityTokenPostExecute(stConnection);
} }
}.execute(); }.execute();
} }
@@ -223,7 +229,7 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
mNfcTagDispatcher.interceptIntent(intent); mNfcTagDispatcher.interceptIntent(intent);
} }
private void handleSecurityTokenError(IOException e) { private void handleSecurityTokenError(SecurityTokenConnection stConnection, IOException e) {
if (e instanceof TagLostException) { if (e instanceof TagLostException) {
onSecurityTokenError(getString(R.string.security_token_error_tag_lost)); onSecurityTokenError(getString(R.string.security_token_error_tag_lost));
@@ -250,7 +256,7 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
SecurityTokenInfo tokeninfo = null; SecurityTokenInfo tokeninfo = null;
try { try {
tokeninfo = mSecurityTokenConnection.getTokenInfo(); tokeninfo = stConnection.getTokenInfo();
} catch (IOException e2) { } catch (IOException e2) {
// don't care // don't care
} }
@@ -271,7 +277,7 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
case 0x6982: { case 0x6982: {
SecurityTokenInfo tokeninfo = null; SecurityTokenInfo tokeninfo = null;
try { try {
tokeninfo = mSecurityTokenConnection.getTokenInfo(); tokeninfo = stConnection.getTokenInfo();
} catch (IOException e2) { } catch (IOException e2) {
// don't care // don't care
} }
@@ -325,7 +331,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 (mSecurityTokenConnection.isFidesmoToken()) { if (stConnection.isFidesmoToken()) {
// 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();
@@ -391,12 +397,11 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
} }
protected void obtainSecurityTokenPin(RequiredInputParcel requiredInput) { protected void obtainSecurityTokenPin(RequiredInputParcel requiredInput) {
try { try {
Passphrase passphrase = PassphraseCacheService.getCachedPassphrase(this, Passphrase passphrase = PassphraseCacheService.getCachedPassphrase(this,
requiredInput.getMasterKeyId(), requiredInput.getSubKeyId()); requiredInput.getMasterKeyId(), requiredInput.getSubKeyId());
if (passphrase != null) { if (passphrase != null) {
mSecurityTokenConnection.setPin(passphrase); mCachedPin = passphrase;
return; return;
} }
@@ -421,7 +426,7 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
return; return;
} }
CryptoInputParcel input = data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT); CryptoInputParcel input = data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
mSecurityTokenConnection.setPin(input.getPassphrase()); mCachedPin = input.getPassphrase();
break; break;
} }
default: default:
@@ -429,19 +434,8 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
} }
} }
protected void handleSecurityToken(Transport transport, Context ctx) throws IOException { protected void handleSecurityToken(SecurityTokenConnection stConnection) throws IOException {
// Don't reconnect if device was already connected doSecurityTokenInBackground(stConnection);
if (!(mSecurityTokenConnection.isPersistentConnectionAllowed()
&& mSecurityTokenConnection.isConnected()
&& mSecurityTokenConnection.getTransport().equals(transport))) {
mSecurityTokenConnection.setTransport(transport);
mSecurityTokenConnection.connectToDevice(ctx);
}
doSecurityTokenInBackground();
}
public boolean isSecurityTokenConnected() {
return mSecurityTokenConnection.isConnected();
} }
public static class IsoDepNotSupportedException extends IOException { public static class IsoDepNotSupportedException extends IOException {
@@ -500,10 +494,6 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
mUsbDispatcher.onStart(); mUsbDispatcher.onStart();
} }
public SecurityTokenConnection getSecurityTokenHelper() {
return mSecurityTokenConnection;
}
/** /**
* Run Security Token routines if last used token is connected and supports * Run Security Token routines if last used token is connected and supports
* persistent connections * persistent connections

View File

@@ -79,6 +79,7 @@ import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection;
import org.sufficientlysecure.keychain.service.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
@@ -619,8 +620,8 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
} }
@Override @Override
protected void onSecurityTokenPostExecute() { protected void onSecurityTokenPostExecute(SecurityTokenConnection stConnection) {
super.onSecurityTokenPostExecute(); super.onSecurityTokenPostExecute(stConnection);
finish(); finish();
} }