further improve yubikey error handling

This commit is contained in:
Vincent Breitmoser 2015-03-23 01:44:14 +01:00
parent 3bb194fc08
commit c694d73cab
5 changed files with 78 additions and 24 deletions

View File

@ -124,7 +124,7 @@ public class PassphraseCacheService extends Service {
public static void addCachedPassphrase(Context context, long masterKeyId, long subKeyId, public static void addCachedPassphrase(Context context, long masterKeyId, long subKeyId,
Passphrase passphrase, Passphrase passphrase,
String primaryUserId) { String primaryUserId) {
Log.d(Constants.TAG, "PassphraseCacheService.cacheNewPassphrase() for " + masterKeyId); Log.d(Constants.TAG, "PassphraseCacheService.addCachedPassphrase() for " + masterKeyId);
Intent intent = new Intent(context, PassphraseCacheService.class); Intent intent = new Intent(context, PassphraseCacheService.class);
intent.setAction(ACTION_PASSPHRASE_CACHE_ADD); intent.setAction(ACTION_PASSPHRASE_CACHE_ADD);
@ -138,6 +138,19 @@ public class PassphraseCacheService extends Service {
context.startService(intent); context.startService(intent);
} }
public static void clearCachedPassphrase(Context context, long masterKeyId, long subKeyId) {
Log.d(Constants.TAG, "PassphraseCacheService.clearCachedPassphrase() for " + masterKeyId);
Intent intent = new Intent(context, PassphraseCacheService.class);
intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
intent.putExtra(EXTRA_KEY_ID, masterKeyId);
intent.putExtra(EXTRA_SUBKEY_ID, subKeyId);
context.startService(intent);
}
/** /**
* Gets a cached passphrase from memory by sending an intent to the service. This method is * Gets a cached passphrase from memory by sending an intent to the service. This method is
* designed to wait until the service returns the passphrase. * designed to wait until the service returns the passphrase.
@ -395,12 +408,27 @@ public class PassphraseCacheService extends Service {
} else if (ACTION_PASSPHRASE_CACHE_CLEAR.equals(intent.getAction())) { } else if (ACTION_PASSPHRASE_CACHE_CLEAR.equals(intent.getAction())) {
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
// Stop all ttl alarms if (intent.hasExtra(EXTRA_SUBKEY_ID) && intent.hasExtra(EXTRA_KEY_ID)) {
for (int i = 0; i < mPassphraseCache.size(); i++) {
am.cancel(buildIntent(this, mPassphraseCache.keyAt(i)));
}
mPassphraseCache.clear(); long keyId;
if (Preferences.getPreferences(mContext).getPassphraseCacheSubs()) {
keyId = intent.getLongExtra(EXTRA_KEY_ID, 0L);
} else {
keyId = intent.getLongExtra(EXTRA_SUBKEY_ID, 0L);
}
// Stop specific ttl alarm and
am.cancel(buildIntent(this, keyId));
mPassphraseCache.delete(keyId);
} else {
// Stop all ttl alarms
for (int i = 0; i < mPassphraseCache.size(); i++) {
am.cancel(buildIntent(this, mPassphraseCache.keyAt(i)));
}
mPassphraseCache.clear();
}
updateService(); updateService();
} else { } else {

View File

@ -56,6 +56,10 @@ public class RequiredInputParcel implements Parcelable {
} }
public long getMasterKeyId() {
return mMasterKeyId;
}
public long getSubKeyId() { public long getSubKeyId() {
return mSubKeyId; return mSubKeyId;
} }

View File

@ -14,10 +14,12 @@ import android.view.WindowManager;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
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;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity; import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import java.io.IOException; import java.io.IOException;
@ -89,4 +91,24 @@ public class NfcOperationActivity extends BaseNfcActivity {
finish(); finish();
} }
@Override
public void handlePinError() {
// avoid a loop
Preferences prefs = Preferences.getPreferences(this);
if (prefs.useDefaultYubikeyPin()) {
toast(getString(R.string.error_pin_nodefault));
setResult(RESULT_CANCELED);
finish();
return;
}
// clear (invalid) passphrase
PassphraseCacheService.clearCachedPassphrase(
this, mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId());
obtainYubikeyPin(RequiredInputParcel.createRequiredPassphrase(mRequiredInput));
}
} }

View File

@ -79,6 +79,12 @@ public abstract class BaseNfcActivity extends BaseActivity {
} }
public void handlePinError() {
toast("Wrong PIN!");
setResult(RESULT_CANCELED);
finish();
}
/** /**
* Called when the system is about to start resuming a previous activity, * Called when the system is about to start resuming a previous activity,
* disables NFC Foreground Dispatch * disables NFC Foreground Dispatch
@ -170,10 +176,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
+ "D27600012401" // Data (6 bytes) + "D27600012401" // Data (6 bytes)
+ "00"; // Le + "00"; // Le
if ( ! nfcCommunicate(opening).equals(accepted)) { // activate connection if ( ! nfcCommunicate(opening).equals(accepted)) { // activate connection
toast("Opening Error!"); throw new IOException("Initialization failed!");
setResult(RESULT_CANCELED);
finish();
return;
} }
if (mPin != null) { if (mPin != null) {
@ -189,9 +192,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
+ String.format("%02x", pin.length) // Lc + String.format("%02x", pin.length) // Lc
+ Hex.toHexString(pin); + Hex.toHexString(pin);
if (!nfcCommunicate(login).equals(accepted)) { // login if (!nfcCommunicate(login).equals(accepted)) { // login
toast("Wrong PIN!"); handlePinError();
setResult(RESULT_CANCELED);
finish();
return; return;
} }
@ -321,7 +322,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
switch (hashAlgo) { switch (hashAlgo) {
case HashAlgorithmTags.SHA1: case HashAlgorithmTags.SHA1:
if (hash.length != 20) { if (hash.length != 20) {
throw new RuntimeException("Bad hash length (" + hash.length + ", expected 10!"); throw new IOException("Bad hash length (" + hash.length + ", expected 10!");
} }
dsi = "23" // Lc dsi = "23" // Lc
+ "3021" // Tag/Length of Sequence, the 0x21 includes all following 33 bytes + "3021" // Tag/Length of Sequence, the 0x21 includes all following 33 bytes
@ -332,36 +333,36 @@ public abstract class BaseNfcActivity extends BaseActivity {
break; break;
case HashAlgorithmTags.RIPEMD160: case HashAlgorithmTags.RIPEMD160:
if (hash.length != 20) { if (hash.length != 20) {
throw new RuntimeException("Bad hash length (" + hash.length + ", expected 20!"); throw new IOException("Bad hash length (" + hash.length + ", expected 20!");
} }
dsi = "233021300906052B2403020105000414" + getHex(hash); dsi = "233021300906052B2403020105000414" + getHex(hash);
break; break;
case HashAlgorithmTags.SHA224: case HashAlgorithmTags.SHA224:
if (hash.length != 28) { if (hash.length != 28) {
throw new RuntimeException("Bad hash length (" + hash.length + ", expected 28!"); throw new IOException("Bad hash length (" + hash.length + ", expected 28!");
} }
dsi = "2F302D300D06096086480165030402040500041C" + getHex(hash); dsi = "2F302D300D06096086480165030402040500041C" + getHex(hash);
break; break;
case HashAlgorithmTags.SHA256: case HashAlgorithmTags.SHA256:
if (hash.length != 32) { if (hash.length != 32) {
throw new RuntimeException("Bad hash length (" + hash.length + ", expected 32!"); throw new IOException("Bad hash length (" + hash.length + ", expected 32!");
} }
dsi = "333031300D060960864801650304020105000420" + getHex(hash); dsi = "333031300D060960864801650304020105000420" + getHex(hash);
break; break;
case HashAlgorithmTags.SHA384: case HashAlgorithmTags.SHA384:
if (hash.length != 48) { if (hash.length != 48) {
throw new RuntimeException("Bad hash length (" + hash.length + ", expected 48!"); throw new IOException("Bad hash length (" + hash.length + ", expected 48!");
} }
dsi = "433041300D060960864801650304020205000430" + getHex(hash); dsi = "433041300D060960864801650304020205000430" + getHex(hash);
break; break;
case HashAlgorithmTags.SHA512: case HashAlgorithmTags.SHA512:
if (hash.length != 64) { if (hash.length != 64) {
throw new RuntimeException("Bad hash length (" + hash.length + ", expected 64!"); throw new IOException("Bad hash length (" + hash.length + ", expected 64!");
} }
dsi = "533051300D060960864801650304020305000440" + getHex(hash); dsi = "533051300D060960864801650304020305000440" + getHex(hash);
break; break;
default: default:
throw new RuntimeException("Not supported hash algo!"); throw new IOException("Not supported hash algo!");
} }
// Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37) // Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37)
@ -388,14 +389,12 @@ public abstract class BaseNfcActivity extends BaseActivity {
Log.d(Constants.TAG, "final response:" + status); Log.d(Constants.TAG, "final response:" + status);
if ( ! "9000".equals(status)) { if ( ! "9000".equals(status)) {
toast("Bad NFC response code: " + status); throw new IOException("Bad NFC response code: " + status);
return null;
} }
// Make sure the signature we received is actually the expected number of bytes long! // Make sure the signature we received is actually the expected number of bytes long!
if (signature.length() != 256 && signature.length() != 512) { if (signature.length() != 256 && signature.length() != 512) {
toast("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2); throw new IOException("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2);
return null;
} }
return Hex.decode(signature); return Hex.decode(signature);

View File

@ -1279,5 +1279,6 @@
<string name="btn_import">"Import"</string> <string name="btn_import">"Import"</string>
<string name="snack_yubi_other">Different key stored on Yubikey!</string> <string name="snack_yubi_other">Different key stored on Yubikey!</string>
<string name="error_nfc">"NFC Error: %s"</string> <string name="error_nfc">"NFC Error: %s"</string>
<string name="error_pin_nodefault">Default PIN was rejected!</string>
</resources> </resources>