From 83af19de20cbfa8fe99216f4dcb8af8819570147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 8 Sep 2014 00:01:29 +0200 Subject: Prepare Yubikey decryption --- .../keychain/pgp/CanonicalizedSecretKey.java | 57 +++++++++++++--------- .../keychain/pgp/PgpDecryptVerify.java | 20 ++++++-- .../keychain/remote/OpenPgpService.java | 34 +++++++++++-- .../keychain/service/KeychainIntentService.java | 3 +- 4 files changed, 83 insertions(+), 31 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 833e1ad3d..f0ce2ea78 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -36,6 +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.NfcSyncPGPContentSignerBuilder; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; @@ -189,14 +190,8 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { return supported; } - public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext, - byte[] nfcSignedHash, Date nfcCreationTimestamp) - throws PgpGeneralException { - if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) { - throw new PrivateKeyNotUnlockedException(); - } - - PGPContentSignerBuilder contentSignerBuilder; + private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, byte[] nfcSignedHash, + Date nfcCreationTimestamp) { if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) { // to sign using nfc PgpSignEncrypt is executed two times. // the first time it stops to return the PendingIntent for nfc connection and signing the hash @@ -207,18 +202,27 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { } // use synchronous "NFC based" SignerBuilder - contentSignerBuilder = new NfcSyncPGPContentSignerBuilder( + return new NfcSyncPGPContentSignerBuilder( mSecretKey.getPublicKey().getAlgorithm(), hashAlgo, mSecretKey.getKeyID(), nfcSignedHash, nfcCreationTimestamp) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - - Log.d(Constants.TAG, "mSecretKey.getKeyID() " + PgpKeyHelper.convertKeyIdToHex(mSecretKey.getKeyID())); } else { // content signer based on signing key algorithm and chosen hash algorithm - contentSignerBuilder = new JcaPGPContentSignerBuilder( + return new JcaPGPContentSignerBuilder( mSecretKey.getPublicKey().getAlgorithm(), hashAlgo) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); } + } + + public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext, + byte[] nfcSignedHash, Date nfcCreationTimestamp) + throws PgpGeneralException { + if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) { + throw new PrivateKeyNotUnlockedException(); + } + + PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgo, + nfcSignedHash, nfcCreationTimestamp); int signatureType; if (cleartext) { @@ -247,12 +251,17 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { } public PublicKeyDataDecryptorFactory getDecryptorFactory() { - // TODO: divert to card missing - if (mPrivateKeyState != PRIVATE_KEY_STATE_UNLOCKED) { + if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) { throw new PrivateKeyNotUnlockedException(); } - return new JcePublicKeyDataDecryptorFactoryBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey); + + if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) { + return new NfcPublicKeyDataDecryptorFactoryBuilder() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey); + } else { + return new JcePublicKeyDataDecryptorFactoryBuilder() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey); + } } /** @@ -262,12 +271,11 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { * @param userIds User IDs to certify, must not be null or empty * @return A keyring with added certifications */ - public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List userIds) + public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List userIds, + byte[] nfcSignedHash, Date nfcCreationTimestamp) throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException, PGPException, SignatureException { - - // TODO: divert to card missing - if (mPrivateKeyState != PRIVATE_KEY_STATE_UNLOCKED) { + if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) { throw new PrivateKeyNotUnlockedException(); } @@ -275,9 +283,8 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { PGPSignatureGenerator signatureGenerator; { // TODO: SHA256 fixed? - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( - mSecretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(PGPUtil.SHA256, + nfcSignedHash, nfcCreationTimestamp); signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey); @@ -285,6 +292,10 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { { // supply signatureGenerator with a SubpacketVector PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + if (nfcCreationTimestamp != null) { + spGen.setSignatureCreationTime(false, nfcCreationTimestamp); + Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp); + } PGPSignatureSubpacketVector packetVector = spGen.generate(); signatureGenerator.setHashedSubpackets(packetVector); } 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 cb077c55c..6abef1ad2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -42,6 +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.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; @@ -59,6 +60,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URLConnection; import java.security.SignatureException; +import java.util.Date; import java.util.Iterator; import java.util.Set; @@ -193,13 +195,21 @@ public class PgpDecryptVerify { } } + public static class NeedNfcDataException extends Exception { + public byte[] mDec; + + public NeedNfcDataException(byte[] dec) { + mDec = dec; + } + } + /** * Decrypts and/or verifies data based on parameters of class */ public PgpDecryptVerifyResult execute() throws IOException, PGPException, SignatureException, WrongPassphraseException, NoSecretKeyException, KeyExtractionException, - InvalidDataException, IntegrityCheckFailedException { + InvalidDataException, IntegrityCheckFailedException, NeedNfcDataException { // automatically works with ascii armor input and binary InputStream in = PGPUtil.getDecoderStream(mData.getInputStream()); if (in instanceof ArmoredInputStream) { @@ -223,7 +233,7 @@ public class PgpDecryptVerify { private PgpDecryptVerifyResult decryptVerify(InputStream in) throws IOException, PGPException, SignatureException, WrongPassphraseException, KeyExtractionException, NoSecretKeyException, - InvalidDataException, IntegrityCheckFailedException { + InvalidDataException, IntegrityCheckFailedException, NeedNfcDataException { PgpDecryptVerifyResult result = new PgpDecryptVerifyResult(); PGPObjectFactory pgpF = new PGPObjectFactory(in, new JcaKeyFingerprintCalculator()); @@ -370,7 +380,11 @@ public class PgpDecryptVerify { updateProgress(R.string.progress_preparing_streams, currentProgress, 100); PublicKeyDataDecryptorFactory decryptorFactory = secretEncryptionKey.getDecryptorFactory(); - clear = encryptedDataAsymmetric.getDataStream(decryptorFactory); + try { + clear = encryptedDataAsymmetric.getDataStream(decryptorFactory); + } catch (NfcPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) { + throw new NeedNfcDataException(e.dec); + } encryptedData = encryptedDataAsymmetric; } else { // no packet has been found where we have the corresponding secret key in our db 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 00a9dc8f1..3b4559cf2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -155,15 +155,38 @@ public class OpenPgpService extends RemoteService { } } - private Intent getNfcIntent(Intent data, byte[] hashToSign, int hashAlgo) { + private Intent getNfcSignIntent(Intent data, String pin, byte[] hashToSign, int hashAlgo) { // build PendingIntent for Yubikey NFC operations Intent intent = new Intent(getBaseContext(), NfcActivity.class); intent.setAction(NfcActivity.ACTION_SIGN_HASH); + // pass params through to activity that it can be returned again later to repeat pgp operation + intent.putExtra(NfcActivity.EXTRA_DATA, data); + intent.putExtra(NfcActivity.EXTRA_PIN, pin); + intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign); intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); + PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, + intent, + PendingIntent.FLAG_CANCEL_CURRENT); + + // return PendingIntent to be executed by client + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_INTENT, pi); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED); + return result; + } + + private Intent getNfcDecryptIntent(Intent data, String pin, byte[] dec) { + // build PendingIntent for Yubikey NFC operations + Intent intent = new Intent(getBaseContext(), NfcActivity.class); + intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY); // pass params through to activity that it can be returned again later to repeat pgp operation intent.putExtra(NfcActivity.EXTRA_DATA, data); + intent.putExtra(NfcActivity.EXTRA_PIN, pin); + + intent.putExtra(NfcActivity.EXTRA_NFC_DEC, dec); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); @@ -257,7 +280,7 @@ public class OpenPgpService extends RemoteService { // pass through the signature creation timestamp to be used again on second execution // of PgpSignEncrypt when we have the signed hash! data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, e.mCreationTimestamp.getTime()); - return getNfcIntent(data, e.mHashToSign, e.mHashAlgo); + return getNfcSignIntent(data, passphrase, e.mHashToSign, e.mHashAlgo); } } finally { is.close(); @@ -322,8 +345,8 @@ public class OpenPgpService extends RemoteService { .setOriginalFilename(originalFilename) .setAdditionalEncryptId(accSettings.getKeyId()); // add acc key for encryption + String passphrase = null; if (sign) { - String passphrase; if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); } else { @@ -359,7 +382,7 @@ public class OpenPgpService extends RemoteService { // pass through the signature creation timestamp to be used again on second execution // of PgpSignEncrypt when we have the signed hash! data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, e.mCreationTimestamp.getTime()); - return getNfcIntent(data, e.mHashToSign, e.mHashAlgo); + return getNfcSignIntent(data, passphrase, e.mHashToSign, e.mHashAlgo); } } finally { is.close(); @@ -439,6 +462,9 @@ public class OpenPgpService extends RemoteService { throw new Exception(getString(R.string.error_no_secret_key_found)); } catch (PgpDecryptVerify.IntegrityCheckFailedException e) { 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); } if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index a5b672e7e..b9746aee5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -697,7 +697,8 @@ public class KeychainIntentService extends IntentService implements Progressable if (!certificationKey.unlock(signaturePassphrase)) { throw new PgpGeneralException("Error extracting key (bad passphrase?)"); } - UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds); + // TODO: supply nfc stuff + UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds, null, null); // store the signed key in our local cache providerHelper.savePublicKeyRing(newRing); -- cgit v1.2.3