1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-12-12 15:21:08 +01:00

Kotlin conversion: SignatureVerifier

This commit is contained in:
Paul Schaub 2023-10-26 12:52:04 +02:00
parent dc05a492f5
commit 19b45644ae
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
4 changed files with 596 additions and 494 deletions

View file

@ -1,36 +0,0 @@
<!--
SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
SPDX-License-Identifier: Apache-2.0
-->
# Signature Verification and Validation
This package can be a bit overwhelming, hence this README file.
Signature verification and validation in OpenPGP is a complex topic (see a
[related blog post of mine](https://blog.jabberhead.tk/2021/04/03/why-signature-verification-in-openpgp-is-hard/)),
therefore let me quickly outline some of its challenges for you:
A signature is either valid or it is not.
However, signature validity goes beyond merely checking the cryptographic correctness like BouncyCastle does.
A signature that is correct can still be invalid, e.g. if it is past its expiry date
or the key that issued the signature got revoked or is simply not a signing key in the first place.
All the little criteria like "is not expired", "has a hashed signature creation time subpacket",
"does not contain critical unknown notations/subpackets" and so forth
are implemented in the SignatureValidator class. This class defines an abstract "verify()" method
which is overwritten in a collection of anonymous subclasses which check for one or more such criteria.
Whether a signature is cryptographically correct is checked in the SignatureVerifier class.
This class draws on the SignatureValidator class to compose the subclass building blocks depending on
the signature type to check if the signature fulfills formal criteria and further checks for
cryptographic correctness.
Lastly the CertificateValidator class not only verifies single signatures, but also verifies that
the corresponding certificate (public key ring) is still valid.
It checks if the signing subkey is properly bound to its primary key, that no key in the chain is
revoked or expired and that the signing key is capable of signing in the first place.
I hope this little guide helps you to get access to the package more quickly.
Happy Hacking!

View file

@ -1,486 +0,0 @@
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.signature.consumer;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.exception.SignatureValidationException;
import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.policy.Policy;
/**
* Collection of static methods for signature verification.
* Signature verification entails validation of certain criteria (see {@link SignatureValidator}), as well as
* cryptographic verification of signature correctness.
*/
public final class SignatureVerifier {
private SignatureVerifier() {
}
/**
* Verify a signature (certification or revocation) over a user-id.
*
* @param userId user-id
* @param signature certification signature
* @param signingKey key that created the certification
* @param keyWithUserId key carrying the user-id
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if signature verification is successful
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifySignatureOverUserId(String userId, PGPSignature signature, PGPPublicKey signingKey,
PGPPublicKey keyWithUserId, Policy policy, Date referenceDate)
throws SignatureValidationException {
SignatureType type = SignatureType.valueOf(signature.getSignatureType());
switch (type) {
case GENERIC_CERTIFICATION:
case NO_CERTIFICATION:
case CASUAL_CERTIFICATION:
case POSITIVE_CERTIFICATION:
return verifyUserIdCertification(userId, signature, signingKey, keyWithUserId, policy, referenceDate);
case CERTIFICATION_REVOCATION:
return verifyUserIdRevocation(userId, signature, signingKey, keyWithUserId, policy, referenceDate);
default:
throw new SignatureValidationException("Signature is not a valid user-id certification/revocation signature: " + type);
}
}
/**
* Verify a certification self-signature over a user-id.
*
* @param userId user-id
* @param signature certification signature
* @param primaryKey primary key
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if the self-signature is verified successfully
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifyUserIdCertification(String userId, PGPSignature signature, PGPPublicKey primaryKey,
Policy policy, Date referenceDate)
throws SignatureValidationException {
return verifyUserIdCertification(userId, signature, primaryKey, primaryKey, policy, referenceDate);
}
/**
* Verify a user-id certification.
*
* @param userId user-id
* @param signature certification signature
* @param signingKey key that created the certification
* @param keyWithUserId primary key that carries the user-id
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if signature verification is successful
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifyUserIdCertification(String userId, PGPSignature signature, PGPPublicKey signingKey,
PGPPublicKey keyWithUserId, Policy policy, Date referenceDate)
throws SignatureValidationException {
SignatureValidator.wasPossiblyMadeByKey(signingKey).verify(signature);
SignatureValidator.signatureIsCertification().verify(signature);
SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature);
SignatureValidator.signatureIsEffective(referenceDate).verify(signature);
SignatureValidator.correctSignatureOverUserId(userId, keyWithUserId, signingKey).verify(signature);
return true;
}
/**
* Verify a user-id revocation self-signature.
*
* @param userId user-id
* @param signature user-id revocation signature
* @param primaryKey primary key
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if the user-id revocation signature is successfully verified
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifyUserIdRevocation(String userId, PGPSignature signature, PGPPublicKey primaryKey,
Policy policy, Date referenceDate)
throws SignatureValidationException {
return verifyUserIdRevocation(userId, signature, primaryKey, primaryKey, policy, referenceDate);
}
/**
* Verify a user-id revocation signature.
*
* @param userId user-id
* @param signature revocation signature
* @param signingKey key that created the revocation signature
* @param keyWithUserId primary key carrying the user-id
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if the user-id revocation signature is successfully verified
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifyUserIdRevocation(String userId, PGPSignature signature, PGPPublicKey signingKey,
PGPPublicKey keyWithUserId, Policy policy, Date referenceDate)
throws SignatureValidationException {
SignatureValidator.wasPossiblyMadeByKey(signingKey).verify(signature);
SignatureValidator.signatureIsOfType(SignatureType.CERTIFICATION_REVOCATION).verify(signature);
SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature);
SignatureValidator.signatureIsEffective(referenceDate).verify(signature);
SignatureValidator.correctSignatureOverUserId(userId, keyWithUserId, signingKey).verify(signature);
return true;
}
/**
* Verify a certification self-signature over a user-attributes packet.
*
* @param userAttributes user attributes
* @param signature certification self-signature
* @param primaryKey primary key that carries the user-attributes
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if the signature can be verified successfully
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifyUserAttributesCertification(PGPUserAttributeSubpacketVector userAttributes,
PGPSignature signature, PGPPublicKey primaryKey,
Policy policy, Date referenceDate)
throws SignatureValidationException {
return verifyUserAttributesCertification(userAttributes, signature, primaryKey, primaryKey, policy,
referenceDate);
}
/**
* Verify a certification signature over a user-attributes packet.
*
* @param userAttributes user attributes
* @param signature certification signature
* @param signingKey key that created the user-attributes certification
* @param keyWithUserAttributes key that carries the user-attributes certification
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if the signature can be verified successfully
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifyUserAttributesCertification(PGPUserAttributeSubpacketVector userAttributes,
PGPSignature signature, PGPPublicKey signingKey,
PGPPublicKey keyWithUserAttributes, Policy policy,
Date referenceDate)
throws SignatureValidationException {
SignatureValidator.wasPossiblyMadeByKey(signingKey).verify(signature);
SignatureValidator.signatureIsCertification().verify(signature);
SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature);
SignatureValidator.signatureIsEffective(referenceDate).verify(signature);
SignatureValidator.correctSignatureOverUserAttributes(userAttributes, keyWithUserAttributes, signingKey)
.verify(signature);
return true;
}
/**
* Verify a user-attributes revocation self-signature.
*
* @param userAttributes user-attributes
* @param signature user-attributes revocation signature
* @param primaryKey primary key that carries the user-attributes
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if the revocation signature can be verified successfully
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifyUserAttributesRevocation(PGPUserAttributeSubpacketVector userAttributes,
PGPSignature signature, PGPPublicKey primaryKey,
Policy policy, Date referenceDate)
throws SignatureValidationException {
return verifyUserAttributesRevocation(userAttributes, signature, primaryKey, primaryKey, policy, referenceDate);
}
/**
* Verify a user-attributes revocation signature.
*
* @param userAttributes user-attributes
* @param signature revocation signature
* @param signingKey revocation key
* @param keyWithUserAttributes key that carries the user-attributes
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if the revocation signature can be verified successfully
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifyUserAttributesRevocation(PGPUserAttributeSubpacketVector userAttributes,
PGPSignature signature, PGPPublicKey signingKey,
PGPPublicKey keyWithUserAttributes, Policy policy,
Date referenceDate)
throws SignatureValidationException {
SignatureValidator.wasPossiblyMadeByKey(signingKey).verify(signature);
SignatureValidator.signatureIsOfType(SignatureType.CERTIFICATION_REVOCATION).verify(signature);
SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature);
SignatureValidator.signatureIsEffective(referenceDate).verify(signature);
SignatureValidator.correctSignatureOverUserAttributes(userAttributes, keyWithUserAttributes, signingKey)
.verify(signature);
return true;
}
/**
* Verify a subkey binding signature.
*
* @param signature binding signature
* @param primaryKey primary key
* @param subkey subkey
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if the binding signature can be verified successfully
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifySubkeyBindingSignature(PGPSignature signature, PGPPublicKey primaryKey,
PGPPublicKey subkey, Policy policy, Date referenceDate)
throws SignatureValidationException {
SignatureValidator.signatureIsOfType(SignatureType.SUBKEY_BINDING).verify(signature);
SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature);
SignatureValidator.signatureDoesNotPredateSignee(subkey).verify(signature);
SignatureValidator.signatureIsEffective(referenceDate).verify(signature);
SignatureValidator.hasValidPrimaryKeyBindingSignatureIfRequired(primaryKey, subkey, policy, referenceDate)
.verify(signature);
SignatureValidator.correctSubkeyBindingSignature(primaryKey, subkey).verify(signature);
return true;
}
/**
* Verify a subkey revocation signature.
*
* @param signature subkey revocation signature
* @param primaryKey primary key
* @param subkey subkey
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if the subkey revocation signature can be verified successfully
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifySubkeyBindingRevocation(PGPSignature signature, PGPPublicKey primaryKey,
PGPPublicKey subkey, Policy policy, Date referenceDate)
throws SignatureValidationException {
SignatureValidator.signatureIsOfType(SignatureType.SUBKEY_REVOCATION).verify(signature);
SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature);
SignatureValidator.signatureDoesNotPredateSignee(subkey).verify(signature);
SignatureValidator.signatureIsEffective(referenceDate).verify(signature);
SignatureValidator.correctSignatureOverKey(primaryKey, subkey).verify(signature);
return true;
}
/**
* Verify a direct-key self-signature.
*
* @param signature signature
* @param primaryKey primary key
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if the signature can be verified successfully
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifyDirectKeySignature(PGPSignature signature, PGPPublicKey primaryKey,
Policy policy, Date referenceDate)
throws SignatureValidationException {
return verifyDirectKeySignature(signature, primaryKey, primaryKey, policy, referenceDate);
}
/**
* Verify a direct-key signature.
*
* @param signature signature
* @param signingKey signing key
* @param signedKey signed key
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if signature verification is successful
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifyDirectKeySignature(PGPSignature signature, PGPPublicKey signingKey,
PGPPublicKey signedKey, Policy policy, Date referenceDate)
throws SignatureValidationException {
SignatureValidator.signatureIsOfType(SignatureType.DIRECT_KEY).verify(signature);
SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature);
SignatureValidator.signatureDoesNotPredateSignee(signedKey).verify(signature);
SignatureValidator.signatureIsEffective(referenceDate).verify(signature);
SignatureValidator.correctSignatureOverKey(signingKey, signedKey).verify(signature);
return true;
}
/**
* Verify a key revocation signature.
*
* @param signature signature
* @param primaryKey primary key
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if signature verification is successful
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifyKeyRevocationSignature(PGPSignature signature, PGPPublicKey primaryKey,
Policy policy, Date referenceDate)
throws SignatureValidationException {
SignatureValidator.signatureIsOfType(SignatureType.KEY_REVOCATION).verify(signature);
SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature);
SignatureValidator.signatureIsEffective(referenceDate).verify(signature);
SignatureValidator.correctSignatureOverKey(primaryKey, primaryKey).verify(signature);
return true;
}
/**
* Initialize a signature and verify it afterwards by updating it with the signed data.
*
* @param signature OpenPGP signature
* @param signedData input stream containing the signed data
* @param signingKey the key that created the signature
* @param policy policy
* @param referenceDate reference date of signature verification
* @return true if the signature is successfully verified
*
* @throws SignatureValidationException if the signature verification fails for some reason
*/
public static boolean verifyUninitializedSignature(PGPSignature signature, InputStream signedData,
PGPPublicKey signingKey, Policy policy, Date referenceDate)
throws SignatureValidationException {
initializeSignatureAndUpdateWithSignedData(signature, signedData, signingKey);
return verifyInitializedSignature(signature, signingKey, policy, referenceDate);
}
/**
* Initialize a signature and then update it with the signed data from the given {@link InputStream}.
*
* @param signature OpenPGP signature
* @param signedData input stream containing signed data
* @param signingKey key that created the signature
*
* @throws SignatureValidationException in case the signature cannot be verified for some reason
*/
public static void initializeSignatureAndUpdateWithSignedData(PGPSignature signature, InputStream signedData,
PGPPublicKey signingKey)
throws SignatureValidationException {
try {
signature.init(ImplementationFactory.getInstance().getPgpContentVerifierBuilderProvider(), signingKey);
int read;
byte[] buf = new byte[8192];
byte lastByte = -1;
while ((read = signedData.read(buf)) != -1) {
// If we previously omitted a newline, but the stream is not yet empty, add it now
if (lastByte == (byte) '\n') {
signature.update(lastByte);
}
lastByte = buf[read - 1];
if (lastByte == (byte) '\n') {
// if last byte in buffer is newline, omit it for now
signature.update(buf, 0, read - 1);
} else {
// otherwise, write buffer as usual
signature.update(buf, 0, read);
}
}
} catch (PGPException e) {
throw new SignatureValidationException("Cannot init signature.", e);
} catch (IOException e) {
throw new SignatureValidationException("Cannot update signature.", e);
}
}
/**
* Verify an initialized signature.
* An initialized signature was already updated with the signed data.
*
* @param signature OpenPGP signature
* @param signingKey key that created the signature
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if signature is verified successfully
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifyInitializedSignature(PGPSignature signature, PGPPublicKey signingKey, Policy policy,
Date referenceDate)
throws SignatureValidationException {
SignatureValidator.wasPossiblyMadeByKey(signingKey).verify(signature);
SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature);
SignatureValidator.signatureIsEffective(referenceDate).verify(signature);
try {
if (!signature.verify()) {
throw new SignatureValidationException("Signature is not correct.");
}
return true;
} catch (PGPException e) {
throw new SignatureValidationException("Could not verify signature correctness.", e);
}
}
public static boolean verifyOnePassSignature(PGPSignature signature, PGPPublicKey signingKey,
OnePassSignatureCheck onePassSignature, Policy policy)
throws SignatureValidationException {
try {
SignatureValidator.wasPossiblyMadeByKey(signingKey).verify(signature);
SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature);
SignatureValidator.signatureIsEffective().verify(signature);
} catch (SignatureValidationException e) {
throw new SignatureValidationException("Signature is not valid: " + e.getMessage(), e);
}
try {
if (onePassSignature.getSignature() == null) {
throw new IllegalStateException("No comparison signature provided.");
}
if (!onePassSignature.getOnePassSignature().verify(signature)) {
throw new SignatureValidationException("Bad signature of key " +
Long.toHexString(signingKey.getKeyID()));
}
} catch (PGPException e) {
throw new SignatureValidationException("Could not verify correctness of One-Pass-Signature: " +
e.getMessage(), e);
}
return true;
}
/**
* Verify a signature (certification or revocation) over a user-id.
*
* @param userId user-id
* @param signature self-signature
* @param primaryKey primary key that created the signature
* @param policy policy
* @param referenceDate reference date for signature verification
* @return true if the signature is successfully verified
*
* @throws SignatureValidationException if signature verification fails for some reason
*/
public static boolean verifySignatureOverUserId(String userId, PGPSignature signature, PGPPublicKey primaryKey,
Policy policy, Date referenceDate)
throws SignatureValidationException {
return verifySignatureOverUserId(userId, signature, primaryKey, primaryKey, policy, referenceDate);
}
}

View file

@ -1,8 +0,0 @@
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
/**
* Classes related to OpenPGP signature verification.
*/
package org.pgpainless.signature.consumer;