diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/algorithm/SymmetricKeyAlgorithm.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/algorithm/SymmetricKeyAlgorithm.java index ec5cec80..2972b05b 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/algorithm/SymmetricKeyAlgorithm.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/algorithm/SymmetricKeyAlgorithm.java @@ -46,7 +46,7 @@ public enum SymmetricKeyAlgorithm { } } - public static SymmetricKeyAlgorithm forId(int id) { + public static SymmetricKeyAlgorithm fromId(int id) { return MAP.get(id); } diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilder.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilder.java index 7688cdec..80cf1473 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilder.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilder.java @@ -33,7 +33,6 @@ public class DecryptionBuilder implements DecryptionBuilderInterface { private PGPSecretKeyRingCollection decryptionKeys; private SecretKeyRingProtector decryptionKeyDecryptor; private Set verificationKeys = new HashSet<>(); - private Set trustedKeyIds = new HashSet<>(); private MissingPublicKeyCallback missingPublicKeyCallback = null; @Override @@ -66,22 +65,23 @@ public class DecryptionBuilder implements DecryptionBuilderInterface { PGPPublicKeyRingCollection publicKeyRingCollection) { Set publicKeyRings = new HashSet<>(); for (Iterator i = publicKeyRingCollection.getKeyRings(); i.hasNext(); ) { - publicKeyRings.add(i.next()); + PGPPublicKeyRing p = i.next(); + if (trustedKeyIds.contains(p.getPublicKey().getKeyID())) { + publicKeyRings.add(p); + } } - return verifyWith(trustedKeyIds, publicKeyRings); + return verifyWith(publicKeyRings); } @Override - public MissingPublicKeyFeedback verifyWith(Set trustedKeyIds, Set publicKeyRings) { + public MissingPublicKeyFeedback verifyWith(Set publicKeyRings) { DecryptionBuilder.this.verificationKeys = publicKeyRings; - DecryptionBuilder.this.trustedKeyIds = trustedKeyIds; return new MissingPublicKeyFeedbackImpl(); } @Override public Build doNotVerify() { DecryptionBuilder.this.verificationKeys = null; - DecryptionBuilder.this.trustedKeyIds = null; return new BuildImpl(); } } @@ -105,7 +105,7 @@ public class DecryptionBuilder implements DecryptionBuilderInterface { @Override public DecryptionStream build() throws IOException, PGPException { return DecryptionStreamFactory.create(inputStream, - decryptionKeys, decryptionKeyDecryptor, verificationKeys, trustedKeyIds, missingPublicKeyCallback); + decryptionKeys, decryptionKeyDecryptor, verificationKeys, missingPublicKeyCallback); } } } diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilderInterface.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilderInterface.java index 062c498e..9c50b220 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilderInterface.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilderInterface.java @@ -41,7 +41,7 @@ public interface DecryptionBuilderInterface { MissingPublicKeyFeedback verifyWith(Set trustedFingerprints, PGPPublicKeyRingCollection publicKeyRings); - MissingPublicKeyFeedback verifyWith(Set trustedFingerprints, Set publicKeyRings); + MissingPublicKeyFeedback verifyWith(Set publicKeyRings); Build doNotVerify(); diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionStreamFactory.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionStreamFactory.java index 772658e7..b3e71b17 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionStreamFactory.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionStreamFactory.java @@ -23,8 +23,10 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; -import de.vanitasvitae.crypto.pgpainless.PainlessStream; import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm; import de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector; @@ -51,10 +53,12 @@ import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; public class DecryptionStreamFactory { + private static final Logger LOGGER = Logger.getLogger(DecryptionStreamFactory.class.getName()); + private static final Level LEVEL = Level.INFO; + private final PGPSecretKeyRingCollection decryptionKeys; private final SecretKeyRingProtector decryptionKeyDecryptor; private final Set verificationKeys = new HashSet<>(); - private final Set trustedKeyIds = new HashSet<>(); private final MissingPublicKeyCallback missingPublicKeyCallback; private final PainlessResult.Builder resultBuilder = PainlessResult.getBuilder(); @@ -65,12 +69,10 @@ public class DecryptionStreamFactory { private DecryptionStreamFactory(PGPSecretKeyRingCollection decryptionKeys, SecretKeyRingProtector decryptor, Set verificationKeys, - Set trustedKeyIds, MissingPublicKeyCallback missingPublicKeyCallback) { this.decryptionKeys = decryptionKeys; this.decryptionKeyDecryptor = decryptor; this.verificationKeys.addAll(verificationKeys != null ? verificationKeys : Collections.emptyList()); - this.trustedKeyIds.addAll(trustedKeyIds != null ? trustedKeyIds : Collections.emptyList()); this.missingPublicKeyCallback = missingPublicKeyCallback; } @@ -78,14 +80,12 @@ public class DecryptionStreamFactory { PGPSecretKeyRingCollection decryptionKeys, SecretKeyRingProtector decryptor, Set verificationKeys, - Set trustedKeyIds, MissingPublicKeyCallback missingPublicKeyCallback) throws IOException, PGPException { DecryptionStreamFactory factory = new DecryptionStreamFactory(decryptionKeys, decryptor, verificationKeys, - trustedKeyIds, missingPublicKeyCallback); PGPObjectFactory objectFactory = new PGPObjectFactory( @@ -100,6 +100,7 @@ public class DecryptionStreamFactory { while ((pgpObj = objectFactory.nextObject()) != null) { if (pgpObj instanceof PGPEncryptedDataList) { + LOGGER.log(LEVEL, "Encountered PGPEncryptedDataList"); PGPEncryptedDataList encDataList = (PGPEncryptedDataList) pgpObj; InputStream nextStream = decrypt(encDataList); objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(nextStream), fingerCalc); @@ -111,20 +112,25 @@ public class DecryptionStreamFactory { InputStream nextStream = compressedData.getDataStream(); resultBuilder.setCompressionAlgorithm(CompressionAlgorithm.fromId(compressedData.getAlgorithm())); objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(nextStream), fingerCalc); + LOGGER.log(LEVEL, "Encountered PGPCompressedData: " + + CompressionAlgorithm.fromId(compressedData.getAlgorithm())); return wrap(objectFactory); } if (pgpObj instanceof PGPOnePassSignatureList) { PGPOnePassSignatureList onePassSignatures = (PGPOnePassSignatureList) pgpObj; + LOGGER.log(LEVEL, "Encountered PGPOnePassSignatureList of size " + onePassSignatures.size()); initOnePassSignatures(onePassSignatures); return wrap(objectFactory); } if (pgpObj instanceof PGPLiteralData) { + LOGGER.log(LEVEL, "Encountered PGPLiteralData"); PGPLiteralData literalData = (PGPLiteralData) pgpObj; InputStream literalDataInputStream = literalData.getInputStream(); if (verifiableOnePassSignatures.isEmpty()) { + LOGGER.log(LEVEL, "No OnePassSignatures found -> We are done"); return literalDataInputStream; } @@ -133,14 +139,14 @@ public class DecryptionStreamFactory { } } - throw new PGPException("No Literal Data Packet found!"); + throw new PGPException("No Literal Data Packet found"); } private InputStream decrypt(PGPEncryptedDataList encryptedDataList) throws PGPException { Iterator iterator = encryptedDataList.getEncryptedDataObjects(); if (!iterator.hasNext()) { - throw new PGPException("Decryption failed - No encrypted data found!"); + throw new PGPException("Decryption failed - EncryptedDataList has no items"); } PGPPrivateKey decryptionKey = null; @@ -150,27 +156,37 @@ public class DecryptionStreamFactory { long keyId = encryptedSessionKey.getKeyID(); resultBuilder.addRecipientKeyId(keyId); - + LOGGER.log(LEVEL, "PGPEncryptedData is encrypted for key " + Long.toHexString(keyId)); if (decryptionKey != null) { continue; } PGPSecretKey secretKey = decryptionKeys.getSecretKey(keyId); if (secretKey != null) { + LOGGER.log(LEVEL, "Found respective secret key " + Long.toHexString(keyId)); decryptionKey = secretKey.extractPrivateKey(decryptionKeyDecryptor.getDecryptor(keyId)); resultBuilder.setDecryptionKeyId(keyId); } } if (decryptionKey == null) { - throw new PGPException("Decryption failed - No suitable decryption key found!"); + throw new PGPException("Decryption failed - No suitable decryption key found"); } PublicKeyDataDecryptorFactory keyDecryptor = new BcPublicKeyDataDecryptorFactory(decryptionKey); - resultBuilder.setSymmetricKeyAlgorithm( - SymmetricKeyAlgorithm.forId(encryptedSessionKey.getSymmetricAlgorithm(keyDecryptor))); - resultBuilder.setIntegrityProtected(encryptedSessionKey.isIntegrityProtected()); + SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm + .fromId(encryptedSessionKey.getSymmetricAlgorithm(keyDecryptor)); + LOGGER.log(LEVEL, "Message is encrypted using " + symmetricKeyAlgorithm); + resultBuilder.setSymmetricKeyAlgorithm(symmetricKeyAlgorithm); + + if (encryptedSessionKey.isIntegrityProtected()) { + LOGGER.log(LEVEL, "Message is integrity protected"); + resultBuilder.setIntegrityProtected(true); + } else { + LOGGER.log(LEVEL, "Message is not integrity protected"); + resultBuilder.setIntegrityProtected(false); + } InputStream decryptionStream = encryptedSessionKey.getDataStream(keyDecryptor); return decryptionStream; @@ -179,7 +195,7 @@ public class DecryptionStreamFactory { private void initOnePassSignatures(PGPOnePassSignatureList onePassSignatureList) throws PGPException { Iterator iterator = onePassSignatureList.iterator(); if (!iterator.hasNext()) { - throw new PGPException("Verification failed - No OnePassSignatures found!"); + throw new PGPException("Verification failed - No OnePassSignatures found"); } while (iterator.hasNext()) { @@ -187,10 +203,16 @@ public class DecryptionStreamFactory { long keyId = signature.getKeyID(); resultBuilder.addSignatureKeyId(keyId); + LOGGER.log(LEVEL, "Message contains OnePassSignature from " + Long.toHexString(keyId)); + // Find public key PGPPublicKey verificationKey = null; for (PGPPublicKeyRing publicKeyRing : verificationKeys) { - verificationKey = publicKeyRing.getPublicKey(signature.getKeyID()); + verificationKey = publicKeyRing.getPublicKey(keyId); + if (verificationKey != null) { + LOGGER.log(LEVEL, "Found respective public key " + Long.toHexString(keyId)); + break; + } } if (verificationKey != null) { diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/SignatureVerifyingInputStream.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/SignatureVerifyingInputStream.java index 6d81fc86..a56d2ae7 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/SignatureVerifyingInputStream.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/SignatureVerifyingInputStream.java @@ -20,6 +20,8 @@ import java.io.IOException; import java.io.InputStream; import java.security.SignatureException; import java.util.Map; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; @@ -32,11 +34,14 @@ import org.bouncycastle.openpgp.PGPSignatureList; public class SignatureVerifyingInputStream extends FilterInputStream { private static final Logger LOGGER = Logger.getLogger(SignatureVerifyingInputStream.class.getName()); + private static final Level LEVEL = Level.INFO; private final PGPObjectFactory objectFactory; private final Map onePassSignatures; private final PainlessResult.Builder resultBuilder; + private boolean validated = false; + protected SignatureVerifyingInputStream(InputStream inputStream, PGPObjectFactory objectFactory, Map onePassSignatures, @@ -45,6 +50,8 @@ public class SignatureVerifyingInputStream extends FilterInputStream { this.objectFactory = objectFactory; this.resultBuilder = resultBuilder; this.onePassSignatures = onePassSignatures; + + LOGGER.log(LEVEL, "Begin verifying OnePassSignatures"); } private void updateOnePassSignatures(byte data) { @@ -60,8 +67,16 @@ public class SignatureVerifyingInputStream extends FilterInputStream { } private void validateOnePassSignatures() throws IOException { + + if (validated) { + LOGGER.log(LEVEL, "Validated signatures already. Skip"); + return; + } + + validated = true; + if (onePassSignatures.isEmpty()) { - LOGGER.log(Level.FINE, "No One-Pass-Signatures found -> No validation."); + LOGGER.log(LEVEL, "No One-Pass-Signatures found -> No validation"); return; } @@ -77,19 +92,20 @@ public class SignatureVerifyingInputStream extends FilterInputStream { } if (signatureList == null || signatureList.isEmpty()) { - throw new IOException("Verification failed - No Signatures found!"); + throw new IOException("Verification failed - No Signatures found"); } for (PGPSignature signature : signatureList) { PGPOnePassSignature onePassSignature = onePassSignatures.get(signature.getKeyID()); if (onePassSignature == null) { + LOGGER.log(LEVEL, "Found Signature without respective OnePassSignature packet -> skip"); continue; } if (!onePassSignature.verify(signature)) { throw new SignatureException("Bad Signature of key " + signature.getKeyID()); } else { + LOGGER.log(LEVEL, "Verified signature of key " + Long.toHexString(signature.getKeyID())); resultBuilder.addVerifiedSignatureKeyId(signature.getKeyID()); - onePassSignatures.remove(signature.getKeyID()); } } } catch (PGPException | SignatureException e) { @@ -130,17 +146,17 @@ public class SignatureVerifyingInputStream extends FilterInputStream { @Override public long skip(long n) { - throw new UnsupportedOperationException("skip() is not supported."); + throw new UnsupportedOperationException("skip() is not supported"); } @Override public synchronized void mark(int readlimit) { - throw new UnsupportedOperationException("mark() not supported."); + throw new UnsupportedOperationException("mark() not supported"); } @Override public synchronized void reset() { - throw new UnsupportedOperationException("reset() is not supported."); + throw new UnsupportedOperationException("reset() is not supported"); } @Override