From 6a90c4303ef7fdeab544a7d42a1f96f472ae249d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sun, 4 Jul 2021 13:08:24 +0200 Subject: [PATCH] OpenPgpMetadat: identify verified sigs by SubkeyIdentifier --- .../DecryptionStreamFactory.java | 4 +- .../OpenPgpMetadata.java | 39 ++++++++++--------- .../org/pgpainless/key/SubkeyIdentifier.java | 5 +++ .../signature/OnePassSignature.java | 5 ++- .../DecryptAndVerifyMessageTest.java | 5 ++- .../DecryptHiddenRecipientMessage.java | 4 +- .../VerifyWithMissingPublicKeyCallback.java | 3 +- .../signature/IgnoreMarkerPackets.java | 4 +- .../org/pgpainless/sop/commands/Decrypt.java | 17 +++----- .../org/pgpainless/sop/commands/Verify.java | 22 +++++------ 10 files changed, 55 insertions(+), 53 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java index f863ccf9..15695a6d 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java @@ -243,7 +243,7 @@ public final class DecryptionStreamFactory { // Watch out! This assignment is possibly done multiple times. encryptedSessionKey = publicKeyEncryptedData; decryptionKey = UnlockSecretKey.unlockSecretKey(secretKey, options.getSecretKeyProtector(decryptionKeyRing)); - resultBuilder.setDecryptionFingerprint(new OpenPgpV4Fingerprint(secretKey)); + resultBuilder.setDecryptionKey(new SubkeyIdentifier(decryptionKeyRing, decryptionKey.getKeyID())); } } @@ -265,7 +265,7 @@ public final class DecryptionStreamFactory { publicKeyEncryptedData.getSymmetricAlgorithm(decryptorFactory); // will only succeed if we have the right secret key LOGGER.log(LEVEL, "Found correct key " + Long.toHexString(key.getKeyID()) + " for hidden recipient decryption."); decryptionKey = privateKey; - resultBuilder.setDecryptionFingerprint(new OpenPgpV4Fingerprint(key)); + resultBuilder.setDecryptionKey(new SubkeyIdentifier(ring, decryptionKey.getKeyID())); encryptedSessionKey = publicKeyEncryptedData; break outerloop; } catch (PGPException | ClassCastException e) { diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMetadata.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMetadata.java index 429e82b7..bff32fdf 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMetadata.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMetadata.java @@ -32,13 +32,14 @@ import org.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.algorithm.StreamEncoding; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.key.OpenPgpV4Fingerprint; +import org.pgpainless.key.SubkeyIdentifier; import org.pgpainless.signature.DetachedSignature; import org.pgpainless.signature.OnePassSignature; public class OpenPgpMetadata { private final Set recipientKeyIds; - private final OpenPgpV4Fingerprint decryptionFingerprint; + private final SubkeyIdentifier decryptionKey; private final List onePassSignatures; private final List detachedSignatures; private final SymmetricKeyAlgorithm symmetricKeyAlgorithm; @@ -46,7 +47,7 @@ public class OpenPgpMetadata { private final FileInfo fileInfo; public OpenPgpMetadata(Set recipientKeyIds, - OpenPgpV4Fingerprint decryptionFingerprint, + SubkeyIdentifier decryptionKey, SymmetricKeyAlgorithm symmetricKeyAlgorithm, CompressionAlgorithm algorithm, List onePassSignatures, @@ -54,7 +55,7 @@ public class OpenPgpMetadata { FileInfo fileInfo) { this.recipientKeyIds = Collections.unmodifiableSet(recipientKeyIds); - this.decryptionFingerprint = decryptionFingerprint; + this.decryptionKey = decryptionKey; this.symmetricKeyAlgorithm = symmetricKeyAlgorithm; this.compressionAlgorithm = algorithm; this.detachedSignatures = Collections.unmodifiableList(detachedSignatures); @@ -70,8 +71,8 @@ public class OpenPgpMetadata { return symmetricKeyAlgorithm != SymmetricKeyAlgorithm.NULL && !getRecipientKeyIds().isEmpty(); } - public OpenPgpV4Fingerprint getDecryptionFingerprint() { - return decryptionFingerprint; + public SubkeyIdentifier getDecryptionKey() { + return decryptionKey; } public SymmetricKeyAlgorithm getSymmetricKeyAlgorithm() { @@ -97,26 +98,22 @@ public class OpenPgpMetadata { return !getSignatures().isEmpty(); } - public Map getVerifiedSignatures() { - Map verifiedSignatures = new ConcurrentHashMap<>(); + public Map getVerifiedSignatures() { + Map verifiedSignatures = new ConcurrentHashMap<>(); for (DetachedSignature detachedSignature : detachedSignatures) { if (detachedSignature.isVerified()) { - verifiedSignatures.put(detachedSignature.getSigningKeyIdentifier().getSubkeyFingerprint(), detachedSignature.getSignature()); + verifiedSignatures.put(detachedSignature.getSigningKeyIdentifier(), detachedSignature.getSignature()); } } for (OnePassSignature onePassSignature : onePassSignatures) { if (onePassSignature.isVerified()) { - verifiedSignatures.put(onePassSignature.getFingerprint(), onePassSignature.getSignature()); + verifiedSignatures.put(onePassSignature.getSigningKey(), onePassSignature.getSignature()); } } return verifiedSignatures; } - public Set getVerifiedSignatureKeyFingerprints() { - return getVerifiedSignatures().keySet(); - } - public boolean isVerified() { return !getVerifiedSignatures().isEmpty(); } @@ -132,7 +129,13 @@ public class OpenPgpMetadata { } public boolean containsVerifiedSignatureFrom(OpenPgpV4Fingerprint fingerprint) { - return getVerifiedSignatureKeyFingerprints().contains(fingerprint); + for (SubkeyIdentifier verifiedSigningKey : getVerifiedSignatures().keySet()) { + if (verifiedSigningKey.getPrimaryKeyFingerprint().equals(fingerprint) || + verifiedSigningKey.getSubkeyFingerprint().equals(fingerprint)) { + return true; + } + } + return false; } public static class Signature { @@ -235,7 +238,7 @@ public class OpenPgpMetadata { public static class Builder { private final Set recipientFingerprints = new HashSet<>(); - private OpenPgpV4Fingerprint decryptionFingerprint; + private SubkeyIdentifier decryptionKey; private final List detachedSignatures = new ArrayList<>(); private final List onePassSignatures = new ArrayList<>(); private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.NULL; @@ -247,8 +250,8 @@ public class OpenPgpMetadata { return this; } - public Builder setDecryptionFingerprint(OpenPgpV4Fingerprint fingerprint) { - this.decryptionFingerprint = fingerprint; + public Builder setDecryptionKey(SubkeyIdentifier decryptionKey) { + this.decryptionKey = decryptionKey; return this; } @@ -280,7 +283,7 @@ public class OpenPgpMetadata { } public OpenPgpMetadata build() { - return new OpenPgpMetadata(recipientFingerprints, decryptionFingerprint, + return new OpenPgpMetadata(recipientFingerprints, decryptionKey, symmetricKeyAlgorithm, compressionAlgorithm, onePassSignatures, detachedSignatures, fileInfo); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/SubkeyIdentifier.java b/pgpainless-core/src/main/java/org/pgpainless/key/SubkeyIdentifier.java index 1837131f..4b9ef767 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/SubkeyIdentifier.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/SubkeyIdentifier.java @@ -18,6 +18,7 @@ package org.pgpainless.key; import javax.annotation.Nonnull; import org.bouncycastle.openpgp.PGPKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRing; /** * Tuple class used to identify a subkey by fingerprints of the primary key of the subkeys key ring, @@ -62,6 +63,10 @@ public class SubkeyIdentifier { this.subkeyFingerprint = subkeyFingerprint; } + public SubkeyIdentifier(PGPSecretKeyRing secretKeys) { + this(secretKeys, secretKeys.getPublicKey().getKeyID()); + } + public @Nonnull OpenPgpV4Fingerprint getFingerprint() { return getSubkeyFingerprint(); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/OnePassSignature.java b/pgpainless-core/src/main/java/org/pgpainless/signature/OnePassSignature.java index 693311d8..14f16ba7 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/OnePassSignature.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/OnePassSignature.java @@ -20,6 +20,7 @@ import org.bouncycastle.openpgp.PGPOnePassSignature; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSignature; import org.pgpainless.key.OpenPgpV4Fingerprint; +import org.pgpainless.key.SubkeyIdentifier; /** * Tuple-class that bundles together a {@link PGPOnePassSignature} object, a {@link PGPPublicKeyRing} @@ -66,8 +67,8 @@ public class OnePassSignature { * * @return signing key fingerprint */ - public OpenPgpV4Fingerprint getFingerprint() { - return new OpenPgpV4Fingerprint(verificationKeys.getPublicKey(onePassSignature.getKeyID())); + public SubkeyIdentifier getSigningKey() { + return new SubkeyIdentifier(verificationKeys, onePassSignature.getKeyID()); } /** diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptAndVerifyMessageTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptAndVerifyMessageTest.java index 7ff9edc4..52c7af32 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptAndVerifyMessageTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptAndVerifyMessageTest.java @@ -34,6 +34,7 @@ import org.pgpainless.PGPainless; import org.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.implementation.ImplementationFactory; +import org.pgpainless.key.SubkeyIdentifier; import org.pgpainless.key.TestKeys; import org.pgpainless.key.util.KeyRingUtils; @@ -82,8 +83,8 @@ public class DecryptAndVerifyMessageTest { assertEquals(CompressionAlgorithm.ZLIB, metadata.getCompressionAlgorithm()); assertEquals(SymmetricKeyAlgorithm.AES_256, metadata.getSymmetricKeyAlgorithm()); assertEquals(1, metadata.getSignatures().size()); - assertEquals(1, metadata.getVerifiedSignatureKeyFingerprints().size()); + assertEquals(1, metadata.getVerifiedSignatures().size()); assertTrue(metadata.containsVerifiedSignatureFrom(TestKeys.JULIET_FINGERPRINT)); - assertEquals(TestKeys.JULIET_FINGERPRINT, metadata.getDecryptionFingerprint()); + assertEquals(new SubkeyIdentifier(TestKeys.JULIET_FINGERPRINT), metadata.getDecryptionKey()); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptHiddenRecipientMessage.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptHiddenRecipientMessage.java index c3149493..d0336f13 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptHiddenRecipientMessage.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptHiddenRecipientMessage.java @@ -32,7 +32,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.EncryptionPurpose; import org.pgpainless.implementation.ImplementationFactory; -import org.pgpainless.key.OpenPgpV4Fingerprint; +import org.pgpainless.key.SubkeyIdentifier; import org.pgpainless.key.info.KeyRingInfo; public class DecryptHiddenRecipientMessage { @@ -158,7 +158,7 @@ public class DecryptHiddenRecipientMessage { List encryptionKeys = info.getEncryptionSubkeys(EncryptionPurpose.STORAGE_AND_COMMUNICATIONS); assertEquals(1, encryptionKeys.size()); - assertEquals(new OpenPgpV4Fingerprint(encryptionKeys.iterator().next()), metadata.getDecryptionFingerprint()); + assertEquals(new SubkeyIdentifier(secretKeys, encryptionKeys.get(0).getKeyID()), metadata.getDecryptionKey()); assertEquals("Hello Recipient :)", out.toString()); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/VerifyWithMissingPublicKeyCallback.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/VerifyWithMissingPublicKeyCallback.java index ab59fb1c..42eee3c8 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/VerifyWithMissingPublicKeyCallback.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/VerifyWithMissingPublicKeyCallback.java @@ -39,7 +39,6 @@ import org.pgpainless.algorithm.DocumentSignatureType; import org.pgpainless.encryption_signing.EncryptionStream; import org.pgpainless.encryption_signing.ProducerOptions; import org.pgpainless.encryption_signing.SigningOptions; -import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.TestKeys; import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.protection.SecretKeyRingProtector; @@ -89,6 +88,6 @@ public class VerifyWithMissingPublicKeyCallback { assertArrayEquals(msg.getBytes(StandardCharsets.UTF_8), plainOut.toByteArray()); OpenPgpMetadata metadata = verificationStream.getResult(); - assertTrue(metadata.getVerifiedSignatureKeyFingerprints().contains(new OpenPgpV4Fingerprint(signingKey))); + assertTrue(metadata.containsVerifiedSignatureFrom(signingPubKeys)); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/IgnoreMarkerPackets.java b/pgpainless-core/src/test/java/org/pgpainless/signature/IgnoreMarkerPackets.java index 5c6846e2..9df706de 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/IgnoreMarkerPackets.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/IgnoreMarkerPackets.java @@ -166,7 +166,7 @@ public class IgnoreMarkerPackets { decryptionStream.close(); OpenPgpMetadata metadata = decryptionStream.getResult(); - assertTrue(metadata.getVerifiedSignatures().containsKey(new OpenPgpV4Fingerprint("D1A66E1A23B182C9980F788CFBFCC82A015E7330"))); + assertTrue(metadata.containsVerifiedSignatureFrom(new OpenPgpV4Fingerprint("D1A66E1A23B182C9980F788CFBFCC82A015E7330"))); } @Test @@ -216,7 +216,7 @@ public class IgnoreMarkerPackets { decryptionStream.close(); assertArrayEquals(data.getBytes(StandardCharsets.UTF_8), outputStream.toByteArray()); OpenPgpMetadata metadata = decryptionStream.getResult(); - assertTrue(metadata.getVerifiedSignatures().containsKey(new OpenPgpV4Fingerprint("D1A66E1A23B182C9980F788CFBFCC82A015E7330"))); + assertTrue(metadata.containsVerifiedSignatureFrom(new OpenPgpV4Fingerprint("D1A66E1A23B182C9980F788CFBFCC82A015E7330"))); } @Test diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Decrypt.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Decrypt.java index de3f57e9..8ea4c964 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Decrypt.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Decrypt.java @@ -35,7 +35,7 @@ import org.pgpainless.PGPainless; import org.pgpainless.decryption_verification.ConsumerOptions; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.decryption_verification.OpenPgpMetadata; -import org.pgpainless.key.OpenPgpV4Fingerprint; +import org.pgpainless.key.SubkeyIdentifier; import org.pgpainless.sop.SopKeyUtil; import picocli.CommandLine; @@ -156,18 +156,11 @@ public class Decrypt implements Runnable { StringBuilder sb = new StringBuilder(); if (verifyWith != null) { - for (OpenPgpV4Fingerprint fingerprint : metadata.getVerifiedSignatures().keySet()) { - PGPPublicKeyRing verifier = null; - for (PGPPublicKeyRing ring : verifyWith) { - if (ring.getPublicKey(fingerprint.getKeyId()) != null) { - verifier = ring; - break; - } - } - PGPSignature signature = metadata.getVerifiedSignatures().get(fingerprint); + for (SubkeyIdentifier signingKey : metadata.getVerifiedSignatures().keySet()) { + PGPSignature signature = metadata.getVerifiedSignatures().get(signingKey); sb.append(df.format(signature.getCreationTime())).append(' ') - .append(fingerprint).append(' ') - .append(verifier != null ? new OpenPgpV4Fingerprint(verifier) : "null").append('\n'); + .append(signingKey.getSubkeyFingerprint()).append(' ') + .append(signingKey.getPrimaryKeyFingerprint()).append('\n'); } try { diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java index 52581709..460f83dc 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java @@ -39,7 +39,7 @@ import org.pgpainless.PGPainless; import org.pgpainless.decryption_verification.ConsumerOptions; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.decryption_verification.OpenPgpMetadata; -import org.pgpainless.key.OpenPgpV4Fingerprint; +import org.pgpainless.key.SubkeyIdentifier; import picocli.CommandLine; @CommandLine.Command(name = "verify", @@ -124,12 +124,12 @@ public class Verify implements Runnable { return; } - Map signaturesInTimeRange = new HashMap<>(); - for (OpenPgpV4Fingerprint fingerprint : metadata.getVerifiedSignatures().keySet()) { - PGPSignature signature = metadata.getVerifiedSignatures().get(fingerprint); + Map signaturesInTimeRange = new HashMap<>(); + for (SubkeyIdentifier signingKey : metadata.getVerifiedSignatures().keySet()) { + PGPSignature signature = metadata.getVerifiedSignatures().get(signingKey); Date creationTime = signature.getCreationTime(); if (!creationTime.before(notBeforeDate) && !creationTime.after(notAfterDate)) { - signaturesInTimeRange.put(fingerprint, signature); + signaturesInTimeRange.put(signingKey, signature); } } @@ -141,19 +141,19 @@ public class Verify implements Runnable { printValidSignatures(signaturesInTimeRange, publicKeys); } - private void printValidSignatures(Map validSignatures, Map publicKeys) { - for (OpenPgpV4Fingerprint sigKeyFp : validSignatures.keySet()) { - PGPSignature signature = validSignatures.get(sigKeyFp); + private void printValidSignatures(Map validSignatures, Map publicKeys) { + for (SubkeyIdentifier signingKey : validSignatures.keySet()) { + PGPSignature signature = validSignatures.get(signingKey); + for (PGPPublicKeyRing ring : publicKeys.keySet()) { // Search signing key ring File file = publicKeys.get(ring); - if (ring.getPublicKey(sigKeyFp.getKeyId()) == null) { + if (ring.getPublicKey(signingKey.getKeyId()) == null) { continue; } String utcSigDate = df.format(signature.getCreationTime()); - OpenPgpV4Fingerprint primaryKeyFp = new OpenPgpV4Fingerprint(ring); - print_ln(utcSigDate + " " + sigKeyFp.toString() + " " + primaryKeyFp.toString() + + print_ln(utcSigDate + " " + signingKey.getSubkeyFingerprint() + " " + signingKey.getPrimaryKeyFingerprint() + " signed by " + file.getName()); } }