001// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org> 002// 003// SPDX-License-Identifier: Apache-2.0 004 005package org.pgpainless.key; 006 007import java.util.Collections; 008import java.util.Date; 009import java.util.Iterator; 010import java.util.List; 011 012import org.bouncycastle.openpgp.PGPException; 013import org.bouncycastle.openpgp.PGPKeyRing; 014import org.bouncycastle.openpgp.PGPPublicKey; 015import org.bouncycastle.openpgp.PGPPublicKeyRing; 016import org.bouncycastle.openpgp.PGPSignature; 017import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector; 018import org.pgpainless.algorithm.SignatureType; 019import org.pgpainless.exception.SignatureValidationException; 020import org.pgpainless.implementation.ImplementationFactory; 021import org.pgpainless.policy.Policy; 022import org.pgpainless.signature.consumer.SignatureCreationDateComparator; 023import org.pgpainless.signature.consumer.SignatureVerifier; 024import org.pgpainless.util.CollectionUtils; 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028public final class KeyRingValidator { 029 030 private KeyRingValidator() { 031 032 } 033 034 private static final Logger LOGGER = LoggerFactory.getLogger(KeyRingValidator.class); 035 036 public static <R extends PGPKeyRing> R validate(R keyRing, Policy policy) { 037 try { 038 return validate(keyRing, policy, new Date()); 039 } catch (PGPException e) { 040 return null; 041 } 042 } 043 044 public static <R extends PGPKeyRing> R validate(R keyRing, Policy policy, Date validationDate) throws PGPException { 045 return getKeyRingAtDate(keyRing, policy, validationDate); 046 } 047 048 private static <R extends PGPKeyRing> R getKeyRingAtDate(R keyRing, Policy policy, Date validationDate) throws PGPException { 049 PGPPublicKey primaryKey = keyRing.getPublicKey(); 050 primaryKey = evaluatePrimaryKey(primaryKey, policy, validationDate); 051 if (keyRing instanceof PGPPublicKeyRing) { 052 PGPPublicKeyRing publicKeys = (PGPPublicKeyRing) keyRing; 053 publicKeys = PGPPublicKeyRing.insertPublicKey(publicKeys, primaryKey); 054 keyRing = (R) publicKeys; 055 } 056 057 return keyRing; 058 } 059 060 private static PGPPublicKey evaluatePrimaryKey(PGPPublicKey primaryKey, Policy policy, Date validationDate) throws PGPException { 061 062 PGPPublicKey blank = new PGPPublicKey(primaryKey.getPublicKeyPacket(), ImplementationFactory.getInstance().getKeyFingerprintCalculator()); 063 064 Iterator<PGPSignature> directKeyIterator = primaryKey.getSignaturesOfType(SignatureType.DIRECT_KEY.getCode()); 065 List<PGPSignature> directKeyCertifications = CollectionUtils.iteratorToList(directKeyIterator); 066 Collections.sort(directKeyCertifications, new SignatureCreationDateComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD)); 067 for (PGPSignature signature : directKeyCertifications) { 068 try { 069 if (SignatureVerifier.verifyDirectKeySignature(signature, blank, policy, validationDate)) { 070 blank = PGPPublicKey.addCertification(blank, signature); 071 } 072 } catch (SignatureValidationException e) { 073 LOGGER.debug("Rejecting direct key signature: {}", e.getMessage(), e); 074 } 075 } 076 077 Iterator<PGPSignature> revocationIterator = primaryKey.getSignaturesOfType(SignatureType.KEY_REVOCATION.getCode()); 078 List<PGPSignature> directKeyRevocations = CollectionUtils.iteratorToList(revocationIterator); 079 Collections.sort(directKeyRevocations, new SignatureCreationDateComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD)); 080 for (PGPSignature signature : directKeyRevocations) { 081 try { 082 if (SignatureVerifier.verifyKeyRevocationSignature(signature, primaryKey, policy, validationDate)) { 083 blank = PGPPublicKey.addCertification(blank, signature); 084 } 085 } catch (SignatureValidationException e) { 086 LOGGER.debug("Rejecting key revocation signature: {}", e.getMessage(), e); 087 } 088 } 089 090 Iterator<String> userIdIterator = primaryKey.getUserIDs(); 091 while (userIdIterator.hasNext()) { 092 String userId = userIdIterator.next(); 093 Iterator<PGPSignature> userIdSigs = primaryKey.getSignaturesForID(userId); 094 List<PGPSignature> signatures = CollectionUtils.iteratorToList(userIdSigs); 095 Collections.sort(signatures, new SignatureCreationDateComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD)); 096 for (PGPSignature signature : signatures) { 097 try { 098 if (SignatureType.valueOf(signature.getSignatureType()) == SignatureType.CERTIFICATION_REVOCATION) { 099 if (SignatureVerifier.verifyUserIdRevocation(userId, signature, primaryKey, policy, validationDate)) { 100 blank = PGPPublicKey.addCertification(blank, userId, signature); 101 } 102 } else { 103 if (SignatureVerifier.verifyUserIdCertification(userId, signature, primaryKey, policy, validationDate)) { 104 blank = PGPPublicKey.addCertification(blank, userId, signature); 105 } 106 } 107 } catch (SignatureValidationException e) { 108 LOGGER.debug("Rejecting user-id certification for user-id {}: {}", userId, e.getMessage(), e); 109 } 110 } 111 } 112 113 Iterator<PGPUserAttributeSubpacketVector> userAttributes = primaryKey.getUserAttributes(); 114 while (userAttributes.hasNext()) { 115 PGPUserAttributeSubpacketVector userAttribute = userAttributes.next(); 116 Iterator<PGPSignature> userAttributeSignatureIterator = primaryKey.getSignaturesForUserAttribute(userAttribute); 117 while (userAttributeSignatureIterator.hasNext()) { 118 PGPSignature signature = userAttributeSignatureIterator.next(); 119 try { 120 if (SignatureType.valueOf(signature.getSignatureType()) == SignatureType.CERTIFICATION_REVOCATION) { 121 if (SignatureVerifier.verifyUserAttributesRevocation(userAttribute, signature, primaryKey, policy, validationDate)) { 122 blank = PGPPublicKey.addCertification(blank, userAttribute, signature); 123 } 124 } else { 125 if (SignatureVerifier.verifyUserAttributesCertification(userAttribute, signature, primaryKey, policy, validationDate)) { 126 blank = PGPPublicKey.addCertification(blank, userAttribute, signature); 127 } 128 } 129 } catch (SignatureValidationException e) { 130 LOGGER.debug("Rejecting user-attribute signature: {}", e.getMessage(), e); 131 } 132 } 133 } 134 135 return blank; 136 } 137 138}