diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index ee481ad31..5e8ad96cd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -124,7 +124,7 @@ public class PassphraseCacheService extends Service { public static void addCachedPassphrase(Context context, long masterKeyId, long subKeyId, Passphrase passphrase, 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.setAction(ACTION_PASSPHRASE_CACHE_ADD); @@ -138,6 +138,19 @@ public class PassphraseCacheService extends Service { 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 * 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())) { AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); - // Stop all ttl alarms - for (int i = 0; i < mPassphraseCache.size(); i++) { - am.cancel(buildIntent(this, mPassphraseCache.keyAt(i))); - } + if (intent.hasExtra(EXTRA_SUBKEY_ID) && intent.hasExtra(EXTRA_KEY_ID)) { - 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(); } else { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java index d8d87114e..471fc0ec9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java @@ -56,6 +56,10 @@ public class RequiredInputParcel implements Parcelable { } + public long getMasterKeyId() { + return mMasterKeyId; + } + public long getSubKeyId() { return mSubKeyId; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java index 549d9ece7..511183b04 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java @@ -14,10 +14,12 @@ import android.view.WindowManager; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; import java.io.IOException; @@ -89,4 +91,24 @@ public class NfcOperationActivity extends BaseNfcActivity { 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)); + + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java index 365d32918..a8a5a1f28 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java @@ -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, * disables NFC Foreground Dispatch @@ -170,10 +176,7 @@ public abstract class BaseNfcActivity extends BaseActivity { + "D27600012401" // Data (6 bytes) + "00"; // Le if ( ! nfcCommunicate(opening).equals(accepted)) { // activate connection - toast("Opening Error!"); - setResult(RESULT_CANCELED); - finish(); - return; + throw new IOException("Initialization failed!"); } if (mPin != null) { @@ -189,9 +192,7 @@ public abstract class BaseNfcActivity extends BaseActivity { + String.format("%02x", pin.length) // Lc + Hex.toHexString(pin); if (!nfcCommunicate(login).equals(accepted)) { // login - toast("Wrong PIN!"); - setResult(RESULT_CANCELED); - finish(); + handlePinError(); return; } @@ -321,7 +322,7 @@ public abstract class BaseNfcActivity extends BaseActivity { switch (hashAlgo) { case HashAlgorithmTags.SHA1: 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 + "3021" // Tag/Length of Sequence, the 0x21 includes all following 33 bytes @@ -332,36 +333,36 @@ public abstract class BaseNfcActivity extends BaseActivity { break; case HashAlgorithmTags.RIPEMD160: 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); break; case HashAlgorithmTags.SHA224: 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); break; case HashAlgorithmTags.SHA256: 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); break; case HashAlgorithmTags.SHA384: 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); break; case HashAlgorithmTags.SHA512: 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); break; 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) @@ -388,14 +389,12 @@ public abstract class BaseNfcActivity extends BaseActivity { Log.d(Constants.TAG, "final response:" + status); if ( ! "9000".equals(status)) { - toast("Bad NFC response code: " + status); - return null; + throw new IOException("Bad NFC response code: " + status); } // Make sure the signature we received is actually the expected number of bytes long! if (signature.length() != 256 && signature.length() != 512) { - toast("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2); - return null; + throw new IOException("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2); } return Hex.decode(signature); diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 402dcdf7c..c8e5ea7ca 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -1279,5 +1279,6 @@ "Import" Different key stored on Yubikey! "NFC Error: %s" + Default PIN was rejected!