diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt index 1241b110..af5ffe97 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt @@ -13,6 +13,7 @@ import java.util.zip.InflaterInputStream import openpgp.openPgpKeyId import org.bouncycastle.bcpg.BCPGInputStream import org.bouncycastle.bcpg.CompressionAlgorithmTags +import org.bouncycastle.bcpg.KeyIdentifier import org.bouncycastle.bcpg.UnsupportedPacketVersionException import org.bouncycastle.openpgp.PGPCompressedData import org.bouncycastle.openpgp.PGPEncryptedData @@ -30,6 +31,7 @@ import org.bouncycastle.openpgp.api.OpenPGPKey import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey import org.bouncycastle.openpgp.api.OpenPGPSignature.OpenPGPDocumentSignature +import org.bouncycastle.openpgp.api.exception.MalformedOpenPGPSignatureException import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory import org.bouncycastle.util.io.TeeInputStream @@ -60,8 +62,6 @@ import org.pgpainless.exception.UnacceptableAlgorithmException import org.pgpainless.exception.WrongPassphraseException import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.key.protection.UnlockSecretKey.Companion.unlockSecretKey -import org.pgpainless.policy.Policy -import org.pgpainless.signature.consumer.CertificateValidator import org.pgpainless.signature.consumer.OnePassSignatureCheck import org.pgpainless.signature.consumer.SignatureValidator import org.pgpainless.util.ArmoredInputStreamFactory @@ -312,8 +312,7 @@ class OpenPgpMessageInputStream( signatures .leaveNesting() // TODO: Only leave nesting if all OPSs of the nesting layer are // dealt with - signatures.addCorrespondingOnePassSignature( - signature, layerMetadata, api.algorithmPolicy) + signatures.addCorrespondingOnePassSignature(signature, layerMetadata) } else { LOGGER.debug( "Prepended Signature Packet by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.") @@ -618,7 +617,7 @@ class OpenPgpMessageInputStream( throw RuntimeException(e) } } - signatures.finish(layerMetadata, api.algorithmPolicy) + signatures.finish(layerMetadata) } return r } @@ -645,7 +644,7 @@ class OpenPgpMessageInputStream( throw RuntimeException(e) } } - signatures.finish(layerMetadata, api.algorithmPolicy) + signatures.finish(layerMetadata) } return r } @@ -832,15 +831,11 @@ class OpenPgpMessageInputStream( } } - fun addCorrespondingOnePassSignature( - signature: PGPSignature, - layer: Layer, - policy: Policy - ) { + fun addCorrespondingOnePassSignature(signature: PGPSignature, layer: Layer) { var found = false - val keyId = signature.issuerKeyId for ((i, check) in onePassSignatures.withIndex().reversed()) { - if (check.onePassSignature.keyID != keyId) { + if (!KeyIdentifier.matches( + signature.keyIdentifiers, check.onePassSignature.keyIdentifier, true)) { continue } found = true @@ -848,8 +843,11 @@ class OpenPgpMessageInputStream( if (check.signature != null) { continue } - check.signature = signature + + val documentSignature = + OpenPGPDocumentSignature( + signature, check.verificationKeys.getSigningKeyFor(signature)) val verification = SignatureVerification( signature, @@ -861,11 +859,15 @@ class OpenPgpMessageInputStream( SignatureValidator.signatureWasCreatedInBounds( options.getVerifyNotBefore(), options.getVerifyNotAfter()) .verify(signature) - CertificateValidator.validateCertificateAndVerifyOnePassSignature(check, policy) - LOGGER.debug("Acceptable signature by key ${verification.signingKey}") - layer.addVerifiedOnePassSignature(verification) + if (documentSignature.verify(check.onePassSignature) && + documentSignature.isValid(api.implementation.policy())) { + layer.addVerifiedOnePassSignature(verification) + } else { + throw SignatureValidationException("Incorrect OnePassSignature.") + } + } catch (e: MalformedOpenPGPSignatureException) { + throw SignatureValidationException("Malformed OnePassSignature.", e) } catch (e: SignatureValidationException) { - LOGGER.debug("Rejected signature by key ${verification.signingKey}", e) layer.addRejectedOnePassSignature( SignatureVerification.Failure(verification, e)) } @@ -874,7 +876,7 @@ class OpenPgpMessageInputStream( if (!found) { LOGGER.debug( - "No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.") + "No suitable certificate for verification of signature by key ${signature.issuerKeyId.openPgpKeyId()} found.") inbandSignaturesWithMissingCert.add( SignatureVerification.Failure( signature, null, SignatureValidationException("Missing verification key."))) @@ -963,7 +965,7 @@ class OpenPgpMessageInputStream( } } - fun finish(layer: Layer, policy: Policy) { + fun finish(layer: Layer) { for (detached in detachedSignatures) { val verification = SignatureVerification(detached.signature, SubkeyIdentifier(detached.issuer)) @@ -971,12 +973,14 @@ class OpenPgpMessageInputStream( SignatureValidator.signatureWasCreatedInBounds( options.getVerifyNotBefore(), options.getVerifyNotAfter()) .verify(detached.signature) - CertificateValidator.validateCertificateAndVerifyInitializedSignature( - detached.signature, detached.issuerCertificate.pgpPublicKeyRing, policy) - LOGGER.debug("Acceptable signature by key ${verification.signingKey}") - layer.addVerifiedDetachedSignature(verification) + if (detached.verify() && detached.isValid(api.implementation.policy())) { + layer.addVerifiedDetachedSignature(verification) + } else { + throw SignatureValidationException("Incorrect detached signature.") + } + } catch (e: MalformedOpenPGPSignatureException) { + throw SignatureValidationException("Malformed detached signature.", e) } catch (e: SignatureValidationException) { - LOGGER.debug("Rejected signature by key ${verification.signingKey}", e) layer.addRejectedDetachedSignature( SignatureVerification.Failure(verification, e)) } @@ -989,10 +993,13 @@ class OpenPgpMessageInputStream( SignatureValidator.signatureWasCreatedInBounds( options.getVerifyNotBefore(), options.getVerifyNotAfter()) .verify(prepended.signature) - CertificateValidator.validateCertificateAndVerifyInitializedSignature( - prepended.signature, prepended.issuerCertificate.pgpPublicKeyRing, policy) - LOGGER.debug("Acceptable signature by key ${verification.signingKey}") - layer.addVerifiedPrependedSignature(verification) + if (prepended.verify() && prepended.isValid(api.implementation.policy())) { + layer.addVerifiedPrependedSignature(verification) + } else { + throw SignatureValidationException("Incorrect prepended signature.") + } + } catch (e: MalformedOpenPGPSignatureException) { + throw SignatureValidationException("Malformed prepended signature.", e) } catch (e: SignatureValidationException) { LOGGER.debug("Rejected signature by key ${verification.signingKey}", e) layer.addRejectedPrependedSignature( diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/CertificateValidator.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/CertificateValidator.kt deleted file mode 100644 index 5d5f60b8..00000000 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/CertificateValidator.kt +++ /dev/null @@ -1,318 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.consumer - -import java.io.InputStream -import java.util.* -import openpgp.openPgpKeyId -import org.bouncycastle.openpgp.PGPPublicKey -import org.bouncycastle.openpgp.PGPPublicKeyRing -import org.bouncycastle.openpgp.PGPSignature -import org.pgpainless.PGPainless -import org.pgpainless.algorithm.KeyFlag -import org.pgpainless.algorithm.SignatureType -import org.pgpainless.bouncycastle.extensions.getPublicKey -import org.pgpainless.bouncycastle.extensions.issuerKeyId -import org.pgpainless.exception.SignatureValidationException -import org.pgpainless.key.util.KeyRingUtils -import org.pgpainless.policy.Policy -import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil -import org.slf4j.LoggerFactory - -/** - * A collection of static methods that validate signing certificates (public keys) and verify - * signature correctness. - */ -class CertificateValidator { - - companion object { - - @JvmStatic private val LOGGER = LoggerFactory.getLogger(CertificateValidator::class.java) - - /** - * Check if the signing key was eligible to create the provided signature. - * - * That entails: - * - Check, if the primary key is being revoked via key-revocation signatures. - * - Check, if the keys user-ids are revoked or not bound. - * - Check, if the signing subkey is revoked or expired. - * - Check, if the signing key is not capable of signing - * - * @param signature signature - * @param signingKeyRing signing key ring - * @param policy validation policy - * @return true if the signing key was eligible to create the signature - * @throws SignatureValidationException in case of a validation constraint violation - */ - @JvmStatic - @Throws(SignatureValidationException::class) - fun validateCertificate( - signature: PGPSignature, - signingKeyRing: PGPPublicKeyRing, - policy: Policy = PGPainless.getInstance().algorithmPolicy - ): Boolean { - val signingSubkey: PGPPublicKey = - signingKeyRing.getPublicKey(signature.issuerKeyId) - ?: throw SignatureValidationException( - "Provided key ring does not contain a subkey with id ${signature.issuerKeyId.openPgpKeyId()}.") - val primaryKey = signingKeyRing.publicKey!! - val directKeyAndRevSigs = mutableListOf() - val rejections = mutableMapOf() - // revocations - primaryKey - .getSignaturesOfType(SignatureType.KEY_REVOCATION.code) - .asSequence() - .filter { - it.issuerKeyId == primaryKey.keyID - } // We do not support external rev keys - .forEach { - try { - if (SignatureVerifier.verifyKeyRevocationSignature( - it, primaryKey, policy, signature.creationTime)) { - directKeyAndRevSigs.add(it) - } - } catch (e: SignatureValidationException) { - rejections[it] = e - LOGGER.debug("Rejecting key revocation signature: ${e.message}", e) - } - } - - // direct-key sigs - primaryKey - .getSignaturesOfType(SignatureType.DIRECT_KEY.code) - .asSequence() - .filter { it.issuerKeyId == primaryKey.keyID } - .forEach { - try { - if (SignatureVerifier.verifyDirectKeySignature( - it, primaryKey, policy, signature.creationTime)) { - directKeyAndRevSigs.add(it) - } - } catch (e: SignatureValidationException) { - rejections[it] = e - LOGGER.debug("Rejecting key signature: ${e.message}, e") - } - } - - directKeyAndRevSigs.sortWith( - SignatureValidityComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD)) - if (directKeyAndRevSigs.isNotEmpty()) { - if (directKeyAndRevSigs[0].signatureType == SignatureType.KEY_REVOCATION.code) { - throw SignatureValidationException("Primary key has been revoked.") - } - } - - // UserID signatures - val userIdSignatures = mutableMapOf>() - KeyRingUtils.getUserIdsIgnoringInvalidUTF8(primaryKey).forEach { userId -> - buildList { - primaryKey - .getSignaturesForID(userId) - .asSequence() - .filter { it.issuerKeyId == primaryKey.keyID } - .forEach { uidSig -> - try { - if (SignatureVerifier.verifySignatureOverUserId( - userId, - uidSig, - primaryKey, - policy, - signature.creationTime)) { - add(uidSig) - } - } catch (e: SignatureValidationException) { - rejections[uidSig] = e - LOGGER.debug("Rejecting user-id signature: ${e.message}", e) - } - } - } - .sortedWith( - SignatureValidityComparator( - SignatureCreationDateComparator.Order.NEW_TO_OLD)) - .let { userIdSignatures[userId] = it } - } - - val hasAnyUserIds = userIdSignatures.isNotEmpty() - val isAnyUserIdValid = - userIdSignatures.any { entry -> - entry.value.isNotEmpty() && - entry.value[0].signatureType != SignatureType.CERTIFICATION_REVOCATION.code - } - - if (hasAnyUserIds && !isAnyUserIdValid) { - throw SignatureValidationException("No valid user-id found.", rejections) - } - - // Specific signer user-id - if (policy.signerUserIdValidationLevel == Policy.SignerUserIdValidationLevel.STRICT) { - SignatureSubpacketsUtil.getSignerUserID(signature)?.let { - if (userIdSignatures[it.id] == null || userIdSignatures[it.id]!!.isEmpty()) { - throw SignatureValidationException( - "Signature was allegedly made by user-id '${it.id}'," + - " but we have no valid signatures for that on the certificate.") - } - - if (userIdSignatures[it.id]!![0].signatureType == - SignatureType.CERTIFICATION_REVOCATION.code) { - throw SignatureValidationException( - "Signature was made with user-id '${it.id}' which is revoked.") - } - } - } - - if (signingSubkey.keyID == primaryKey.keyID) { // signing key is primary key - if (directKeyAndRevSigs.isNotEmpty()) { - val directKeySig = directKeyAndRevSigs[0] - val flags = SignatureSubpacketsUtil.getKeyFlags(directKeySig) - if (flags != null && KeyFlag.hasKeyFlag(flags.flags, KeyFlag.SIGN_DATA)) { - return true - } - } - // Reject sigs by non-signing keys - if (userIdSignatures.none { (_, sigs) -> - sigs.any { - SignatureSubpacketsUtil.getKeyFlags(it)?.let { f -> - KeyFlag.hasKeyFlag(f.flags, KeyFlag.SIGN_DATA) - } == true - } - }) { - throw SignatureValidationException( - "Signature was generated by non-signing key.") - } - } else { // signing key is subkey - val subkeySigs = mutableListOf() - signingSubkey - .getSignaturesOfType(SignatureType.SUBKEY_REVOCATION.code) - .asSequence() - .filter { it.issuerKeyId == primaryKey.keyID } - .forEach { - try { - if (SignatureVerifier.verifySubkeyBindingRevocation( - it, primaryKey, signingSubkey, policy, signature.creationTime)) { - subkeySigs.add(it) - } - } catch (e: SignatureValidationException) { - rejections[it] = e - LOGGER.debug("Rejecting subkey revocation signature: ${e.message}", e) - } - } - - signingSubkey - .getSignaturesOfType(SignatureType.SUBKEY_BINDING.code) - .asSequence() - .forEach { - try { - if (SignatureVerifier.verifySubkeyBindingSignature( - it, primaryKey, signingSubkey, policy, signature.creationTime)) { - subkeySigs.add(it) - } - } catch (e: SignatureValidationException) { - rejections[it] = e - LOGGER.debug("Rejecting subkey binding signature: ${e.message}", e) - } - } - - subkeySigs.sortWith( - SignatureValidityComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD)) - if (subkeySigs.isEmpty()) { - throw SignatureValidationException("Subkey is not bound.", rejections) - } - - if (subkeySigs[0].signatureType == SignatureType.SUBKEY_REVOCATION.code) { - throw SignatureValidationException("Subkey is revoked.") - } - - val keyFlags = SignatureSubpacketsUtil.getKeyFlags(subkeySigs[0]) - if (keyFlags == null || !KeyFlag.hasKeyFlag(keyFlags.flags, KeyFlag.SIGN_DATA)) { - throw SignatureValidationException( - "Signature was made by key which is not capable of signing (no keyflag).") - } - } - return true - } - - /** - * Validate the given signing key and then verify the given signature while parsing out the - * signed data. Uninitialized means that no signed data has been read and the hash - * generators state has not yet been updated. - * - * @param signature uninitialized signature - * @param signedData input stream containing signed data - * @param signingKeyRing key ring containing signing key - * @param policy validation policy - * @param validationDate date of validation - * @return true if the signature is valid, false otherwise - * @throws SignatureValidationException for validation constraint violations - */ - @JvmStatic - @Throws(SignatureValidationException::class) - fun validateCertificateAndVerifyUninitializedSignature( - signature: PGPSignature, - signedData: InputStream, - signingKeyRing: PGPPublicKeyRing, - policy: Policy, - referenceTime: Date = signature.creationTime - ): Boolean { - return validateCertificate(signature, signingKeyRing, policy) && - SignatureVerifier.verifyUninitializedSignature( - signature, - signedData, - signingKeyRing.getPublicKey(signature.issuerKeyId)!!, - policy, - referenceTime) - } - - /** - * Validate the signing key and the given initialized signature. Initialized means that the - * signatures hash generator has already been updated by reading the signed data completely. - * - * @param signature initialized signature - * @param verificationKeys key ring containing the verification key - * @param policy validation policy - * @return true if the signature is valid, false otherwise - * @throws SignatureValidationException in case of a validation constraint violation - */ - @JvmStatic - @Throws(SignatureValidationException::class) - fun validateCertificateAndVerifyInitializedSignature( - signature: PGPSignature, - verificationKeys: PGPPublicKeyRing, - policy: Policy - ): Boolean { - return validateCertificate(signature, verificationKeys, policy) && - SignatureVerifier.verifyInitializedSignature( - signature, - verificationKeys.getPublicKey(signature.issuerKeyId), - policy, - signature.creationTime) - } - - /** - * Validate the signing key certificate and the given [OnePassSignatureCheck]. - * - * @param onePassSignature corresponding one-pass-signature - * @param policy policy - * @return true if the certificate is valid and the signature is correct, false otherwise. - * @throws SignatureValidationException in case of a validation error - */ - @JvmStatic - @Throws(SignatureValidationException::class) - fun validateCertificateAndVerifyOnePassSignature( - onePassSignature: OnePassSignatureCheck, - policy: Policy - ): Boolean { - return validateCertificate( - onePassSignature.signature!!, - onePassSignature.verificationKeys.pgpPublicKeyRing, - policy) && - SignatureVerifier.verifyOnePassSignature( - onePassSignature.signature!!, - onePassSignature.verificationKeys.pgpKeyRing.getPublicKey( - onePassSignature.signature!!.issuerKeyId), - onePassSignature, - policy) - } - } -} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureCheck.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureCheck.kt deleted file mode 100644 index 15564773..00000000 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureCheck.kt +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.consumer - -import org.bouncycastle.openpgp.PGPKeyRing -import org.bouncycastle.openpgp.PGPSignature -import org.pgpainless.key.SubkeyIdentifier - -/** - * Tuple-class which bundles together a signature, the signing key that created the signature, an - * identifier of the signing key and a record of whether the signature was verified. - * - * @param signature OpenPGP signature - * @param signingKeyIdentifier identifier pointing to the exact signing key which was used to create - * the signature - * @param signingKeyRing certificate or key ring that contains the signing key that created the - * signature - */ -data class SignatureCheck( - val signature: PGPSignature, - val signingKeyRing: PGPKeyRing, - val signingKeyIdentifier: SubkeyIdentifier -) {} diff --git a/pgpainless-core/src/test/java/investigations/ModifiedPublicKeysInvestigation.java b/pgpainless-core/src/test/java/investigations/ModifiedPublicKeysInvestigation.java index 5ee78246..46bfca6a 100644 --- a/pgpainless-core/src/test/java/investigations/ModifiedPublicKeysInvestigation.java +++ b/pgpainless-core/src/test/java/investigations/ModifiedPublicKeysInvestigation.java @@ -211,7 +211,6 @@ public class ModifiedPublicKeysInvestigation { @Test public void assertModifiedDSAKeyThrowsKeyIntegrityException() throws IOException { - System.out.println(DSA); PGPainless api = PGPainless.newInstance(); Policy policy = api.getAlgorithmPolicy(); policy.setEnableKeyParameterValidation(true); diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CleartextSignatureVerificationTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CleartextSignatureVerificationTest.java index 8b423cba..d9b4c809 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CleartextSignatureVerificationTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CleartextSignatureVerificationTest.java @@ -19,7 +19,6 @@ import java.nio.charset.StandardCharsets; import java.util.Random; import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSignature; @@ -36,9 +35,7 @@ import org.pgpainless.encryption_signing.SigningOptions; import org.pgpainless.exception.WrongConsumingMethodException; import org.pgpainless.key.TestKeys; import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.signature.consumer.CertificateValidator; import org.pgpainless.signature.SignatureUtils; -import org.pgpainless.signature.consumer.SignatureVerifier; import org.pgpainless.util.ArmorUtils; import org.pgpainless.util.TestUtils; @@ -136,19 +133,6 @@ public class CleartextSignatureVerificationTest { assertArrayEquals(MESSAGE_BODY, bytes.toByteArray()); } - @Test - public void verifySignatureDetached() - throws IOException, PGPException { - PGPPublicKeyRing signingKeys = TestKeys.getEmilPublicKeyRing(); - - PGPSignature signature = SignatureUtils.readSignatures(SIGNATURE).get(0); - PGPPublicKey signingKey = signingKeys.getPublicKey(signature.getKeyID()); - - SignatureVerifier.initializeSignatureAndUpdateWithSignedData(signature, new ByteArrayInputStream(MESSAGE_BODY), signingKey); - - CertificateValidator.validateCertificateAndVerifyInitializedSignature(signature, signingKeys, PGPainless.getPolicy()); - } - public static void main(String[] args) throws IOException { // CHECKSTYLE:OFF PGPPublicKeyRing keys = TestKeys.getEmilPublicKeyRing(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/BindingSignatureSubpacketsTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/BindingSignatureSubpacketsTest.java index bf3d7a0d..fa018909 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/BindingSignatureSubpacketsTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/BindingSignatureSubpacketsTest.java @@ -4,23 +4,23 @@ package org.pgpainless.signature; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.Date; -import org.bouncycastle.openpgp.PGPPublicKeyRing; -import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; +import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtendWith; import org.pgpainless.PGPainless; -import org.pgpainless.exception.SignatureValidationException; -import org.pgpainless.policy.Policy; -import org.pgpainless.signature.consumer.CertificateValidator; +import org.pgpainless.decryption_verification.ConsumerOptions; +import org.pgpainless.decryption_verification.DecryptionStream; +import org.pgpainless.decryption_verification.MessageMetadata; import org.pgpainless.util.TestAllImplementations; /** @@ -47,12 +47,9 @@ public class BindingSignatureSubpacketsTest { "-----END PGP SIGNATURE-----\n"; private static final String data = "Hello World :)"; - private Date validationDate = new Date(); - private Policy policy = PGPainless.getPolicy(); - @TestTemplate @ExtendWith(TestAllImplementations.class) - public void baseCase() throws IOException { + public void baseCase() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -113,7 +110,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingIssuerFpOnly() throws IOException { + public void subkeyBindingIssuerFpOnly() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -174,7 +171,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingIssuerV6IssuerFp() throws IOException { + public void subkeyBindingIssuerV6IssuerFp() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -235,7 +232,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingIssuerFakeIssuer() throws IOException { + public void subkeyBindingIssuerFakeIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -296,7 +293,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingFakeIssuerIssuer() throws IOException { + public void subkeyBindingFakeIssuerIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -357,7 +354,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingFakeIssuer() throws IOException { + public void subkeyBindingFakeIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -418,7 +415,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingNoIssuer() throws IOException { + public void subkeyBindingNoIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -478,7 +475,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void unknownSubpacketHashed() throws IOException { + public void unknownSubpacketHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -539,7 +536,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingUnknownCriticalSubpacket() throws IOException { + public void subkeyBindingUnknownCriticalSubpacket() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -600,7 +597,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingUnknownSubpacketUnhashed() throws IOException { + public void subkeyBindingUnknownSubpacketUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -661,7 +658,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingUnknownCriticalSubpacketUnhashed() throws IOException { + public void subkeyBindingUnknownCriticalSubpacketUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -722,7 +719,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingUnknownNotationHashed() throws IOException { + public void subkeyBindingUnknownNotationHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -784,7 +781,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingCriticalUnknownNotationHashed() throws IOException { + public void subkeyBindingCriticalUnknownNotationHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -846,7 +843,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingUnknownNotationUnhashed() throws IOException { + public void subkeyBindingUnknownNotationUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -908,7 +905,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingCriticalUnknownNotationUnhashed() throws IOException { + public void subkeyBindingCriticalUnknownNotationUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -970,7 +967,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingBackSigFakeBackSig() throws IOException { + public void subkeyBindingBackSigFakeBackSig() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1042,7 +1039,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void subkeyBindingFakeBackSigBackSig() throws IOException { + public void subkeyBindingFakeBackSigBackSig() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1114,7 +1111,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingIssuerFpOnly() throws IOException { + public void primaryBindingIssuerFpOnly() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1175,7 +1172,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingIssuerV6IssuerFp() throws IOException { + public void primaryBindingIssuerV6IssuerFp() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1236,7 +1233,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingIssuerFakeIssuer() throws IOException { + public void primaryBindingIssuerFakeIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1297,7 +1294,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingFakeIssuerIssuer() throws IOException { + public void primaryBindingFakeIssuerIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1358,7 +1355,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingFakeIssuer() throws IOException { + public void primaryBindingFakeIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1419,7 +1416,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingNoIssuer() throws IOException { + public void primaryBindingNoIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1479,7 +1476,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingUnknownSubpacketHashed() throws IOException { + public void primaryBindingUnknownSubpacketHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1540,7 +1537,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingCriticalUnknownSubpacketHashed() throws IOException { + public void primaryBindingCriticalUnknownSubpacketHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1601,7 +1598,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingUnknownSubpacketUnhashed() throws IOException { + public void primaryBindingUnknownSubpacketUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1662,7 +1659,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingCriticalUnknownSubpacketUnhashed() throws IOException { + public void primaryBindingCriticalUnknownSubpacketUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1723,7 +1720,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingUnknownNotationHashed() throws IOException { + public void primaryBindingUnknownNotationHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1785,7 +1782,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingCriticalUnknownNotationHashed() throws IOException { + public void primaryBindingCriticalUnknownNotationHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1847,7 +1844,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingUnknownNotationUnhashed() throws IOException { + public void primaryBindingUnknownNotationUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1909,7 +1906,7 @@ public class BindingSignatureSubpacketsTest { @TestTemplate @ExtendWith(TestAllImplementations.class) - public void primaryBindingCriticalUnknownNotationUnhashed() throws IOException { + public void primaryBindingCriticalUnknownNotationUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + @@ -1969,27 +1966,38 @@ public class BindingSignatureSubpacketsTest { expectSignatureValidationSucceeds(key, "Critical unknown notation is acceptable in unhashed area of primary key binding sig."); } - private void expectSignatureValidationSucceeds(String key, String message) throws IOException { - PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key); - PGPSignature signature = SignatureUtils.readSignatures(sig).get(0); + private void expectSignatureValidationSucceeds(String key, String message) throws IOException, PGPException { + PGPainless api = PGPainless.getInstance(); + OpenPGPCertificate certificate = api.readKey().parseCertificate(key); - try { - CertificateValidator.validateCertificateAndVerifyUninitializedSignature(signature, getSignedData(data), publicKeys, policy, validationDate); - } catch (SignatureValidationException e) { - // CHECKSTYLE:OFF - e.printStackTrace(); - // CHECKSTYLE:ON - fail(message + ": " + e.getMessage()); + DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify().onInputStream(getSignedData(data)) + .withOptions(ConsumerOptions.get(api) + .addVerificationCert(certificate) + .addVerificationOfDetachedSignatures(SignatureUtils.readSignatures(sig))); + + Streams.drain(decryptionStream); + decryptionStream.close(); + MessageMetadata metadata = decryptionStream.getMetadata(); + + if (!metadata.getRejectedSignatures().isEmpty()) { + throw metadata.getRejectedSignatures().get(0).getValidationException(); } + assertTrue(decryptionStream.getMetadata().isVerifiedSignedBy(certificate), + message); } - private void expectSignatureValidationFails(String key, String message) throws IOException { - PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key); - PGPSignature signature = SignatureUtils.readSignatures(sig).get(0); + private void expectSignatureValidationFails(String key, String message) throws IOException, PGPException { + PGPainless api = PGPainless.getInstance(); + OpenPGPCertificate certificate = api.readKey().parseCertificate(key); - assertThrows(SignatureValidationException.class, () -> - CertificateValidator.validateCertificateAndVerifyUninitializedSignature( - signature, getSignedData(data), publicKeys, policy, validationDate), + DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify().onInputStream(getSignedData(data)) + .withOptions(ConsumerOptions.get(api) + .addVerificationCert(certificate) + .addVerificationOfDetachedSignatures(SignatureUtils.readSignatures(sig))); + + Streams.drain(decryptionStream); + decryptionStream.close(); + assertFalse(decryptionStream.getMetadata().isVerifiedSignedBy(certificate), message); } diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/CertificateValidatorTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/CertificateValidatorTest.java index b1e70445..ff22d4d3 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/CertificateValidatorTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/CertificateValidatorTest.java @@ -17,6 +17,7 @@ import java.util.Date; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestTemplate; @@ -27,7 +28,6 @@ import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.decryption_verification.MessageMetadata; import org.pgpainless.exception.SignatureValidationException; import org.pgpainless.policy.Policy; -import org.pgpainless.signature.consumer.CertificateValidator; import org.pgpainless.util.TestAllImplementations; public class CertificateValidatorTest { @@ -170,16 +170,16 @@ public class CertificateValidatorTest { Date validationDate = new Date(); String data = "Hello, World"; - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate), "Signature predates primary key"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( unboundSubkey, getSignedData(data), publicKeys, policy, validationDate), "Primary key hard revoked"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( primaryKeyRevoked, getSignedData(data), publicKeys, policy, validationDate), "Primary key hard revoked"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( primaryKeyRevalidated, getSignedData(data), publicKeys, policy, validationDate), "Primary key hard revoked"); } @@ -321,16 +321,16 @@ public class CertificateValidatorTest { Date validationDate = new Date(); String data = "Hello, World"; - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate), "Signature predates primary key"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( unboundSubkey, getSignedData(data), publicKeys, policy, validationDate), "Signing key unbound + hard revocation"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( revokedSubkey, getSignedData(data), publicKeys, policy, validationDate), "Primary key is hard revoked"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( revalidatedSubkey, getSignedData(data), publicKeys, policy, validationDate), "Primary key is hard revoked"); } @@ -473,16 +473,16 @@ public class CertificateValidatorTest { Date validationDate = new Date(); String data = "Hello World :)"; - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate), "Signature predates primary key"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( unboundKey, getSignedData(data), publicKeys, policy, validationDate), "Signing key unbound + hard revocation"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( afterHardRevocation, getSignedData(data), publicKeys, policy, validationDate), "Hard revocation invalidates key at all times"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( afterRevalidation, getSignedData(data), publicKeys, policy, validationDate), "Hard revocation invalidates key at all times"); } @@ -624,22 +624,22 @@ public class CertificateValidatorTest { String data = "Hello, World"; // Sig not valid, as it predates the signing key creation time - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( predatesPrimaryKey, getSignedData(data), publicKeys, policy, predatesPrimaryKey.getCreationTime()), "Signature predates primary key creation date"); // Sig valid - assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertDoesNotThrow(() -> verify( keyIsValid, getSignedData(data), publicKeys, policy, keyIsValid.getCreationTime()), "Signature is valid"); // Sig not valid, as the signing key is revoked - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( keyIsRevoked, getSignedData(data), publicKeys, policy, keyIsRevoked.getCreationTime()), "Signing key is revoked at this point"); // Sig valid, as the signing key is revalidated - assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertDoesNotThrow(() -> verify( keyIsRevalidated, getSignedData(data), publicKeys, policy, keyIsRevalidated.getCreationTime()), "Signature is valid, as signing key is revalidated"); } @@ -782,17 +782,17 @@ public class CertificateValidatorTest { String data = "Hello, World"; Date validationDate = new Date(); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate), "Signature predates primary key creation date"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( keyNotBound, getSignedData(data), publicKeys, policy, validationDate), "Signing key is not bound at this point"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( keyRevoked, getSignedData(data), publicKeys, policy, validationDate), "Signing key is revoked at this point"); assertDoesNotThrow(() -> - CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + verify( valid, getSignedData(data), publicKeys, policy, validationDate), "Signing key is revalidated"); } @@ -935,17 +935,17 @@ public class CertificateValidatorTest { Date validationDate = new Date(); String data = "Hello, World"; - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate), "Signature predates primary key creation date"); - assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertDoesNotThrow(() -> verify( valid, getSignedData(data), publicKeys, policy, validationDate), "Signature is valid"); assertThrows(SignatureValidationException.class, () -> - CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + verify( revoked, getSignedData(data), publicKeys, policy, validationDate), "Primary key is revoked"); - assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertDoesNotThrow(() -> verify( revalidated, getSignedData(data), publicKeys, policy, validationDate), "Primary key is re-legitimized"); } @@ -1275,47 +1275,66 @@ public class CertificateValidatorTest { Date validationDate = new Date(); String data = "Hello World :)"; - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( sigAT0, getSignedData(data), keysA, policy, validationDate), "Signature predates key creation time"); - assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertDoesNotThrow(() -> verify( sigAT1_T2, getSignedData(data), keysA, policy, validationDate), "Key valid"); assertThrows(SignatureValidationException.class, () -> - CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + verify( sigAT2_T3, getSignedData(data), keysA, policy, validationDate), "Key is not valid, as subkey binding expired"); - assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertDoesNotThrow(() -> verify( sigAT3_now, getSignedData(data), keysA, policy, validationDate), "Key is valid again"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( sigBT0, getSignedData(data), keysB, policy, validationDate), "Signature predates key creation time"); - assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertDoesNotThrow(() -> verify( sigBT1_T2, getSignedData(data), keysB, policy, validationDate), "Key is valid"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( sigBT2_T3, getSignedData(data), keysB, policy, validationDate), "Primary key is not signing-capable"); - assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertDoesNotThrow(() -> verify( sigBT3_now, getSignedData(data), keysB, policy, validationDate), "Key is valid again"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( sigCT0, getSignedData(data), keysC, policy, validationDate), "Signature predates key creation time"); - assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertDoesNotThrow(() -> verify( sigCT1_T2, getSignedData(data), keysC, policy, validationDate), "Key is valid"); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertThrows(SignatureValidationException.class, () -> verify( sigCT2_T3, getSignedData(data), keysC, policy, validationDate), "Key is revoked"); - assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature( + assertDoesNotThrow(() -> verify( sigCT3_now, getSignedData(data), keysC, policy, validationDate), "Key is valid again"); } + private void verify(PGPSignature signature, InputStream dataIn, PGPPublicKeyRing cert, Policy policy, Date validationDate) throws PGPException, IOException { + PGPainless api = PGPainless.getInstance(); + OpenPGPCertificate certificate = api.toCertificate(cert); + + DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() + .onInputStream(dataIn) + .withOptions(ConsumerOptions.get(api) + .addVerificationOfDetachedSignature(signature) + .addVerificationCert(certificate)); + + Streams.drain(decryptionStream); + decryptionStream.close(); + MessageMetadata metadata = decryptionStream.getMetadata(); + + if (metadata.hasRejectedSignatures()) { + throw metadata.getRejectedSignatures().get(0).getValidationException(); + } + } + private static InputStream getSignedData(String data) { return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)); } diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/KeyRevocationTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/KeyRevocationTest.java index 87059847..2a45b1c5 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/KeyRevocationTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/KeyRevocationTest.java @@ -8,17 +8,23 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Date; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; +import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtendWith; import org.pgpainless.PGPainless; +import org.pgpainless.decryption_verification.ConsumerOptions; +import org.pgpainless.decryption_verification.DecryptionStream; +import org.pgpainless.decryption_verification.MessageMetadata; import org.pgpainless.exception.SignatureValidationException; -import org.pgpainless.signature.consumer.CertificateValidator; +import org.pgpainless.policy.Policy; import org.pgpainless.util.TestAllImplementations; public class KeyRevocationTest { @@ -153,16 +159,16 @@ public class KeyRevocationTest { PGPSignature t2t3 = SignatureUtils.readSignatures(sigT2T3).get(0); PGPSignature t3now = SignatureUtils.readSignatures(sigT3Now).get(0); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(t0, + assertThrows(SignatureValidationException.class, () -> verify(t0, new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), publicKeys, PGPainless.getPolicy(), new Date())); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(t1t2, + assertThrows(SignatureValidationException.class, () -> verify(t1t2, new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), publicKeys, PGPainless.getPolicy(), new Date())); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(t2t3, + assertThrows(SignatureValidationException.class, () -> verify(t2t3, new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), publicKeys, PGPainless.getPolicy(), new Date())); - assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(t3now, + assertThrows(SignatureValidationException.class, () -> verify(t3now, new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), publicKeys, PGPainless.getPolicy(), new Date())); } @@ -255,8 +261,29 @@ public class KeyRevocationTest { PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key); PGPSignature signature = SignatureUtils.readSignatures(sig).get(0); - CertificateValidator.validateCertificateAndVerifyUninitializedSignature(signature, + verify(signature, new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), publicKeys, PGPainless.getPolicy(), new Date()); } + + + private void verify(PGPSignature signature, InputStream dataIn, PGPPublicKeyRing cert, Policy policy, Date validationDate) throws PGPException, IOException { + PGPainless api = PGPainless.getInstance(); + OpenPGPCertificate certificate = api.toCertificate(cert); + + DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() + .onInputStream(dataIn) + .withOptions(ConsumerOptions.get(api) + .addVerificationOfDetachedSignature(signature) + .addVerificationCert(certificate)); + + Streams.drain(decryptionStream); + decryptionStream.close(); + MessageMetadata metadata = decryptionStream.getMetadata(); + + if (metadata.hasRejectedSignatures()) { + throw metadata.getRejectedSignatures().get(0).getValidationException(); + } + } + }