From a9f77ea10019ca3889198d4dc3f1253aa3e81073 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sun, 16 Oct 2022 18:15:31 +0200 Subject: [PATCH] Cleaning up and collect signature verifications --- .../MessageMetadata.java | 85 +++- .../OpenPgpMessageInputStream.java | 375 ++++++++---------- .../OpenPgpMessageInputStreamTest.java | 19 + 3 files changed, 262 insertions(+), 217 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/MessageMetadata.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/MessageMetadata.java index 87f6d7f6..bb2f5c76 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/MessageMetadata.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/MessageMetadata.java @@ -91,6 +91,62 @@ public class MessageMetadata { }; } + public @Nonnull List getVerifiedSignatures() { + List verifications = new ArrayList<>(); + Iterator> verificationsByLayer = getVerifiedSignaturesByLayer(); + while (verificationsByLayer.hasNext()) { + verifications.addAll(verificationsByLayer.next()); + } + return verifications; + } + + public @Nonnull Iterator> getVerifiedSignaturesByLayer() { + return new LayerIterator>(message) { + @Override + boolean matches(Nested layer) { + return layer instanceof Layer; + } + + @Override + boolean matches(Layer layer) { + return true; + } + + @Override + List getProperty(Layer last) { + return new ArrayList<>(last.getVerifiedSignatures()); + } + }; + } + + public @Nonnull List getRejectedSignatures() { + List rejected = new ArrayList<>(); + Iterator> rejectedByLayer = getRejectedSignaturesByLayer(); + while (rejectedByLayer.hasNext()) { + rejected.addAll(rejectedByLayer.next()); + } + return rejected; + } + + public @Nonnull Iterator> getRejectedSignaturesByLayer() { + return new LayerIterator>(message) { + @Override + boolean matches(Nested layer) { + return layer instanceof Layer; + } + + @Override + boolean matches(Layer layer) { + return true; + } + + @Override + List getProperty(Layer last) { + return new ArrayList<>(last.getFailedSignatures()); + } + }; + } + public String getFilename() { return findLiteralData().getFileName(); } @@ -132,6 +188,14 @@ public class MessageMetadata { public List getFailedSignatures() { return new ArrayList<>(failedSignatures); } + + void addVerifiedSignature(SignatureVerification signatureVerification) { + verifiedSignatures.add(signatureVerification); + } + + void addFailedSignature(SignatureVerification.Failure failure) { + failedSignatures.add(failure); + } } public interface Nested { @@ -223,9 +287,11 @@ public class MessageMetadata { private abstract static class LayerIterator implements Iterator { private Nested current; Layer last = null; + Message parent; LayerIterator(Message message) { super(); + this.parent = message; this.current = message.child; if (matches(current)) { last = (Layer) current; @@ -234,6 +300,9 @@ public class MessageMetadata { @Override public boolean hasNext() { + if (parent != null && matches(parent)) { + return true; + } if (last == null) { findNext(); } @@ -242,6 +311,11 @@ public class MessageMetadata { @Override public O next() { + if (parent != null && matches(parent)) { + O property = getProperty(parent); + parent = null; + return property; + } if (last == null) { findNext(); } @@ -263,7 +337,16 @@ public class MessageMetadata { } } - abstract boolean matches(Nested layer); + boolean matches(Nested layer) { + return false; + } + + boolean matches(Layer layer) { + if (layer instanceof Nested) { + return matches((Nested) layer); + } + return false; + } abstract O getProperty(Layer last); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java index 7e34d329..9e26dc20 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java @@ -34,7 +34,6 @@ import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory; import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider; import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; import org.bouncycastle.openpgp.operator.SessionKeyDataDecryptorFactory; -import org.bouncycastle.util.encoders.Hex; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.algorithm.EncryptionPurpose; @@ -81,16 +80,27 @@ public class OpenPgpMessageInputStream extends DecryptionStream { private final MessageMetadata.Layer metadata; private final Policy policy; - public OpenPgpMessageInputStream(InputStream inputStream, ConsumerOptions options) + public OpenPgpMessageInputStream(@Nonnull InputStream inputStream, + @Nonnull ConsumerOptions options) throws IOException, PGPException { - this(inputStream, options, new MessageMetadata.Message()); + this(inputStream, options, PGPainless.getPolicy()); } - OpenPgpMessageInputStream(InputStream inputStream, ConsumerOptions options, MessageMetadata.Layer metadata) + public OpenPgpMessageInputStream(@Nonnull InputStream inputStream, + @Nonnull ConsumerOptions options, + @Nonnull Policy policy) + throws PGPException, IOException { + this(inputStream, options, new MessageMetadata.Message(), policy); + } + + protected OpenPgpMessageInputStream(@Nonnull InputStream inputStream, + @Nonnull ConsumerOptions options, + @Nonnull MessageMetadata.Layer metadata, + @Nonnull Policy policy) throws PGPException, IOException { super(OpenPgpMetadata.getBuilder()); - this.policy = PGPainless.getPolicy(); + this.policy = policy; this.options = options; this.metadata = metadata; this.signatures = new Signatures(options); @@ -100,6 +110,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { this.signatures.addDetachedSignatures(options.getDetachedSignatures()); } + // tee out packet bytes for signature verification packetInputStream = new TeeBCPGInputStream(BCPGInputStream.wrap(inputStream), signatures); // *omnomnom* @@ -125,37 +136,30 @@ public class OpenPgpMessageInputStream extends DecryptionStream { private void consumePackets() throws IOException, PGPException { OpenPgpPacket nextPacket; - loop: while ((nextPacket = packetInputStream.nextPacketTag()) != null) { + + loop: // we break this when we go deeper. + while ((nextPacket = packetInputStream.nextPacketTag()) != null) { signatures.nextPacket(nextPacket); switch (nextPacket) { // Literal Data - the literal data content is the new input stream case LIT: - automaton.next(InputAlphabet.LiteralData); processLiteralData(); break loop; // Compressed Data - the content contains another OpenPGP message case COMP: - automaton.next(InputAlphabet.CompressedData); processCompressedData(); break loop; // One Pass Signature case OPS: - automaton.next(InputAlphabet.OnePassSignature); - PGPOnePassSignature onePassSignature = readOnePassSignature(); - signatures.addOnePassSignature(onePassSignature); + processOnePassSignature(); break; // Signature - either prepended to the message, or corresponding to a One Pass Signature case SIG: - // true if Signature corresponds to OnePassSignature - boolean isSigForOPS = automaton.peekStack() == StackAlphabet.ops; - automaton.next(InputAlphabet.Signature); - - processSignature(isSigForOPS); - + processSignature(); break; // Encrypted Data (ESKs and SED/SEIPD are parsed the same by BC) @@ -163,14 +167,13 @@ public class OpenPgpMessageInputStream extends DecryptionStream { case SKESK: case SED: case SEIPD: - automaton.next(InputAlphabet.EncryptedData); if (processEncryptedData()) { break loop; } throw new MissingDecryptionMethodException("No working decryption method found."); - // Marker Packets need to be skipped and ignored + // Marker Packets need to be skipped and ignored case MARKER: packetInputStream.readMarker(); break; @@ -200,36 +203,51 @@ public class OpenPgpMessageInputStream extends DecryptionStream { } } - private void processSignature(boolean isSigForOPS) throws PGPException, IOException { - PGPSignature signature = readSignature(); - if (isSigForOPS) { - signatures.leaveNesting(); // TODO: Only leave nesting if all OPSs are dealt with - signatures.addCorrespondingOnePassSignature(signature); - } else { - signatures.addPrependedSignature(signature); - } + private void processLiteralData() throws IOException { + automaton.next(InputAlphabet.LiteralData); + PGPLiteralData literalData = packetInputStream.readLiteralData(); + this.metadata.setChild(new MessageMetadata.LiteralData( + literalData.getFileName(), + literalData.getModificationTime(), + StreamEncoding.requireFromCode(literalData.getFormat()))); + nestedInputStream = literalData.getDataStream(); } private void processCompressedData() throws IOException, PGPException { + automaton.next(InputAlphabet.CompressedData); signatures.enterNesting(); PGPCompressedData compressedData = packetInputStream.readCompressedData(); MessageMetadata.CompressedData compressionLayer = new MessageMetadata.CompressedData( CompressionAlgorithm.fromId(compressedData.getAlgorithm())); InputStream decompressed = compressedData.getDataStream(); - nestedInputStream = new OpenPgpMessageInputStream(buffer(decompressed), options, compressionLayer); + nestedInputStream = new OpenPgpMessageInputStream(buffer(decompressed), options, compressionLayer, policy); } - private void processLiteralData() throws IOException { - PGPLiteralData literalData = packetInputStream.readLiteralData(); - this.metadata.setChild(new MessageMetadata.LiteralData(literalData.getFileName(), literalData.getModificationTime(), - StreamEncoding.requireFromCode(literalData.getFormat()))); - nestedInputStream = literalData.getDataStream(); + private void processOnePassSignature() throws PGPException, IOException { + automaton.next(InputAlphabet.OnePassSignature); + PGPOnePassSignature onePassSignature = packetInputStream.readOnePassSignature(); + signatures.addOnePassSignature(onePassSignature); + } + + private void processSignature() throws PGPException, IOException { + // true if Signature corresponds to OnePassSignature + boolean isSigForOPS = automaton.peekStack() == StackAlphabet.ops; + automaton.next(InputAlphabet.Signature); + PGPSignature signature = packetInputStream.readSignature(); + if (isSigForOPS) { + signatures.leaveNesting(); // TODO: Only leave nesting if all OPSs of the nesting layer are dealt with + signatures.addCorrespondingOnePassSignature(signature, metadata); + } else { + signatures.addPrependedSignature(signature); + } } private boolean processEncryptedData() throws IOException, PGPException { + automaton.next(InputAlphabet.EncryptedData); PGPEncryptedDataList encDataList = packetInputStream.readEncryptedDataList(); // TODO: Replace with !encDataList.isIntegrityProtected() + // once BC ships it if (!encDataList.get(0).isIntegrityProtected()) { throw new MessageNotIntegrityProtectedException(); } @@ -239,24 +257,25 @@ public class OpenPgpMessageInputStream extends DecryptionStream { // Try session key if (options.getSessionKey() != null) { SessionKey sessionKey = options.getSessionKey(); - if (!policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(sessionKey.getAlgorithm())) { - throw new UnacceptableAlgorithmException("Symmetric algorithm " + sessionKey.getAlgorithm() + " is not acceptable."); - } + + throwIfUnacceptable(sessionKey.getAlgorithm()); + SessionKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance() .getSessionKeyDataDecryptorFactory(sessionKey); - // TODO: Replace with encDataList.addSessionKeyDecryptionMethod(sessionKey) - PGPEncryptedData esk = esks.all().get(0); + MessageMetadata.EncryptedData encryptedData = new MessageMetadata.EncryptedData(sessionKey.getAlgorithm()); + try { - MessageMetadata.EncryptedData encryptedData = new MessageMetadata.EncryptedData(sessionKey.getAlgorithm()); + // TODO: Use BCs new API once shipped + PGPEncryptedData esk = esks.all().get(0); if (esk instanceof PGPPBEEncryptedData) { PGPPBEEncryptedData skesk = (PGPPBEEncryptedData) esk; InputStream decrypted = skesk.getDataStream(decryptorFactory); - nestedInputStream = new OpenPgpMessageInputStream(buffer(decrypted), options, encryptedData); + nestedInputStream = new OpenPgpMessageInputStream(buffer(decrypted), options, encryptedData, policy); return true; } else if (esk instanceof PGPPublicKeyEncryptedData) { PGPPublicKeyEncryptedData pkesk = (PGPPublicKeyEncryptedData) esk; InputStream decrypted = pkesk.getDataStream(decryptorFactory); - nestedInputStream = new OpenPgpMessageInputStream(buffer(decrypted), options, encryptedData); + nestedInputStream = new OpenPgpMessageInputStream(buffer(decrypted), options, encryptedData, policy); return true; } else { throw new RuntimeException("Unknown ESK class type: " + esk.getClass().getName()); @@ -268,19 +287,25 @@ public class OpenPgpMessageInputStream extends DecryptionStream { // Try passwords for (PGPPBEEncryptedData skesk : esks.skesks) { + SymmetricKeyAlgorithm kekAlgorithm = SymmetricKeyAlgorithm.requireFromId(skesk.getAlgorithm()); + throwIfUnacceptable(kekAlgorithm); for (Passphrase passphrase : options.getDecryptionPassphrases()) { - PBEDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance() - .getPBEDataDecryptorFactory(passphrase); - try { - InputStream decrypted = skesk.getDataStream(decryptorFactory); - MessageMetadata.EncryptedData encryptedData = new MessageMetadata.EncryptedData( - SymmetricKeyAlgorithm.requireFromId(skesk.getSymmetricAlgorithm(decryptorFactory))); - nestedInputStream = new OpenPgpMessageInputStream(buffer(decrypted), options, encryptedData); - return true; - } catch (PGPException e) { - // password mismatch? Try next password - } + PBEDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance() + .getPBEDataDecryptorFactory(passphrase); + try { + InputStream decrypted = skesk.getDataStream(decryptorFactory); + SymmetricKeyAlgorithm sessionKeyAlgorithm = SymmetricKeyAlgorithm.requireFromId( + skesk.getSymmetricAlgorithm(decryptorFactory)); + throwIfUnacceptable(sessionKeyAlgorithm); + MessageMetadata.EncryptedData encryptedData = new MessageMetadata.EncryptedData(sessionKeyAlgorithm); + nestedInputStream = new OpenPgpMessageInputStream(buffer(decrypted), options, encryptedData, policy); + return true; + } catch (UnacceptableAlgorithmException e) { + throw e; + } catch (PGPException e) { + // Password mismatch? + } } } @@ -299,19 +324,17 @@ public class OpenPgpMessageInputStream extends DecryptionStream { .getPublicKeyDataDecryptorFactory(privateKey); try { SymmetricKeyAlgorithm symAlg = SymmetricKeyAlgorithm.requireFromId(pkesk.getSymmetricAlgorithm(decryptorFactory)); - if (!policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(symAlg)) { - throw new UnacceptableAlgorithmException("Symmetric-key algorithm " + symAlg + " is not acceptable."); - } + throwIfUnacceptable(symAlg); InputStream decrypted = pkesk.getDataStream(decryptorFactory); MessageMetadata.EncryptedData encryptedData = new MessageMetadata.EncryptedData( SymmetricKeyAlgorithm.requireFromId(pkesk.getSymmetricAlgorithm(decryptorFactory))); - nestedInputStream = new OpenPgpMessageInputStream(buffer(decrypted), options, encryptedData); + nestedInputStream = new OpenPgpMessageInputStream(buffer(decrypted), options, encryptedData, policy); return true; + } catch (UnacceptableAlgorithmException e) { + throw e; } catch (PGPException e) { - if (e instanceof UnacceptableAlgorithmException) { - throw e; - } + } } @@ -327,7 +350,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { InputStream decrypted = pkesk.getDataStream(decryptorFactory); MessageMetadata.EncryptedData encryptedData = new MessageMetadata.EncryptedData( SymmetricKeyAlgorithm.requireFromId(pkesk.getSymmetricAlgorithm(decryptorFactory))); - nestedInputStream = new OpenPgpMessageInputStream(buffer(decrypted), options, encryptedData); + nestedInputStream = new OpenPgpMessageInputStream(buffer(decrypted), options, encryptedData, policy); return true; } catch (PGPException e) { // hm :/ @@ -339,6 +362,13 @@ public class OpenPgpMessageInputStream extends DecryptionStream { return false; } + private void throwIfUnacceptable(SymmetricKeyAlgorithm algorithm) + throws UnacceptableAlgorithmException { + if (!policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(algorithm)) { + throw new UnacceptableAlgorithmException("Symmetric-Key algorithm " + algorithm + " is not acceptable for message decryption."); + } + } + private static InputStream buffer(InputStream inputStream) { return new BufferedInputStream(inputStream); } @@ -370,16 +400,6 @@ public class OpenPgpMessageInputStream extends DecryptionStream { return null; } - private PGPOnePassSignature readOnePassSignature() - throws PGPException, IOException { - return packetInputStream.readOnePassSignature(); - } - - private PGPSignature readSignature() - throws PGPException, IOException { - return packetInputStream.readSignature(); - } - @Override public int read() throws IOException { if (nestedInputStream == null) { @@ -407,7 +427,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { } catch (PGPException e) { throw new RuntimeException(e); } - signatures.finish(); + signatures.finish(metadata); } return r; } @@ -435,7 +455,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { } catch (PGPException e) { throw new RuntimeException(e); } - signatures.finish(); + signatures.finish(metadata); } return r; } @@ -517,11 +537,11 @@ public class OpenPgpMessageInputStream extends DecryptionStream { // Furthermore, For 'OPS COMP(LIT("Foo")) SIG', the signature is updated with "Foo". CHAOS!!! private static final class Signatures extends OutputStream { final ConsumerOptions options; - final List detachedSignatures; - final List prependedSignatures; - final List onePassSignatures; - final Stack> opsUpdateStack; - List literalOPS = new ArrayList<>(); + final List detachedSignatures; + final List prependedSignatures; + final List onePassSignatures; + final Stack> opsUpdateStack; + List literalOPS = new ArrayList<>(); final List correspondingSignatures; boolean isLiteral = true; @@ -546,19 +566,19 @@ public class OpenPgpMessageInputStream extends DecryptionStream { long keyId = SignatureUtils.determineIssuerKeyId(signature); PGPPublicKeyRing certificate = findCertificate(keyId); initialize(signature, certificate, keyId); - this.detachedSignatures.add(new SIG(signature, certificate, keyId)); + this.detachedSignatures.add(new DetachedOrPrependedSignature(signature, certificate, keyId)); } void addPrependedSignature(PGPSignature signature) { long keyId = SignatureUtils.determineIssuerKeyId(signature); PGPPublicKeyRing certificate = findCertificate(keyId); initialize(signature, certificate, keyId); - this.prependedSignatures.add(new SIG(signature, certificate, keyId)); + this.prependedSignatures.add(new DetachedOrPrependedSignature(signature, certificate, keyId)); } void addOnePassSignature(PGPOnePassSignature signature) { PGPPublicKeyRing certificate = findCertificate(signature.getKeyID()); - OPS ops = new OPS(signature, certificate, signature.getKeyID()); + OnePassSignature ops = new OnePassSignature(signature, certificate, signature.getKeyID()); ops.init(certificate); onePassSignatures.add(ops); @@ -568,9 +588,9 @@ public class OpenPgpMessageInputStream extends DecryptionStream { } } - void addCorrespondingOnePassSignature(PGPSignature signature) { + void addCorrespondingOnePassSignature(PGPSignature signature, MessageMetadata.Layer layer) { for (int i = onePassSignatures.size() - 1; i >= 0; i--) { - OPS onePassSignature = onePassSignatures.get(i); + OnePassSignature onePassSignature = onePassSignatures.get(i); if (onePassSignature.opSignature.getKeyID() != signature.getKeyID()) { continue; } @@ -579,8 +599,14 @@ public class OpenPgpMessageInputStream extends DecryptionStream { } boolean verified = onePassSignature.verify(signature); - log("One-Pass-Signature by " + Long.toHexString(onePassSignature.opSignature.getKeyID()) + " is " + (verified ? "verified" : "unverified")); - log(onePassSignature.toString()); + SignatureVerification verification = new SignatureVerification(signature, + new SubkeyIdentifier(onePassSignature.certificate, onePassSignature.keyId)); + if (verified) { + layer.addVerifiedSignature(verification); + } else { + layer.addFailedSignature(new SignatureVerification.Failure(verification, + new SignatureValidationException("Incorrect Signature."))); + } break; } } @@ -597,11 +623,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { opsUpdateStack.pop(); } - private static void initialize(PGPSignature signature, PGPPublicKeyRing certificate, long keyId) { - if (certificate == null) { - // SHIT - return; - } + private static void initialize(@Nonnull PGPSignature signature, @Nonnull PGPPublicKeyRing certificate, long keyId) { PGPContentVerifierBuilderProvider verifierProvider = ImplementationFactory.getInstance() .getPGPContentVerifierBuilderProvider(); try { @@ -611,10 +633,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { } } - private static void initialize(PGPOnePassSignature ops, PGPPublicKeyRing certificate) { - if (certificate == null) { - return; - } + private static void initialize(@Nonnull PGPOnePassSignature ops, @Nonnull PGPPublicKeyRing certificate) { PGPContentVerifierBuilderProvider verifierProvider = ImplementationFactory.getInstance() .getPGPContentVerifierBuilderProvider(); try { @@ -635,76 +654,74 @@ public class OpenPgpMessageInputStream extends DecryptionStream { } public void updateLiteral(byte b) { - for (OPS ops : literalOPS) { + for (OnePassSignature ops : literalOPS) { ops.update(b); } - for (SIG detached : detachedSignatures) { + for (DetachedOrPrependedSignature detached : detachedSignatures) { detached.update(b); } + + for (DetachedOrPrependedSignature prepended : prependedSignatures) { + prepended.update(b); + } } public void updateLiteral(byte[] b, int off, int len) { - for (OPS ops : literalOPS) { + for (OnePassSignature ops : literalOPS) { ops.update(b, off, len); } - for (SIG detached : detachedSignatures) { + for (DetachedOrPrependedSignature detached : detachedSignatures) { detached.update(b, off, len); } + + for (DetachedOrPrependedSignature prepended : prependedSignatures) { + prepended.update(b, off, len); + } } public void updatePacket(byte b) { - for (SIG detached : detachedSignatures) { - detached.update(b); - } - - for (SIG prepended : prependedSignatures) { - prepended.update(b); - } - for (int i = opsUpdateStack.size() - 1; i >= 0; i--) { - List nestedOPSs = opsUpdateStack.get(i); - for (OPS ops : nestedOPSs) { + List nestedOPSs = opsUpdateStack.get(i); + for (OnePassSignature ops : nestedOPSs) { ops.update(b); } } } public void updatePacket(byte[] buf, int off, int len) { - for (SIG detached : detachedSignatures) { - detached.update(buf, off, len); - } - - for (SIG prepended : prependedSignatures) { - prepended.update(buf, off, len); - } - for (int i = opsUpdateStack.size() - 1; i >= 0; i--) { - List nestedOPSs = opsUpdateStack.get(i); - for (OPS ops : nestedOPSs) { + List nestedOPSs = opsUpdateStack.get(i); + for (OnePassSignature ops : nestedOPSs) { ops.update(buf, off, len); } } } - public void finish() { - for (SIG detached : detachedSignatures) { + public void finish(MessageMetadata.Layer layer) { + for (DetachedOrPrependedSignature detached : detachedSignatures) { boolean verified = detached.verify(); + SignatureVerification verification = new SignatureVerification( + detached.signature, new SubkeyIdentifier(detached.certificate, detached.keyId)); if (verified) { - this.verified.add(detached.signature); + layer.addVerifiedSignature(verification); + } else { + layer.addFailedSignature(new SignatureVerification.Failure( + verification, new SignatureValidationException("Incorrect Signature."))); } - log("Detached Signature by " + Long.toHexString(detached.signature.getKeyID()) + " is " + (verified ? "verified" : "unverified")); - log(detached.toString()); } - for (SIG prepended : prependedSignatures) { + for (DetachedOrPrependedSignature prepended : prependedSignatures) { boolean verified = prepended.verify(); + SignatureVerification verification = new SignatureVerification( + prepended.signature, new SubkeyIdentifier(prepended.certificate, prepended.keyId)); if (verified) { - this.verified.add(prepended.signature); + layer.addVerifiedSignature(verification); + } else { + layer.addFailedSignature(new SignatureVerification.Failure( + verification, new SignatureValidationException("Incorrect Signature."))); } - log("Prepended Signature by " + Long.toHexString(prepended.signature.getKeyID()) + " is " + (verified ? "verified" : "unverified")); - log(prepended.toString()); } } @@ -729,7 +746,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { } } - static class SIG { + static class DetachedOrPrependedSignature { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); PGPSignature signature; PGPPublicKeyRing certificate; @@ -737,7 +754,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { boolean finished; boolean valid; - public SIG(PGPSignature signature, PGPPublicKeyRing certificate, long keyId) { + public DetachedOrPrependedSignature(PGPSignature signature, PGPPublicKeyRing certificate, long keyId) { this.signature = signature; this.certificate = certificate; this.keyId = keyId; @@ -762,8 +779,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { public void update(byte b) { if (finished) { - log("Updating finished sig!"); - return; + throw new IllegalStateException("Already finished."); } signature.update(b); bytes.write(b); @@ -771,52 +787,14 @@ public class OpenPgpMessageInputStream extends DecryptionStream { public void update(byte[] bytes, int off, int len) { if (finished) { - log("Updating finished sig!"); - return; + throw new IllegalStateException("Already finished."); } signature.update(bytes, off, len); this.bytes.write(bytes, off, len); } - - @Override - public String toString() { - String OPS = "c40d03000a01fbfcc82a015e733001"; - String LIT_H = "cb28620000000000"; - String LIT = "656e637279707420e28898207369676e20e28898207369676e20e28898207369676e"; - String SIG1 = "c2c10400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267b0409ed8ea96dac66447bdff5b7b60c9f80a0ab91d257029153dc3b6d8c27b98162104d1a66e1a23b182c9980f788cfbfcc82a015e7330000029640c00846b5096d92474fd446cc7edaf9f14572cab93a80e12384c1e829f95debc6e8373c2ce5402be53dc1a18cf92a0ed909e0fb38855713ef8ffb13502ffac7c830fa254cc1aa6c666a97b0cc3bc176538f6913d3b8e8981a65cc42df10e0f39e4d0a06dfe961437b59a71892f4fca1116aed15123ea0d86a7b2ce47dd9d3ef22d920631bc011e82babe03ad5d72b3ba7f95bf646f20ccf6f7a4d95de37397c76c7d53741458e51ab6074007f61181c7b88b7c98f5b7510c8dfa3be01f4841501679478b15c5249d928e2a10d15ec63efa1500b994d5bfb32ffb174a976116930eb97a111e6dfd4c5e43e04a5d76ba74806a62fda63a8c3f53f6eebaf852892340e81dd08bbf348454a2cf525aeb512cf33aeeee78465ee4c442e41cc45ac4e3bb0c3333677aa60332ee7f464d9020f8554b82d619872477cca18d8431888f4ae8abe5894e9720f759c410cd7991db12703dc147040dd0d3758223e0b75de6ceae49c1a0c2c45efedeb7114ae785cc886afdc45c82172e4476e1ab5b86dc4314dd76"; - String SIG1f = "c2c13b0400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267b0409ed8ea96dac66447bdff5b7b60c9f80a0ab91d257029153dc3b6d8c27b98162104d1a66e1a23b182c9980f788cfbfcc82a015e7330000029640c00846b5096d92474fd446cc7edaf9f14572cab93a80e12384c1e829f95debc6e8373c2ce5402be53dc1a18cf92a0ed909e0fb38855713ef8ffb13502ffac7c830fa254cc1aa6c666a97b0cc3bc176538f6913d3b8e8981a65cc42df10e0f39e4d0a06dfe961437b59a71892f4fca1116aed15123ea0d86a7b2ce47dd9d3ef22d920631bc011e82babe03ad5d72b3ba7f95bf646f20ccf6f7a4d95de37397c76c7d53741458e51ab6074007f61181c7b88b7c98f5b7510c8dfa3be01f4841501679478b15c5249d928e2a10d15ec63efa1500b994d5bfb32ffb174a976116930eb97a111e6dfd4c5e43e04a5d76ba74806a62fda63a8c3f53f6eebaf852892340e81dd08bbf348454a2cf525aeb512cf33aeeee78465ee4c442e41cc45ac4e3bb0c3333677aa60332ee7f464d9020f8554b82d619872477cca18d8431888f4ae8abe5894e9720f759c410cd7991db12703dc147040dd0d3758223e0b75de6ceae49c1a0c2c45efedeb7114ae785cc886afdc45c82172e4476e1ab5b86dc4314dd76"; - String SIG2 = "c2c10400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267a4d9c117dc7ba3a7e9270856f128d2ab271743eac3cb5750b22a89bd5fd60753162104d1a66e1a23b182c9980f788cfbfcc82a015e73300000b8400bff796c20fa8b25ff7a42686338e06417a2966e85a0fc2723c928bef6cd19d34cf5e7d55ada33080613012dadb79e0278e59d9e7ed7d2d6102912a5f768c2e75b60099225c3d8bfe0c123240188b80dbee89b9b3bd5b13ccc662abc37e2129b6968adac9aba43aa778c0fe4fe337591ee87a96a29a013debc83555293c877144fc676aa1b03782c501949521a320adf6ad96c4f2e036b52a18369c637fdc49033696a84d03a69580b953187fce5aca6fb26fc8815da9f3b513bfe8e304f33ecb4b521aeb7d09c4a284ea66123bd0d6a358b2526d762ca110e1f7f20b3038d774b64d5cfd34e2213765828359d7afc5bf24d5270e99d80c3c1568fa01624b6ea1e9ce4e6890ce9bacf6611a45d41e2671f68f5b096446bf08d27ce75608425b2e3ab92146229ad1fcd8224aca5b5f73960506e7df07bfbf3664348e8ecbfb2eb467b9cfe412cb377a6ee2eb5fd11be9cf9208fe9a74c296f52cfa02a1eb0519ad9a8349bf6ccd6495feb7e391451bf96e08a0798883dee5974e47cbf3b51f111b6d3"; - String SIG2f = "c2c13b0400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267a4d9c117dc7ba3a7e9270856f128d2ab271743eac3cb5750b22a89bd5fd60753162104d1a66e1a23b182c9980f788cfbfcc82a015e73300000b8400bff796c20fa8b25ff7a42686338e06417a2966e85a0fc2723c928bef6cd19d34cf5e7d55ada33080613012dadb79e0278e59d9e7ed7d2d6102912a5f768c2e75b60099225c3d8bfe0c123240188b80dbee89b9b3bd5b13ccc662abc37e2129b6968adac9aba43aa778c0fe4fe337591ee87a96a29a013debc83555293c877144fc676aa1b03782c501949521a320adf6ad96c4f2e036b52a18369c637fdc49033696a84d03a69580b953187fce5aca6fb26fc8815da9f3b513bfe8e304f33ecb4b521aeb7d09c4a284ea66123bd0d6a358b2526d762ca110e1f7f20b3038d774b64d5cfd34e2213765828359d7afc5bf24d5270e99d80c3c1568fa01624b6ea1e9ce4e6890ce9bacf6611a45d41e2671f68f5b096446bf08d27ce75608425b2e3ab92146229ad1fcd8224aca5b5f73960506e7df07bfbf3664348e8ecbfb2eb467b9cfe412cb377a6ee2eb5fd11be9cf9208fe9a74c296f52cfa02a1eb0519ad9a8349bf6ccd6495feb7e391451bf96e08a0798883dee5974e47cbf3b51f111b6d3"; - String out = ""; - - String hex = Hex.toHexString(bytes.toByteArray()); - while (hex.contains(OPS)) { - hex = hex.replace(OPS, "[OPS]"); - } - while (hex.contains(LIT_H)) { - hex = hex.replace(LIT_H, "[LIT]"); - } - while (hex.contains(LIT)) { - hex = hex.replace(LIT, ""); - } - while (hex.contains(SIG1)) { - hex = hex.replace(SIG1, "[SIG1]"); - } - while (hex.contains(SIG1f)) { - hex = hex.replace(SIG1f, "[SIG1f]"); - } - while (hex.contains(SIG2)) { - hex = hex.replace(SIG2, "[SIG2]"); - } - while (hex.contains(SIG2f)) { - hex = hex.replace(SIG2f, "[SIG2f]"); - } - - return out + hex; - } } - static class OPS { + static class OnePassSignature { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); PGPOnePassSignature opSignature; PGPSignature signature; @@ -825,7 +803,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { boolean finished; boolean valid; - public OPS(PGPOnePassSignature signature, PGPPublicKeyRing certificate, long keyId) { + public OnePassSignature(PGPOnePassSignature signature, PGPPublicKeyRing certificate, long keyId) { this.opSignature = signature; this.certificate = certificate; this.keyId = keyId; @@ -836,6 +814,10 @@ public class OpenPgpMessageInputStream extends DecryptionStream { } public boolean verify(PGPSignature signature) { + if (finished) { + throw new IllegalStateException("Already finished."); + } + if (this.opSignature.getKeyID() != signature.getKeyID()) { // nope return false; @@ -852,8 +834,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { public void update(byte b) { if (finished) { - log("Updating finished sig!"); - return; + throw new IllegalStateException("Already finished."); } opSignature.update(b); bytes.write(b); @@ -861,49 +842,11 @@ public class OpenPgpMessageInputStream extends DecryptionStream { public void update(byte[] bytes, int off, int len) { if (finished) { - log("Updating finished sig!"); - return; + throw new IllegalStateException("Already finished."); } opSignature.update(bytes, off, len); this.bytes.write(bytes, off, len); } - - @Override - public String toString() { - String OPS = "c40d03000a01fbfcc82a015e733001"; - String LIT_H = "cb28620000000000"; - String LIT = "656e637279707420e28898207369676e20e28898207369676e20e28898207369676e"; - String SIG1 = "c2c10400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267b0409ed8ea96dac66447bdff5b7b60c9f80a0ab91d257029153dc3b6d8c27b98162104d1a66e1a23b182c9980f788cfbfcc82a015e7330000029640c00846b5096d92474fd446cc7edaf9f14572cab93a80e12384c1e829f95debc6e8373c2ce5402be53dc1a18cf92a0ed909e0fb38855713ef8ffb13502ffac7c830fa254cc1aa6c666a97b0cc3bc176538f6913d3b8e8981a65cc42df10e0f39e4d0a06dfe961437b59a71892f4fca1116aed15123ea0d86a7b2ce47dd9d3ef22d920631bc011e82babe03ad5d72b3ba7f95bf646f20ccf6f7a4d95de37397c76c7d53741458e51ab6074007f61181c7b88b7c98f5b7510c8dfa3be01f4841501679478b15c5249d928e2a10d15ec63efa1500b994d5bfb32ffb174a976116930eb97a111e6dfd4c5e43e04a5d76ba74806a62fda63a8c3f53f6eebaf852892340e81dd08bbf348454a2cf525aeb512cf33aeeee78465ee4c442e41cc45ac4e3bb0c3333677aa60332ee7f464d9020f8554b82d619872477cca18d8431888f4ae8abe5894e9720f759c410cd7991db12703dc147040dd0d3758223e0b75de6ceae49c1a0c2c45efedeb7114ae785cc886afdc45c82172e4476e1ab5b86dc4314dd76"; - String SIG1f = "c2c13b0400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267b0409ed8ea96dac66447bdff5b7b60c9f80a0ab91d257029153dc3b6d8c27b98162104d1a66e1a23b182c9980f788cfbfcc82a015e7330000029640c00846b5096d92474fd446cc7edaf9f14572cab93a80e12384c1e829f95debc6e8373c2ce5402be53dc1a18cf92a0ed909e0fb38855713ef8ffb13502ffac7c830fa254cc1aa6c666a97b0cc3bc176538f6913d3b8e8981a65cc42df10e0f39e4d0a06dfe961437b59a71892f4fca1116aed15123ea0d86a7b2ce47dd9d3ef22d920631bc011e82babe03ad5d72b3ba7f95bf646f20ccf6f7a4d95de37397c76c7d53741458e51ab6074007f61181c7b88b7c98f5b7510c8dfa3be01f4841501679478b15c5249d928e2a10d15ec63efa1500b994d5bfb32ffb174a976116930eb97a111e6dfd4c5e43e04a5d76ba74806a62fda63a8c3f53f6eebaf852892340e81dd08bbf348454a2cf525aeb512cf33aeeee78465ee4c442e41cc45ac4e3bb0c3333677aa60332ee7f464d9020f8554b82d619872477cca18d8431888f4ae8abe5894e9720f759c410cd7991db12703dc147040dd0d3758223e0b75de6ceae49c1a0c2c45efedeb7114ae785cc886afdc45c82172e4476e1ab5b86dc4314dd76"; - String SIG2 = "c2c10400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267a4d9c117dc7ba3a7e9270856f128d2ab271743eac3cb5750b22a89bd5fd60753162104d1a66e1a23b182c9980f788cfbfcc82a015e73300000b8400bff796c20fa8b25ff7a42686338e06417a2966e85a0fc2723c928bef6cd19d34cf5e7d55ada33080613012dadb79e0278e59d9e7ed7d2d6102912a5f768c2e75b60099225c3d8bfe0c123240188b80dbee89b9b3bd5b13ccc662abc37e2129b6968adac9aba43aa778c0fe4fe337591ee87a96a29a013debc83555293c877144fc676aa1b03782c501949521a320adf6ad96c4f2e036b52a18369c637fdc49033696a84d03a69580b953187fce5aca6fb26fc8815da9f3b513bfe8e304f33ecb4b521aeb7d09c4a284ea66123bd0d6a358b2526d762ca110e1f7f20b3038d774b64d5cfd34e2213765828359d7afc5bf24d5270e99d80c3c1568fa01624b6ea1e9ce4e6890ce9bacf6611a45d41e2671f68f5b096446bf08d27ce75608425b2e3ab92146229ad1fcd8224aca5b5f73960506e7df07bfbf3664348e8ecbfb2eb467b9cfe412cb377a6ee2eb5fd11be9cf9208fe9a74c296f52cfa02a1eb0519ad9a8349bf6ccd6495feb7e391451bf96e08a0798883dee5974e47cbf3b51f111b6d3"; - String SIG2f = "c2c13b0400010a006f058262c806350910fbfcc82a015e7330471400000000001e002073616c74406e6f746174696f6e732e736571756f69612d7067702e6f7267a4d9c117dc7ba3a7e9270856f128d2ab271743eac3cb5750b22a89bd5fd60753162104d1a66e1a23b182c9980f788cfbfcc82a015e73300000b8400bff796c20fa8b25ff7a42686338e06417a2966e85a0fc2723c928bef6cd19d34cf5e7d55ada33080613012dadb79e0278e59d9e7ed7d2d6102912a5f768c2e75b60099225c3d8bfe0c123240188b80dbee89b9b3bd5b13ccc662abc37e2129b6968adac9aba43aa778c0fe4fe337591ee87a96a29a013debc83555293c877144fc676aa1b03782c501949521a320adf6ad96c4f2e036b52a18369c637fdc49033696a84d03a69580b953187fce5aca6fb26fc8815da9f3b513bfe8e304f33ecb4b521aeb7d09c4a284ea66123bd0d6a358b2526d762ca110e1f7f20b3038d774b64d5cfd34e2213765828359d7afc5bf24d5270e99d80c3c1568fa01624b6ea1e9ce4e6890ce9bacf6611a45d41e2671f68f5b096446bf08d27ce75608425b2e3ab92146229ad1fcd8224aca5b5f73960506e7df07bfbf3664348e8ecbfb2eb467b9cfe412cb377a6ee2eb5fd11be9cf9208fe9a74c296f52cfa02a1eb0519ad9a8349bf6ccd6495feb7e391451bf96e08a0798883dee5974e47cbf3b51f111b6d3"; - String out = "last=" + opSignature.isContaining() + "\n"; - - String hex = Hex.toHexString(bytes.toByteArray()); - while (hex.contains(OPS)) { - hex = hex.replace(OPS, "[OPS]"); - } - while (hex.contains(LIT_H)) { - hex = hex.replace(LIT_H, "[LIT]"); - } - while (hex.contains(LIT)) { - hex = hex.replace(LIT, ""); - } - while (hex.contains(SIG1)) { - hex = hex.replace(SIG1, "[SIG1]"); - } - while (hex.contains(SIG1f)) { - hex = hex.replace(SIG1f, "[SIG1f]"); - } - while (hex.contains(SIG2)) { - hex = hex.replace(SIG2, "[SIG2]"); - } - while (hex.contains(SIG2f)) { - hex = hex.replace(SIG2f, "[SIG2f]"); - } - - return out + hex; - } } } @@ -916,7 +859,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { resultBuilder.setFileEncoding(m.getFormat()); resultBuilder.setSessionKey(m.getSessionKey()); - for (Signatures.OPS ops : signatures.onePassSignatures) { + for (Signatures.OnePassSignature ops : signatures.onePassSignatures) { if (!ops.finished) { continue; } @@ -930,7 +873,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { } } - for (Signatures.SIG prep : signatures.prependedSignatures) { + for (Signatures.DetachedOrPrependedSignature prep : signatures.prependedSignatures) { if (!prep.finished) { continue; } @@ -944,7 +887,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { } } - for (Signatures.SIG det : signatures.detachedSignatures) { + for (Signatures.DetachedOrPrependedSignature det : signatures.detachedSignatures) { if (!det.finished) { continue; } @@ -964,7 +907,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { static void log(String message) { LOGGER.debug(message); // CHECKSTYLE:OFF - // System.out.println(message); + System.out.println(message); // CHECKSTYLE:ON } diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStreamTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStreamTest.java index 1955eebe..87ae27f8 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStreamTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStreamTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -331,6 +332,8 @@ public class OpenPgpMessageInputStreamTest { assertEquals("", metadata.getFilename()); JUtils.assertDateEquals(new Date(0L), metadata.getModificationDate()); assertEquals(StreamEncoding.BINARY, metadata.getFormat()); + assertTrue(metadata.getVerifiedSignatures().isEmpty()); + assertTrue(metadata.getRejectedSignatures().isEmpty()); } @ParameterizedTest(name = "Process LIT LIT using {0}") @@ -349,6 +352,8 @@ public class OpenPgpMessageInputStreamTest { assertEquals(PLAINTEXT, plain); MessageMetadata metadata = result.getB(); assertEquals(CompressionAlgorithm.ZIP, metadata.getCompressionAlgorithm()); + assertTrue(metadata.getVerifiedSignatures().isEmpty()); + assertTrue(metadata.getRejectedSignatures().isEmpty()); } @ParameterizedTest(name = "Process COMP using {0}") @@ -372,6 +377,8 @@ public class OpenPgpMessageInputStreamTest { assertEquals(CompressionAlgorithm.BZIP2, compressionAlgorithms.next()); assertFalse(compressionAlgorithms.hasNext()); assertNull(metadata.getEncryptionAlgorithm()); + assertTrue(metadata.getVerifiedSignatures().isEmpty()); + assertTrue(metadata.getRejectedSignatures().isEmpty()); } @ParameterizedTest(name = "Process SIG COMP(LIT) using {0}") @@ -388,6 +395,8 @@ public class OpenPgpMessageInputStreamTest { MessageMetadata metadata = result.getB(); assertEquals(CompressionAlgorithm.ZIP, metadata.getCompressionAlgorithm()); assertNull(metadata.getEncryptionAlgorithm()); + assertFalse(metadata.getVerifiedSignatures().isEmpty()); + assertTrue(metadata.getRejectedSignatures().isEmpty()); } @ParameterizedTest(name = "Process SENC(LIT) using {0}") @@ -401,6 +410,8 @@ public class OpenPgpMessageInputStreamTest { MessageMetadata metadata = result.getB(); assertNull(metadata.getCompressionAlgorithm()); assertEquals(SymmetricKeyAlgorithm.AES_256, metadata.getEncryptionAlgorithm()); + assertTrue(metadata.getVerifiedSignatures().isEmpty()); + assertTrue(metadata.getRejectedSignatures().isEmpty()); } @ParameterizedTest(name = "Process PENC(COMP(LIT)) using {0}") @@ -415,6 +426,8 @@ public class OpenPgpMessageInputStreamTest { MessageMetadata metadata = result.getB(); assertEquals(CompressionAlgorithm.ZLIB, metadata.getCompressionAlgorithm()); assertEquals(SymmetricKeyAlgorithm.AES_256, metadata.getEncryptionAlgorithm()); + assertTrue(metadata.getVerifiedSignatures().isEmpty()); + assertTrue(metadata.getRejectedSignatures().isEmpty()); } @ParameterizedTest(name = "Process OPS LIT SIG using {0}") @@ -429,6 +442,8 @@ public class OpenPgpMessageInputStreamTest { MessageMetadata metadata = result.getB(); assertNull(metadata.getEncryptionAlgorithm()); assertNull(metadata.getCompressionAlgorithm()); + assertFalse(metadata.getVerifiedSignatures().isEmpty()); + assertTrue(metadata.getRejectedSignatures().isEmpty()); } String BOB_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + @@ -564,6 +579,8 @@ public class OpenPgpMessageInputStreamTest { MessageMetadata metadata = result.getB(); assertEquals(SymmetricKeyAlgorithm.AES_256, metadata.getEncryptionAlgorithm()); assertNull(metadata.getCompressionAlgorithm()); + assertFalse(metadata.getVerifiedSignatures().isEmpty()); + assertTrue(metadata.getRejectedSignatures().isEmpty()); } @ParameterizedTest(name = "Process PENC(OPS OPS OPS LIT SIG SIG SIG) using {0}") @@ -627,6 +644,8 @@ public class OpenPgpMessageInputStreamTest { MessageMetadata metadata = result.getB(); assertEquals(SymmetricKeyAlgorithm.AES_256, metadata.getEncryptionAlgorithm()); assertNull(metadata.getCompressionAlgorithm()); + assertFalse(metadata.getVerifiedSignatures().isEmpty()); + assertTrue(metadata.getRejectedSignatures().isEmpty()); } private static Tuple processReadBuffered(String armoredMessage, ConsumerOptions options)