From 518d7116e2b5d5bf1588c0493aeed4512705114b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 8 Sep 2014 14:04:46 +0200 Subject: Work on Yubikey decryption --- .../keychain/pgp/CanonicalizedSecretKey.java | 26 +++++++++++++--------- .../keychain/pgp/PgpDecryptVerify.java | 25 +++++++++++++++------ .../keychain/remote/OpenPgpService.java | 13 ++++++----- .../keychain/ui/DecryptFileFragment.java | 18 ++++++++++++++- .../keychain/ui/EditKeyFragment.java | 1 + 5 files changed, 59 insertions(+), 24 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java index f0ce2ea78..1bb4537dc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -36,7 +36,7 @@ import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; -import org.spongycastle.openpgp.operator.jcajce.NfcPublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder; import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; @@ -82,21 +82,27 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { } public enum SecretKeyType { - UNAVAILABLE(0), GNU_DUMMY (1), PASSPHRASE (2), PASSPHRASE_EMPTY (3), DIVERT_TO_CARD (4); + UNAVAILABLE(0), GNU_DUMMY(1), PASSPHRASE(2), PASSPHRASE_EMPTY(3), DIVERT_TO_CARD(4); final int mNum; + SecretKeyType(int num) { mNum = num; } public static SecretKeyType fromNum(int num) { switch (num) { - case 1: return GNU_DUMMY; - case 2: return PASSPHRASE; - case 3: return PASSPHRASE_EMPTY; - case 4: return DIVERT_TO_CARD; + case 1: + return GNU_DUMMY; + case 2: + return PASSPHRASE; + case 3: + return PASSPHRASE_EMPTY; + case 4: + return DIVERT_TO_CARD; // if this case happens, it's probably a check from a database value - default: return UNAVAILABLE; + default: + return UNAVAILABLE; } } @@ -250,14 +256,14 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { } } - public PublicKeyDataDecryptorFactory getDecryptorFactory() { + public PublicKeyDataDecryptorFactory getDecryptorFactory(byte[] nfcDecryptedSessionKey) { if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) { throw new PrivateKeyNotUnlockedException(); } if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) { - return new NfcPublicKeyDataDecryptorFactoryBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey); + return new NfcSyncPublicKeyDataDecryptorFactoryBuilder() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(nfcDecryptedSessionKey); } else { return new JcePublicKeyDataDecryptorFactoryBuilder() .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java index 6abef1ad2..aa85b1082 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -42,7 +42,7 @@ import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; -import org.spongycastle.openpgp.operator.jcajce.NfcPublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; @@ -78,6 +78,7 @@ public class PgpDecryptVerify { private String mPassphrase; private Set mAllowedKeyIds; private boolean mDecryptMetadataOnly; + private byte[] mDecryptedSessionKey; private PgpDecryptVerify(Builder builder) { // private Constructor can only be called from Builder @@ -91,6 +92,7 @@ public class PgpDecryptVerify { this.mPassphrase = builder.mPassphrase; this.mAllowedKeyIds = builder.mAllowedKeyIds; this.mDecryptMetadataOnly = builder.mDecryptMetadataOnly; + this.mDecryptedSessionKey = builder.mDecryptedSessionKey; } public static class Builder { @@ -106,6 +108,7 @@ public class PgpDecryptVerify { private String mPassphrase = null; private Set mAllowedKeyIds = null; private boolean mDecryptMetadataOnly = false; + private byte[] mDecryptedSessionKey = null; public Builder(ProviderHelper providerHelper, PassphraseCache passphraseCache, InputData data, OutputStream outStream) { @@ -148,6 +151,11 @@ public class PgpDecryptVerify { return this; } + public Builder setNfcState(byte[] decryptedSessionKey) { + mDecryptedSessionKey = decryptedSessionKey; + return this; + } + public PgpDecryptVerify build() { return new PgpDecryptVerify(this); } @@ -196,10 +204,12 @@ public class PgpDecryptVerify { } public static class NeedNfcDataException extends Exception { - public byte[] mDec; + public byte[] mEncryptedSessionKey; + public String mPassphrase; - public NeedNfcDataException(byte[] dec) { - mDec = dec; + public NeedNfcDataException(byte[] encryptedSessionKey, String passphrase) { + mEncryptedSessionKey = encryptedSessionKey; + mPassphrase = passphrase; } } @@ -379,11 +389,12 @@ public class PgpDecryptVerify { currentProgress += 2; updateProgress(R.string.progress_preparing_streams, currentProgress, 100); - PublicKeyDataDecryptorFactory decryptorFactory = secretEncryptionKey.getDecryptorFactory(); try { + PublicKeyDataDecryptorFactory decryptorFactory + = secretEncryptionKey.getDecryptorFactory(mDecryptedSessionKey); clear = encryptedDataAsymmetric.getDataStream(decryptorFactory); - } catch (NfcPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) { - throw new NeedNfcDataException(e.dec); + } catch (NfcSyncPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) { + throw new NeedNfcDataException(e.encryptedSessionKey, mPassphrase); } encryptedData = encryptedDataAsymmetric; } else { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index f51a3ac14..0e1dcd7db 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -177,7 +177,7 @@ public class OpenPgpService extends RemoteService { return result; } - private Intent getNfcDecryptIntent(Intent data, String pin, byte[] dec) { + private Intent getNfcDecryptIntent(Intent data, String pin, byte[] encryptedSessionKey) { // build PendingIntent for Yubikey NFC operations Intent intent = new Intent(getBaseContext(), NfcActivity.class); intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY); @@ -185,7 +185,7 @@ public class OpenPgpService extends RemoteService { intent.putExtra(NfcActivity.EXTRA_DATA, data); intent.putExtra(NfcActivity.EXTRA_PIN, pin); - intent.putExtra(NfcActivity.EXTRA_NFC_DEC, dec); + intent.putExtra(NfcActivity.EXTRA_NFC_ENC_SESSION_KEY, encryptedSessionKey); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, intent, @@ -432,7 +432,6 @@ public class OpenPgpService extends RemoteService { Intent result = new Intent(); try { - String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); long inputLength = is.available(); InputData inputData = new InputData(is, inputLength); @@ -453,12 +452,15 @@ public class OpenPgpService extends RemoteService { inputData, os ); + byte[] nfcDecryptedSessionKey = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_DECRYPTED_SESSION_KEY); + // allow only private keys associated with accounts of this app // no support for symmetric encryption builder.setPassphrase(passphrase) .setAllowSymmetricDecryption(false) .setAllowedKeyIds(allowedKeyIds) - .setDecryptMetadataOnly(decryptMetadataOnly); + .setDecryptMetadataOnly(decryptMetadataOnly) + .setNfcState(nfcDecryptedSessionKey); PgpDecryptVerifyResult decryptVerifyResult; try { @@ -478,7 +480,7 @@ public class OpenPgpService extends RemoteService { throw new Exception(getString(R.string.error_integrity_check_failed)); } catch (PgpDecryptVerify.NeedNfcDataException e) { // return PendingIntent to execute NFC activity - return getNfcDecryptIntent(data, passphrase, e.mDec); + return getNfcDecryptIntent(data, e.mPassphrase, e.mEncryptedSessionKey); } if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) { @@ -522,7 +524,6 @@ public class OpenPgpService extends RemoteService { result.putExtra(OpenPgpApi.RESULT_METADATA, metadata); } } - } finally { is.close(); if (os != null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java index 845fbfa3b..ee1460ee3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java @@ -33,12 +33,15 @@ import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.TextView; +import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.FileHelper; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.util.Log; @@ -182,7 +185,20 @@ public class DecryptFileFragment extends DecryptFragment { returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT); if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) { - showPassphraseDialogForFilename(decryptVerifyResult.getKeyIdPassphraseNeeded()); + try { + // try to get passphrase from cache... + String passphrase = PassphraseCacheService.getCachedPassphrase( + getActivity(), decryptVerifyResult.getKeyIdPassphraseNeeded()); + + if (passphrase != null) { + // try again with passphrase from cache + decryptOriginalFilename(passphrase); + } else { + showPassphraseDialogForFilename(decryptVerifyResult.getKeyIdPassphraseNeeded()); + } + } catch (PassphraseCacheService.KeyNotFoundException e) { + Log.e(Constants.TAG, "PassphraseCacheService.KeyNotFoundException", e); + } } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) { showPassphraseDialogForFilename(Constants.key.symmetric); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java index 11d5292bb..830e856aa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -186,6 +186,7 @@ public class EditKeyFragment extends LoaderFragment implements mSaveKeyringParcel.mMasterKeyId); } catch (PassphraseCacheService.KeyNotFoundException e) { Log.e(Constants.TAG, "Key not found!", e); + Toast.makeText(getActivity(), R.string.error_no_secret_key_found, Toast.LENGTH_SHORT).show(); getActivity().finish(); return; } -- cgit v1.2.3