mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-23 03:17:58 +01:00
Kotlin conversion: SignaturePicker
This commit is contained in:
parent
841b386226
commit
6b397a0d56
3 changed files with 314 additions and 395 deletions
|
@ -1,395 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package org.pgpainless.signature.consumer;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
|
||||||
import org.pgpainless.algorithm.SignatureType;
|
|
||||||
import org.pgpainless.exception.SignatureValidationException;
|
|
||||||
import org.pgpainless.policy.Policy;
|
|
||||||
import org.pgpainless.signature.SignatureUtils;
|
|
||||||
import org.pgpainless.util.CollectionUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick signatures from keys.
|
|
||||||
*
|
|
||||||
* The format of a V4 OpenPGP key is:
|
|
||||||
*
|
|
||||||
* Primary-Key
|
|
||||||
* [Revocation Self Signature]
|
|
||||||
* [Direct Key Signature...]
|
|
||||||
* User ID [Signature ...]
|
|
||||||
* [User ID [Signature ...] ...]
|
|
||||||
* [User Attribute [Signature ...] ...]
|
|
||||||
* [[Subkey [Binding-Signature-Revocation] Primary-Key-Binding-Signature] ...]
|
|
||||||
*/
|
|
||||||
public final class SignaturePicker {
|
|
||||||
|
|
||||||
private SignaturePicker() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick the at validation date most recent valid key revocation signature.
|
|
||||||
* If there are hard revocation signatures, the latest hard revocation sig is picked, even if it was created after
|
|
||||||
* validationDate or if it is already expired.
|
|
||||||
*
|
|
||||||
* @param keyRing key ring
|
|
||||||
* @param policy policy
|
|
||||||
* @param validationDate date of signature validation
|
|
||||||
* @return most recent, valid key revocation signature
|
|
||||||
*/
|
|
||||||
public static PGPSignature pickCurrentRevocationSelfSignature(PGPKeyRing keyRing, Policy policy, Date validationDate) {
|
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
|
||||||
|
|
||||||
List<PGPSignature> signatures = getSortedSignaturesOfType(primaryKey, SignatureType.KEY_REVOCATION);
|
|
||||||
PGPSignature mostCurrentValidSig = null;
|
|
||||||
|
|
||||||
for (PGPSignature signature : signatures) {
|
|
||||||
try {
|
|
||||||
SignatureVerifier.verifyKeyRevocationSignature(signature, primaryKey, policy, validationDate);
|
|
||||||
} catch (SignatureValidationException e) {
|
|
||||||
// Signature is not valid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mostCurrentValidSig = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mostCurrentValidSig;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick the at validationDate most recent, valid direct key signature.
|
|
||||||
* This method might return null, if there is no direct key self-signature which is valid at validationDate.
|
|
||||||
*
|
|
||||||
* @param keyRing key ring
|
|
||||||
* @param policy policy
|
|
||||||
* @param validationDate validation date
|
|
||||||
* @return direct-key self-signature
|
|
||||||
*/
|
|
||||||
public static PGPSignature pickCurrentDirectKeySelfSignature(PGPKeyRing keyRing, Policy policy, Date validationDate) {
|
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
|
||||||
return pickCurrentDirectKeySignature(primaryKey, primaryKey, policy, validationDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick the at validationDate, latest, valid direct key signature made by signingKey on signedKey.
|
|
||||||
* This method might return null, if there is no direct key self signature which is valid at validationDate.
|
|
||||||
*
|
|
||||||
* @param signingKey key that created the signature
|
|
||||||
* @param signedKey key that carries the signature
|
|
||||||
* @param policy policy
|
|
||||||
* @param validationDate validation date
|
|
||||||
* @return direct key sig
|
|
||||||
*/
|
|
||||||
public static PGPSignature pickCurrentDirectKeySignature(PGPPublicKey signingKey, PGPPublicKey signedKey, Policy policy, Date validationDate) {
|
|
||||||
List<PGPSignature> directKeySignatures = getSortedSignaturesOfType(signedKey, SignatureType.DIRECT_KEY);
|
|
||||||
|
|
||||||
PGPSignature mostRecentDirectKeySigBySigningKey = null;
|
|
||||||
for (PGPSignature signature : directKeySignatures) {
|
|
||||||
try {
|
|
||||||
SignatureVerifier.verifyDirectKeySignature(signature, signingKey, signedKey, policy, validationDate);
|
|
||||||
} catch (SignatureValidationException e) {
|
|
||||||
// Direct key sig is not valid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mostRecentDirectKeySigBySigningKey = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mostRecentDirectKeySigBySigningKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick the at validationDate latest direct key signature.
|
|
||||||
* This method might return an expired signature.
|
|
||||||
* If there are more than one direct-key signature, and some of those are not expired, the latest non-expired
|
|
||||||
* yet already effective direct-key signature will be returned.
|
|
||||||
*
|
|
||||||
* @param keyRing key ring
|
|
||||||
* @param policy policy
|
|
||||||
* @param validationDate validation date
|
|
||||||
* @return latest direct key signature
|
|
||||||
*/
|
|
||||||
public static PGPSignature pickLatestDirectKeySignature(PGPKeyRing keyRing, Policy policy, Date validationDate) {
|
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
|
||||||
return pickLatestDirectKeySignature(primaryKey, primaryKey, policy, validationDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick the at validationDate latest direct key signature made by signingKey on signedKey.
|
|
||||||
* This method might return an expired signature.
|
|
||||||
* If a non-expired direct-key signature exists, the latest non-expired yet already effective direct-key
|
|
||||||
* signature will be returned.
|
|
||||||
*
|
|
||||||
* @param signingKey signing key (key that made the sig)
|
|
||||||
* @param signedKey signed key (key that carries the sig)
|
|
||||||
* @param policy policy
|
|
||||||
* @param validationDate date of validation
|
|
||||||
* @return latest direct key sig
|
|
||||||
*/
|
|
||||||
public static PGPSignature pickLatestDirectKeySignature(PGPPublicKey signingKey, PGPPublicKey signedKey, Policy policy, Date validationDate) {
|
|
||||||
List<PGPSignature> signatures = getSortedSignaturesOfType(signedKey, SignatureType.DIRECT_KEY);
|
|
||||||
|
|
||||||
PGPSignature latestDirectKeySignature = null;
|
|
||||||
for (PGPSignature signature : signatures) {
|
|
||||||
try {
|
|
||||||
SignatureValidator.signatureIsOfType(SignatureType.DIRECT_KEY).verify(signature);
|
|
||||||
SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature);
|
|
||||||
SignatureValidator.signatureIsAlreadyEffective(validationDate).verify(signature);
|
|
||||||
// if the currently latest signature is not yet expired, check if the next candidate is not yet expired
|
|
||||||
if (latestDirectKeySignature != null && !SignatureUtils.isSignatureExpired(latestDirectKeySignature, validationDate)) {
|
|
||||||
SignatureValidator.signatureIsNotYetExpired(validationDate).verify(signature);
|
|
||||||
}
|
|
||||||
SignatureValidator.correctSignatureOverKey(signingKey, signedKey).verify(signature);
|
|
||||||
} catch (SignatureValidationException e) {
|
|
||||||
// Direct key signature is not valid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
latestDirectKeySignature = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return latestDirectKeySignature;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick the at validationDate most recent, valid user-id revocation signature.
|
|
||||||
* If there are hard revocation signatures, the latest hard revocation sig is picked, even if it was created after
|
|
||||||
* validationDate or if it is already expired.
|
|
||||||
*
|
|
||||||
* @param keyRing key ring
|
|
||||||
* @param userId user-Id that gets revoked
|
|
||||||
* @param policy policy
|
|
||||||
* @param validationDate validation date
|
|
||||||
* @return revocation signature
|
|
||||||
*/
|
|
||||||
public static PGPSignature pickCurrentUserIdRevocationSignature(PGPKeyRing keyRing, String userId, Policy policy, Date validationDate) {
|
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
|
||||||
List<PGPSignature> signatures = getSortedSignaturesOfType(primaryKey, SignatureType.CERTIFICATION_REVOCATION);
|
|
||||||
|
|
||||||
PGPSignature latestUserIdRevocation = null;
|
|
||||||
for (PGPSignature signature : signatures) {
|
|
||||||
PGPPublicKey signer = keyRing.getPublicKey(signature.getKeyID());
|
|
||||||
if (signer == null) {
|
|
||||||
// Signature made by external key. Skip.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
SignatureVerifier.verifyUserIdRevocation(userId, signature, primaryKey, policy, validationDate);
|
|
||||||
} catch (SignatureValidationException e) {
|
|
||||||
// User-id revocation is not valid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
latestUserIdRevocation = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return latestUserIdRevocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick the at validationDate latest, valid certification self-signature for the given user-id.
|
|
||||||
* This method might return null, if there is no certification self signature for that user-id which is valid
|
|
||||||
* at validationDate.
|
|
||||||
*
|
|
||||||
* @param keyRing keyring
|
|
||||||
* @param userId userid
|
|
||||||
* @param policy policy
|
|
||||||
* @param validationDate validation date
|
|
||||||
* @return user-id certification
|
|
||||||
*/
|
|
||||||
public static PGPSignature pickCurrentUserIdCertificationSignature(PGPKeyRing keyRing, String userId, Policy policy, Date validationDate) {
|
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
|
||||||
|
|
||||||
Iterator<PGPSignature> userIdSigIterator = primaryKey.getSignaturesForID(userId);
|
|
||||||
List<PGPSignature> signatures = CollectionUtils.iteratorToList(userIdSigIterator);
|
|
||||||
|
|
||||||
Collections.sort(signatures, new SignatureCreationDateComparator());
|
|
||||||
|
|
||||||
PGPSignature mostRecentUserIdCertification = null;
|
|
||||||
for (PGPSignature signature : signatures) {
|
|
||||||
if (primaryKey.getKeyID() != signature.getKeyID()) {
|
|
||||||
// Signature not made by primary key
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
SignatureVerifier.verifyUserIdCertification(userId, signature, primaryKey, policy, validationDate);
|
|
||||||
} catch (SignatureValidationException e) {
|
|
||||||
// User-id certification is not valid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mostRecentUserIdCertification = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mostRecentUserIdCertification;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick the at validationDate latest certification self-signature for the given user-id.
|
|
||||||
* This method might return an expired signature.
|
|
||||||
* If a non-expired user-id certification signature exists, the latest non-expired yet already effective
|
|
||||||
* user-id certification signature for the given user-id will be returned.
|
|
||||||
*
|
|
||||||
* @param keyRing keyring
|
|
||||||
* @param userId userid
|
|
||||||
* @param policy policy
|
|
||||||
* @param validationDate validation date
|
|
||||||
* @return user-id certification
|
|
||||||
*/
|
|
||||||
public static PGPSignature pickLatestUserIdCertificationSignature(PGPKeyRing keyRing, String userId, Policy policy, Date validationDate) {
|
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
|
||||||
|
|
||||||
Iterator<PGPSignature> userIdSigIterator = primaryKey.getSignaturesForID(userId);
|
|
||||||
List<PGPSignature> signatures = CollectionUtils.iteratorToList(userIdSigIterator);
|
|
||||||
Collections.sort(signatures, new SignatureCreationDateComparator());
|
|
||||||
|
|
||||||
PGPSignature latestUserIdCert = null;
|
|
||||||
for (PGPSignature signature : signatures) {
|
|
||||||
try {
|
|
||||||
SignatureValidator.wasPossiblyMadeByKey(primaryKey).verify(signature);
|
|
||||||
SignatureValidator.signatureIsCertification().verify(signature);
|
|
||||||
SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature);
|
|
||||||
SignatureValidator.signatureIsAlreadyEffective(validationDate).verify(signature);
|
|
||||||
SignatureValidator.correctSignatureOverUserId(userId, primaryKey, primaryKey).verify(signature);
|
|
||||||
} catch (SignatureValidationException e) {
|
|
||||||
// User-id certification is not valid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
latestUserIdCert = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return latestUserIdCert;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick the at validationDate most recent, valid subkey revocation signature.
|
|
||||||
* If there are hard revocation signatures, the latest hard revocation sig is picked, even if it was created after
|
|
||||||
* validationDate or if it is already expired.
|
|
||||||
*
|
|
||||||
* @param keyRing keyring
|
|
||||||
* @param subkey subkey
|
|
||||||
* @param policy policy
|
|
||||||
* @param validationDate validation date
|
|
||||||
* @return subkey revocation signature
|
|
||||||
*/
|
|
||||||
public static PGPSignature pickCurrentSubkeyBindingRevocationSignature(PGPKeyRing keyRing, PGPPublicKey subkey, Policy policy, Date validationDate) {
|
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
|
||||||
if (primaryKey.getKeyID() == subkey.getKeyID()) {
|
|
||||||
throw new IllegalArgumentException("Primary key cannot have subkey binding revocations.");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<PGPSignature> signatures = getSortedSignaturesOfType(subkey, SignatureType.SUBKEY_REVOCATION);
|
|
||||||
PGPSignature latestSubkeyRevocation = null;
|
|
||||||
|
|
||||||
for (PGPSignature signature : signatures) {
|
|
||||||
try {
|
|
||||||
SignatureVerifier.verifySubkeyBindingRevocation(signature, primaryKey, subkey, policy, validationDate);
|
|
||||||
} catch (SignatureValidationException e) {
|
|
||||||
// subkey binding revocation is not valid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
latestSubkeyRevocation = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return latestSubkeyRevocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick the at validationDate latest, valid subkey binding signature for the given subkey.
|
|
||||||
* This method might return null, if there is no subkey binding signature which is valid
|
|
||||||
* at validationDate.
|
|
||||||
*
|
|
||||||
* @param keyRing key ring
|
|
||||||
* @param subkey subkey
|
|
||||||
* @param policy policy
|
|
||||||
* @param validationDate date of validation
|
|
||||||
* @return most recent valid subkey binding signature
|
|
||||||
*/
|
|
||||||
public static PGPSignature pickCurrentSubkeyBindingSignature(PGPKeyRing keyRing, PGPPublicKey subkey, Policy policy, Date validationDate) {
|
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
|
||||||
if (primaryKey.getKeyID() == subkey.getKeyID()) {
|
|
||||||
throw new IllegalArgumentException("Primary key cannot have subkey binding signature.");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<PGPSignature> subkeyBindingSigs = getSortedSignaturesOfType(subkey, SignatureType.SUBKEY_BINDING);
|
|
||||||
PGPSignature mostCurrentValidSig = null;
|
|
||||||
|
|
||||||
for (PGPSignature signature : subkeyBindingSigs) {
|
|
||||||
try {
|
|
||||||
SignatureVerifier.verifySubkeyBindingSignature(signature, primaryKey, subkey, policy, validationDate);
|
|
||||||
} catch (SignatureValidationException validationException) {
|
|
||||||
// Subkey binding sig is not valid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mostCurrentValidSig = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mostCurrentValidSig;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pick the at validationDate latest subkey binding signature for the given subkey.
|
|
||||||
* This method might return an expired signature.
|
|
||||||
* If a non-expired subkey binding signature exists, the latest non-expired yet already effective
|
|
||||||
* subkey binding signature for the given subkey will be returned.
|
|
||||||
*
|
|
||||||
* @param keyRing key ring
|
|
||||||
* @param subkey subkey
|
|
||||||
* @param policy policy
|
|
||||||
* @param validationDate validationDate
|
|
||||||
* @return subkey binding signature
|
|
||||||
*/
|
|
||||||
public static PGPSignature pickLatestSubkeyBindingSignature(PGPKeyRing keyRing, PGPPublicKey subkey, Policy policy, Date validationDate) {
|
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
|
||||||
if (primaryKey.getKeyID() == subkey.getKeyID()) {
|
|
||||||
throw new IllegalArgumentException("Primary key cannot have subkey binding signature.");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<PGPSignature> signatures = getSortedSignaturesOfType(subkey, SignatureType.SUBKEY_BINDING);
|
|
||||||
PGPSignature latestSubkeyBinding = null;
|
|
||||||
|
|
||||||
for (PGPSignature signature : signatures) {
|
|
||||||
try {
|
|
||||||
SignatureValidator.signatureIsOfType(SignatureType.SUBKEY_BINDING).verify(signature);
|
|
||||||
SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature);
|
|
||||||
SignatureValidator.signatureDoesNotPredateSignee(subkey).verify(signature);
|
|
||||||
SignatureValidator.signatureIsAlreadyEffective(validationDate).verify(signature);
|
|
||||||
// if the currently latest signature is not yet expired, check if the next candidate is not yet expired
|
|
||||||
if (latestSubkeyBinding != null && !SignatureUtils.isSignatureExpired(latestSubkeyBinding, validationDate)) {
|
|
||||||
SignatureValidator.signatureIsNotYetExpired(validationDate).verify(signature);
|
|
||||||
}
|
|
||||||
SignatureValidator.correctSubkeyBindingSignature(primaryKey, subkey).verify(signature);
|
|
||||||
} catch (SignatureValidationException e) {
|
|
||||||
// Subkey binding sig is not valid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
latestSubkeyBinding = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return latestSubkeyBinding;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a list of all signatures of the given {@link SignatureType} on the given key, sorted using a
|
|
||||||
* {@link SignatureCreationDateComparator}.
|
|
||||||
*
|
|
||||||
* The returned list will be sorted first by ascending signature creation time.
|
|
||||||
*
|
|
||||||
* @param key key
|
|
||||||
* @param type type of signatures which shall be collected and sorted
|
|
||||||
* @return sorted list of signatures
|
|
||||||
*/
|
|
||||||
private static List<PGPSignature> getSortedSignaturesOfType(PGPPublicKey key, SignatureType type) {
|
|
||||||
Iterator<PGPSignature> signaturesOfType = key.getSignaturesOfType(type.getCode());
|
|
||||||
List<PGPSignature> signatureList = CollectionUtils.iteratorToList(signaturesOfType);
|
|
||||||
Collections.sort(signatureList, new SignatureCreationDateComparator());
|
|
||||||
return signatureList;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,6 +5,7 @@
|
||||||
package org.bouncycastle.extensions
|
package org.bouncycastle.extensions
|
||||||
|
|
||||||
import openpgp.plusSeconds
|
import openpgp.plusSeconds
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKey
|
||||||
import org.bouncycastle.openpgp.PGPSignature
|
import org.bouncycastle.openpgp.PGPSignature
|
||||||
import org.pgpainless.algorithm.RevocationState
|
import org.pgpainless.algorithm.RevocationState
|
||||||
import org.pgpainless.algorithm.SignatureType
|
import org.pgpainless.algorithm.SignatureType
|
||||||
|
@ -69,6 +70,9 @@ fun PGPSignature.wasIssuedBy(fingerprint: ByteArray): Boolean =
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun PGPSignature.wasIssuedBy(key: PGPPublicKey): Boolean =
|
||||||
|
wasIssuedBy(OpenPgpFingerprint.of(key))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true, if this signature is a hard revocation.
|
* Return true, if this signature is a hard revocation.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,310 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.signature.consumer
|
||||||
|
|
||||||
|
import org.bouncycastle.extensions.getPublicKeyFor
|
||||||
|
import org.bouncycastle.extensions.hasPublicKey
|
||||||
|
import org.bouncycastle.extensions.isExpired
|
||||||
|
import org.bouncycastle.extensions.wasIssuedBy
|
||||||
|
import org.bouncycastle.openpgp.PGPKeyRing
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKey
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature
|
||||||
|
import org.pgpainless.algorithm.SignatureType
|
||||||
|
import org.pgpainless.exception.SignatureValidationException
|
||||||
|
import org.pgpainless.policy.Policy
|
||||||
|
import java.util.Date
|
||||||
|
import kotlin.math.sign
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick signatures from keys.
|
||||||
|
*
|
||||||
|
* The format of a V4 OpenPGP key is:
|
||||||
|
*
|
||||||
|
* Primary-Key
|
||||||
|
* [Revocation Self Signature]
|
||||||
|
* [Direct Key Signature...]
|
||||||
|
* User ID [Signature ...]
|
||||||
|
* [User ID [Signature ...] ...]
|
||||||
|
* [User Attribute [Signature ...] ...]
|
||||||
|
* [[Subkey [Binding-Signature-Revocation] Primary-Key-Binding-Signature] ...]
|
||||||
|
*/
|
||||||
|
class SignaturePicker {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the at validation date most recent valid key revocation signature.
|
||||||
|
* If there are hard revocation signatures, the latest hard revocation sig is picked, even if it was created after
|
||||||
|
* validationDate or if it is already expired.
|
||||||
|
*
|
||||||
|
* @param keyRing key ring
|
||||||
|
* @param policy policy
|
||||||
|
* @param referenceTime date of signature validation
|
||||||
|
* @return most recent, valid key revocation signature
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun pickCurrentRevocationSelfSignature(keyRing: PGPKeyRing, policy: Policy, referenceTime: Date): PGPSignature? {
|
||||||
|
val primaryKey = keyRing.publicKey
|
||||||
|
return getSortedSignaturesOfType(primaryKey, SignatureType.KEY_REVOCATION).lastOrNull {
|
||||||
|
return@lastOrNull try {
|
||||||
|
SignatureVerifier.verifyKeyRevocationSignature(it, primaryKey, policy, referenceTime)
|
||||||
|
true // valid
|
||||||
|
} catch (e : SignatureValidationException) {
|
||||||
|
false // not valid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the at validationDate most recent, valid direct key signature.
|
||||||
|
* This method might return null, if there is no direct key self-signature which is valid at validationDate.
|
||||||
|
*
|
||||||
|
* @param keyRing key ring
|
||||||
|
* @param policy policy
|
||||||
|
* @param referenceTime validation date
|
||||||
|
* @return direct-key self-signature
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun pickCurrentDirectKeySelfSignature(keyRing: PGPKeyRing, policy: Policy, referenceTime: Date): PGPSignature? {
|
||||||
|
val primaryKey = keyRing.publicKey
|
||||||
|
return pickCurrentDirectKeySignature(primaryKey, primaryKey, policy, referenceTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun pickCurrentDirectKeySignature(signingKey: PGPPublicKey, signedKey: PGPPublicKey, policy: Policy, referenceTime: Date): PGPSignature? {
|
||||||
|
return getSortedSignaturesOfType(signedKey, SignatureType.DIRECT_KEY).lastOrNull {
|
||||||
|
return@lastOrNull try {
|
||||||
|
SignatureVerifier.verifyDirectKeySignature(it, signingKey, signedKey, policy, referenceTime)
|
||||||
|
true
|
||||||
|
} catch (e : SignatureValidationException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the at validationDate latest direct key signature.
|
||||||
|
* This method might return an expired signature.
|
||||||
|
* If there are more than one direct-key signature, and some of those are not expired, the latest non-expired
|
||||||
|
* yet already effective direct-key signature will be returned.
|
||||||
|
*
|
||||||
|
* @param keyRing key ring
|
||||||
|
* @param policy policy
|
||||||
|
* @param referenceTime validation date
|
||||||
|
* @return latest direct key signature
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun pickLatestDirectKeySignature(keyRing: PGPKeyRing, policy: Policy, referenceTime: Date): PGPSignature? {
|
||||||
|
return pickLatestDirectKeySignature(keyRing.publicKey, keyRing.publicKey, policy, referenceTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the at validationDate latest direct key signature made by signingKey on signedKey.
|
||||||
|
* This method might return an expired signature.
|
||||||
|
* If a non-expired direct-key signature exists, the latest non-expired yet already effective direct-key
|
||||||
|
* signature will be returned.
|
||||||
|
*
|
||||||
|
* @param signingKey signing key (key that made the sig)
|
||||||
|
* @param signedKey signed key (key that carries the sig)
|
||||||
|
* @param policy policy
|
||||||
|
* @param referenceTime date of validation
|
||||||
|
* @return latest direct key sig
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun pickLatestDirectKeySignature(signingKey: PGPPublicKey, signedKey: PGPPublicKey, policy: Policy, referenceTime: Date): PGPSignature? {
|
||||||
|
var latest: PGPSignature? = null
|
||||||
|
return getSortedSignaturesOfType(signedKey, SignatureType.DIRECT_KEY).lastOrNull {
|
||||||
|
try {
|
||||||
|
SignatureValidator.signatureIsOfType(SignatureType.DIRECT_KEY).verify(it)
|
||||||
|
SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(it)
|
||||||
|
SignatureValidator.signatureIsAlreadyEffective(referenceTime).verify(it)
|
||||||
|
if (latest != null && !latest!!.isExpired(referenceTime)) {
|
||||||
|
SignatureValidator.signatureIsNotYetExpired(referenceTime).verify(it)
|
||||||
|
}
|
||||||
|
SignatureValidator.correctSignatureOverKey(signingKey, signedKey).verify(it)
|
||||||
|
latest = it
|
||||||
|
true
|
||||||
|
} catch (e : SignatureValidationException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the at validationDate most recent, valid user-id revocation signature.
|
||||||
|
* If there are hard revocation signatures, the latest hard revocation sig is picked, even if it was created after
|
||||||
|
* validationDate or if it is already expired.
|
||||||
|
*
|
||||||
|
* @param keyRing key ring
|
||||||
|
* @param userId user-Id that gets revoked
|
||||||
|
* @param policy policy
|
||||||
|
* @param referenceTime validation date
|
||||||
|
* @return revocation signature
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun pickCurrentUserIdRevocationSignature(keyRing: PGPKeyRing, userId: CharSequence, policy: Policy, referenceTime: Date): PGPSignature? {
|
||||||
|
val primaryKey = keyRing.publicKey
|
||||||
|
return getSortedSignaturesOfType(primaryKey, SignatureType.CERTIFICATION_REVOCATION).lastOrNull {
|
||||||
|
keyRing.getPublicKeyFor(it) ?: return@lastOrNull false // signature made by external key. skip.
|
||||||
|
return@lastOrNull try {
|
||||||
|
SignatureVerifier.verifyUserIdRevocation(userId.toString(), it, primaryKey, policy, referenceTime)
|
||||||
|
true
|
||||||
|
} catch (e : SignatureValidationException) {
|
||||||
|
false // signature not valid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the at validationDate latest, valid certification self-signature for the given user-id.
|
||||||
|
* This method might return null, if there is no certification self signature for that user-id which is valid
|
||||||
|
* at validationDate.
|
||||||
|
*
|
||||||
|
* @param keyRing keyring
|
||||||
|
* @param userId userid
|
||||||
|
* @param policy policy
|
||||||
|
* @param referenceTime validation date
|
||||||
|
* @return user-id certification
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun pickCurrentUserIdCertificationSignature(keyRing: PGPKeyRing, userId: CharSequence, policy: Policy, referenceTime: Date): PGPSignature? {
|
||||||
|
val primaryKey = keyRing.publicKey
|
||||||
|
return primaryKey.getSignaturesForID(userId.toString()).asSequence()
|
||||||
|
.sortedWith(SignatureCreationDateComparator())
|
||||||
|
.lastOrNull {
|
||||||
|
return@lastOrNull it.wasIssuedBy(primaryKey) && try {
|
||||||
|
SignatureVerifier.verifyUserIdCertification(userId.toString(), it, primaryKey, policy, referenceTime)
|
||||||
|
true
|
||||||
|
} catch (e : SignatureValidationException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the at validationDate latest certification self-signature for the given user-id.
|
||||||
|
* This method might return an expired signature.
|
||||||
|
* If a non-expired user-id certification signature exists, the latest non-expired yet already effective
|
||||||
|
* user-id certification signature for the given user-id will be returned.
|
||||||
|
*
|
||||||
|
* @param keyRing keyring
|
||||||
|
* @param userId userid
|
||||||
|
* @param policy policy
|
||||||
|
* @param referenceTime validation date
|
||||||
|
* @return user-id certification
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun pickLatestUserIdCertificationSignature(keyRing: PGPKeyRing, userId: CharSequence, policy: Policy, referenceTime: Date): PGPSignature? {
|
||||||
|
val primaryKey = keyRing.publicKey
|
||||||
|
return primaryKey.getSignaturesForID(userId.toString()).asSequence()
|
||||||
|
.sortedWith(SignatureCreationDateComparator())
|
||||||
|
.lastOrNull {
|
||||||
|
return@lastOrNull try {
|
||||||
|
SignatureValidator.wasPossiblyMadeByKey(primaryKey).verify(it)
|
||||||
|
SignatureValidator.signatureIsCertification().verify(it)
|
||||||
|
SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(it)
|
||||||
|
SignatureValidator.signatureIsAlreadyEffective(referenceTime).verify(it)
|
||||||
|
SignatureValidator.correctSignatureOverUserId(userId.toString(), primaryKey, primaryKey).verify(it)
|
||||||
|
true
|
||||||
|
} catch (e : SignatureValidationException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the at validationDate most recent, valid subkey revocation signature.
|
||||||
|
* If there are hard revocation signatures, the latest hard revocation sig is picked, even if it was created after
|
||||||
|
* validationDate or if it is already expired.
|
||||||
|
*
|
||||||
|
* @param keyRing keyring
|
||||||
|
* @param subkey subkey
|
||||||
|
* @param policy policy
|
||||||
|
* @param referenceTime validation date
|
||||||
|
* @return subkey revocation signature
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun pickCurrentSubkeyBindingRevocationSignature(keyRing: PGPKeyRing, subkey: PGPPublicKey, policy: Policy, referenceTime: Date): PGPSignature? {
|
||||||
|
val primaryKey = keyRing.publicKey
|
||||||
|
require(primaryKey.keyID != subkey.keyID) { "Primary key cannot have subkey binding revocations." }
|
||||||
|
return getSortedSignaturesOfType(subkey, SignatureType.SUBKEY_REVOCATION).lastOrNull {
|
||||||
|
return@lastOrNull try {
|
||||||
|
SignatureVerifier.verifySubkeyBindingRevocation(it, primaryKey, subkey, policy, referenceTime)
|
||||||
|
true
|
||||||
|
} catch (e : SignatureValidationException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the at validationDate latest, valid subkey binding signature for the given subkey.
|
||||||
|
* This method might return null, if there is no subkey binding signature which is valid
|
||||||
|
* at validationDate.
|
||||||
|
*
|
||||||
|
* @param keyRing key ring
|
||||||
|
* @param subkey subkey
|
||||||
|
* @param policy policy
|
||||||
|
* @param referenceTime date of validation
|
||||||
|
* @return most recent valid subkey binding signature
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun pickCurrentSubkeyBindingSignature(keyRing: PGPKeyRing, subkey: PGPPublicKey, policy: Policy, referenceTime: Date): PGPSignature? {
|
||||||
|
val primaryKey = keyRing.publicKey
|
||||||
|
require(primaryKey.keyID != subkey.keyID) { "Primary key cannot have subkey binding signatures." }
|
||||||
|
return getSortedSignaturesOfType(subkey, SignatureType.SUBKEY_BINDING).lastOrNull {
|
||||||
|
return@lastOrNull try {
|
||||||
|
SignatureVerifier.verifySubkeyBindingSignature(it, primaryKey, subkey, policy, referenceTime)
|
||||||
|
true
|
||||||
|
} catch (e : SignatureValidationException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the at validationDate latest subkey binding signature for the given subkey.
|
||||||
|
* This method might return an expired signature.
|
||||||
|
* If a non-expired subkey binding signature exists, the latest non-expired yet already effective
|
||||||
|
* subkey binding signature for the given subkey will be returned.
|
||||||
|
*
|
||||||
|
* @param keyRing key ring
|
||||||
|
* @param subkey subkey
|
||||||
|
* @param policy policy
|
||||||
|
* @param referenceTime validationDate
|
||||||
|
* @return subkey binding signature
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun pickLatestSubkeyBindingSignature(keyRing: PGPKeyRing, subkey: PGPPublicKey, policy: Policy, referenceTime: Date): PGPSignature? {
|
||||||
|
val primaryKey = keyRing.publicKey
|
||||||
|
require(primaryKey.keyID != subkey.keyID) { "Primary key cannot have subkey binding signatures." }
|
||||||
|
var latest: PGPSignature? = null
|
||||||
|
return getSortedSignaturesOfType(subkey, SignatureType.SUBKEY_BINDING).lastOrNull {
|
||||||
|
return@lastOrNull try {
|
||||||
|
SignatureValidator.signatureIsOfType(SignatureType.SUBKEY_BINDING).verify(it)
|
||||||
|
SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(it)
|
||||||
|
SignatureValidator.signatureDoesNotPredateSignee(subkey).verify(it)
|
||||||
|
SignatureValidator.signatureIsAlreadyEffective(referenceTime).verify(it)
|
||||||
|
// if the currently latest signature is not yet expired, check if the next candidate is not yet expired
|
||||||
|
if (latest != null && !latest!!.isExpired(referenceTime)) {
|
||||||
|
SignatureValidator.signatureIsNotYetExpired(referenceTime).verify(it)
|
||||||
|
}
|
||||||
|
SignatureValidator.correctSubkeyBindingSignature(primaryKey, subkey).verify(it)
|
||||||
|
latest = it
|
||||||
|
true
|
||||||
|
} catch (e : SignatureValidationException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
private fun getSortedSignaturesOfType(key: PGPPublicKey, type: SignatureType): List<PGPSignature> =
|
||||||
|
key.getSignaturesOfType(type.code).asSequence()
|
||||||
|
.sortedWith(SignatureCreationDateComparator())
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue