diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp')
6 files changed, 100 insertions, 51 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java index 77977b691..d2384e679 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java @@ -61,6 +61,8 @@ public abstract class KeyRing { private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$"); + private static final Pattern EMAIL_PATTERN = Pattern.compile("^.*@.*\\..*$"); + /** * Splits userId string into naming part, email part, and comment part * <p/> @@ -71,25 +73,38 @@ public abstract class KeyRing { if (!TextUtils.isEmpty(userId)) { final Matcher matcher = USER_ID_PATTERN.matcher(userId); if (matcher.matches()) { - return new UserId(matcher.group(1), matcher.group(3), matcher.group(2)); + String name = matcher.group(1).isEmpty() ? null : matcher.group(1); + String comment = matcher.group(2); + String email = matcher.group(3); + if (comment == null && email == null && name != null && EMAIL_PATTERN.matcher(name).matches()) { + email = name; + name = null; + } + return new UserId(name, email, comment); } } return new UserId(null, null, null); } /** - * Returns a composed user id. Returns null if name is null! + * Returns a composed user id. Returns null if name, email and comment are empty. */ public static String createUserId(UserId userId) { - String userIdString = userId.name; // consider name a required value - if (userIdString != null && !TextUtils.isEmpty(userId.comment)) { - userIdString += " (" + userId.comment + ")"; + StringBuilder userIdBuilder = new StringBuilder(); + if (!TextUtils.isEmpty(userId.name)) { + userIdBuilder.append(userId.comment); } - if (userIdString != null && !TextUtils.isEmpty(userId.email)) { - userIdString += " <" + userId.email + ">"; + if (!TextUtils.isEmpty(userId.comment)) { + userIdBuilder.append(" ("); + userIdBuilder.append(userId.comment); + userIdBuilder.append(")"); } - - return userIdString; + if (!TextUtils.isEmpty(userId.email)) { + userIdBuilder.append(" <"); + userIdBuilder.append(userId.email); + userIdBuilder.append(">"); + } + return userIdBuilder.length() == 0 ? null : userIdBuilder.toString(); } public static class UserId implements Serializable { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java index c4525e5cd..31a3f91b6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java @@ -26,6 +26,8 @@ public class OpenPgpDecryptionResultBuilder { // builder private boolean mInsecure = false; private boolean mEncrypted = false; + private byte[] sessionKey; + private byte[] decryptedSessionKey; public void setInsecure(boolean insecure) { this.mInsecure = insecure; @@ -36,24 +38,26 @@ public class OpenPgpDecryptionResultBuilder { } public OpenPgpDecryptionResult build() { - OpenPgpDecryptionResult result = new OpenPgpDecryptionResult(); - if (mInsecure) { Log.d(Constants.TAG, "RESULT_INSECURE"); - result.setResult(OpenPgpDecryptionResult.RESULT_INSECURE); - return result; + return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_INSECURE, sessionKey, decryptedSessionKey); } if (mEncrypted) { Log.d(Constants.TAG, "RESULT_ENCRYPTED"); - result.setResult(OpenPgpDecryptionResult.RESULT_ENCRYPTED); - } else { - Log.d(Constants.TAG, "RESULT_NOT_ENCRYPTED"); - result.setResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED); + return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_ENCRYPTED, sessionKey, decryptedSessionKey); } - return result; + Log.d(Constants.TAG, "RESULT_NOT_ENCRYPTED"); + return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED); } + public void setSessionKey(byte[] sessionKey, byte[] decryptedSessionKey) { + if ((sessionKey == null) != (decryptedSessionKey == null)) { + throw new AssertionError("sessionKey must be null iff decryptedSessionKey is null!"); + } + this.sessionKey = sessionKey; + this.decryptedSessionKey = decryptedSessionKey; + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java index aa1c2e037..ae0a31191 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java @@ -37,7 +37,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; -import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder; +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenSignOperationsBuilder; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; @@ -76,7 +76,7 @@ public class PgpCertifyOperation { // get the master subkey (which we certify for) PGPPublicKey publicKey = publicRing.getPublicKey().getPublicKey(); - NfcSignOperationsBuilder requiredInput = new NfcSignOperationsBuilder(creationTimestamp, + SecurityTokenSignOperationsBuilder requiredInput = new SecurityTokenSignOperationsBuilder(creationTimestamp, publicKey.getKeyID(), publicKey.getKeyID()); try { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java index e15139a7f..a27e4a8d5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java @@ -26,9 +26,12 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.ByteBuffer; import java.security.SignatureException; import java.util.Date; import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; import android.content.Context; import android.support.annotation.NonNull; @@ -60,7 +63,6 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants.key; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.BaseOperation; -import org.sufficientlysecure.keychain.util.CharsetVerifier; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; @@ -73,6 +75,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.CharsetVerifier; import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; @@ -197,6 +200,10 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp PGPEncryptedData encryptedData; InputStream cleartextStream; + // the cached session key + byte[] sessionKey; + byte[] decryptedSessionKey; + int symmetricEncryptionAlgo = 0; boolean skippedDisallowedKey = false; @@ -304,6 +311,9 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp // if this worked out so far, the data is encrypted decryptionResultBuilder.setEncrypted(true); + if (esResult.sessionKey != null && esResult.decryptedSessionKey != null) { + decryptionResultBuilder.setSessionKey(esResult.sessionKey, esResult.decryptedSessionKey); + } if (esResult.insecureEncryptionKey) { log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1); @@ -545,10 +555,14 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp boolean asymmetricPacketFound = false; boolean symmetricPacketFound = false; boolean anyPacketFound = false; + boolean decryptedSessionKeyAvailable = false; PGPPublicKeyEncryptedData encryptedDataAsymmetric = null; PGPPBEEncryptedData encryptedDataSymmetric = null; CanonicalizedSecretKey decryptionKey = null; + CachingDataDecryptorFactory cachedKeyDecryptorFactory = new CachingDataDecryptorFactory( + Constants.BOUNCY_CASTLE_PROVIDER_NAME, cryptoInput.getCryptoData()); + ; Passphrase passphrase = null; @@ -569,6 +583,13 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp log.add(LogType.MSG_DC_ASYM, indent, KeyFormattingUtils.convertKeyIdToHex(subKeyId)); + decryptedSessionKeyAvailable = cachedKeyDecryptorFactory.hasCachedSessionData(encData); + if (decryptedSessionKeyAvailable) { + asymmetricPacketFound = true; + encryptedDataAsymmetric = encData; + break; + } + CachedPublicKeyRing cachedPublicKeyRing; try { // get actual keyring object based on master key id @@ -746,34 +767,38 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp currentProgress += 2; updateProgress(R.string.progress_extracting_key, currentProgress, 100); - try { - log.add(LogType.MSG_DC_UNLOCKING, indent + 1); - if (!decryptionKey.unlock(passphrase)) { - log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1); + CachingDataDecryptorFactory decryptorFactory; + if (decryptedSessionKeyAvailable) { + decryptorFactory = cachedKeyDecryptorFactory; + } else { + try { + log.add(LogType.MSG_DC_UNLOCKING, indent + 1); + if (!decryptionKey.unlock(passphrase)) { + log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1); + return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log)); + } + } catch (PgpGeneralException e) { + log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent + 1); return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log)); } - } catch (PgpGeneralException e) { - log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent + 1); - return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log)); - } - - currentProgress += 2; - updateProgress(R.string.progress_preparing_streams, currentProgress, 100); - CachingDataDecryptorFactory decryptorFactory - = decryptionKey.getCachingDecryptorFactory(cryptoInput); + currentProgress += 2; + updateProgress(R.string.progress_preparing_streams, currentProgress, 100); - // special case: if the decryptor does not have a session key cached for this encrypted - // data, and can't actually decrypt on its own, return a pending intent - if (!decryptorFactory.canDecrypt() - && !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) { + decryptorFactory = decryptionKey.getCachingDecryptorFactory(cryptoInput); - log.add(LogType.MSG_DC_PENDING_NFC, indent + 1); - return result.with(new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation( - decryptionKey.getRing().getMasterKeyId(), - decryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0] - ), cryptoInput)); + // special case: if the decryptor does not have a session key cached for this encrypted + // data, and can't actually decrypt on its own, return a pending intent + if (!decryptorFactory.canDecrypt() + && !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) { + log.add(LogType.MSG_DC_PENDING_NFC, indent + 1); + return result.with(new DecryptVerifyResult(log, + RequiredInputParcel.createSecurityTokenDecryptOperation( + decryptionKey.getRing().getMasterKeyId(), + decryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0] + ), cryptoInput)); + } } try { @@ -786,8 +811,13 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp result.symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory); result.encryptedData = encryptedDataAsymmetric; - cryptoInput.addCryptoData(decryptorFactory.getCachedSessionKeys()); - + Map<ByteBuffer, byte[]> cachedSessionKeys = decryptorFactory.getCachedSessionKeys(); + cryptoInput.addCryptoData(cachedSessionKeys); + if (cachedSessionKeys.size() >= 1) { + Entry<ByteBuffer, byte[]> entry = cachedSessionKeys.entrySet().iterator().next(); + result.sessionKey = entry.getKey().array(); + result.decryptedSessionKey = entry.getValue(); + } } else { // there wasn't even any useful data if (!anyPacketFound) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 102e11674..ce9c30894 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -78,8 +78,8 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; -import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcKeyToCardOperationsBuilder; -import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder; +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenKeyToCardOperationsBuilder; +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.SecurityTokenSignOperationsBuilder; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -496,10 +496,10 @@ public class PgpKeyOperation { OperationLog log, int indent) { - NfcSignOperationsBuilder nfcSignOps = new NfcSignOperationsBuilder( + SecurityTokenSignOperationsBuilder nfcSignOps = new SecurityTokenSignOperationsBuilder( cryptoInput.getSignatureTime(), masterSecretKey.getKeyID(), masterSecretKey.getKeyID()); - NfcKeyToCardOperationsBuilder nfcKeyToCardOps = new NfcKeyToCardOperationsBuilder( + SecurityTokenKeyToCardOperationsBuilder nfcKeyToCardOps = new SecurityTokenKeyToCardOperationsBuilder( masterSecretKey.getKeyID()); progress(R.string.progress_modify, 0); @@ -1277,7 +1277,7 @@ public class PgpKeyOperation { PGPPublicKey masterPublicKey, int flags, long expiry, CryptoInputParcel cryptoInput, - NfcSignOperationsBuilder nfcSignOps, + SecurityTokenSignOperationsBuilder nfcSignOps, int indent, OperationLog log) throws PGPException, IOException, SignatureException { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java index 48ba68fa6..0bb6419eb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java @@ -528,7 +528,7 @@ public class PgpSignEncryptOperation extends BaseOperation { } catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) { // this secret key diverts to a OpenPGP card, throw exception with hash that will be signed log.add(LogType.MSG_PSE_PENDING_NFC, indent); - return new PgpSignEncryptResult(log, RequiredInputParcel.createNfcSignOperation( + return new PgpSignEncryptResult(log, RequiredInputParcel.createSecurityTokenSignOperation( signingKey.getRing().getMasterKeyId(), signingKey.getKeyId(), e.hashToSign, e.hashAlgo, cryptoInput.getSignatureTime()), cryptoInput); } |