1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-09-09 18:29:39 +02:00

KeyRingInfo: Replace PGPainless signature evaluation with BCs

This commit is contained in:
Paul Schaub 2025-02-07 13:19:43 +01:00
parent b488b70050
commit 69f802d442
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
2 changed files with 36 additions and 123 deletions

View file

@ -20,7 +20,6 @@ import org.pgpainless.key.OpenPgpFingerprint
import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.key.SubkeyIdentifier
import org.pgpainless.key.util.KeyRingUtils import org.pgpainless.key.util.KeyRingUtils
import org.pgpainless.policy.Policy import org.pgpainless.policy.Policy
import org.pgpainless.signature.consumer.SignaturePicker
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil.Companion.getKeyExpirationTimeAsDate import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil.Companion.getKeyExpirationTimeAsDate
import org.pgpainless.util.DateUtil import org.pgpainless.util.DateUtil
@ -47,7 +46,7 @@ class KeyRingInfo(
referenceDate: Date = Date() referenceDate: Date = Date()
) : this(keys, PGPainless.getPolicy(), referenceDate) ) : this(keys, PGPainless.getPolicy(), referenceDate)
private val signatures: Signatures = Signatures(keys.pgpKeyRing, referenceDate, policy) // private val signatures: Signatures = Signatures(keys.pgpKeyRing, referenceDate, policy)
/** Primary [OpenPGPCertificate.OpenPGPPrimaryKey]. */ /** Primary [OpenPGPCertificate.OpenPGPPrimaryKey]. */
val publicKey: OpenPGPCertificate.OpenPGPPrimaryKey = keys.primaryKey val publicKey: OpenPGPCertificate.OpenPGPPrimaryKey = keys.primaryKey
@ -67,10 +66,16 @@ class KeyRingInfo(
val userIds: List<String> = KeyRingUtils.getUserIdsIgnoringInvalidUTF8(publicKey.pgpPublicKey) val userIds: List<String> = KeyRingUtils.getUserIdsIgnoringInvalidUTF8(publicKey.pgpPublicKey)
/** Primary User-ID. */ /** Primary User-ID. */
val primaryUserId = keys.getPrimaryUserId(referenceDate)?.userId val primaryUserId: String? = keys.getPrimaryUserId(referenceDate)?.userId
/** Revocation State. */ /** Revocation State. */
val revocationState = signatures.primaryKeyRevocation.toRevocationState() val revocationState: RevocationState =
publicKey.getLatestSelfSignature(referenceDate)?.let {
if (!it.isRevocation) RevocationState.notRevoked()
else if (it.isHardRevocation) RevocationState.hardRevoked()
else RevocationState.softRevoked(it.creationTime)
}
?: RevocationState.notRevoked()
/** /**
* Return the date on which the primary key was revoked, or null if it has not yet been revoked. * Return the date on which the primary key was revoked, or null if it has not yet been revoked.
* *
@ -111,13 +116,7 @@ class KeyRingInfo(
val validUserIds: List<String> = keys.getValidUserIds(referenceDate).map { it.userId } val validUserIds: List<String> = keys.getValidUserIds(referenceDate).map { it.userId }
/** List of valid and expired user-IDs. */ /** List of valid and expired user-IDs. */
val validAndExpiredUserIds: List<String> = val validAndExpiredUserIds: List<String> = userIds
userIds.filter {
val certification = signatures.userIdCertifications[it] ?: return@filter false
val revocation = signatures.userIdRevocations[it] ?: return@filter true
return@filter !revocation.isHardRevocation &&
certification.creationTime > revocation.creationTime
}
/** List of email addresses that can be extracted from the user-IDs. */ /** List of email addresses that can be extracted from the user-IDs. */
val emailAddresses: List<String> = val emailAddresses: List<String> =
@ -132,10 +131,12 @@ class KeyRingInfo(
} }
/** Newest direct-key self-signature on the primary key. */ /** Newest direct-key self-signature on the primary key. */
val latestDirectKeySelfSignature: PGPSignature? = signatures.primaryKeySelfSignature val latestDirectKeySelfSignature: PGPSignature? =
publicKey.getLatestDirectKeySelfSignature(referenceDate)?.signature
/** Newest primary-key revocation self-signature. */ /** Newest primary-key revocation self-signature. */
val revocationSelfSignature: PGPSignature? = signatures.primaryKeyRevocation val revocationSelfSignature: PGPSignature? =
publicKey.getLatestKeyRevocationSignature(referenceDate)?.signature
/** Public-key encryption-algorithm of the primary key. */ /** Public-key encryption-algorithm of the primary key. */
val algorithm: PublicKeyAlgorithm = val algorithm: PublicKeyAlgorithm =
@ -166,7 +167,7 @@ class KeyRingInfo(
.asSequence() .asSequence()
.filter { .filter {
if (!it.keyIdentifier.matches(keyIdentifier)) { if (!it.keyIdentifier.matches(keyIdentifier)) {
if (signatures.subkeyBindings[it.keyIdentifier.keyId] == null) { if (it.getLatestSelfSignature(referenceDate) == null) {
LOGGER.debug("Subkey ${it.keyIdentifier} has no binding signature.") LOGGER.debug("Subkey ${it.keyIdentifier} has no binding signature.")
return@filter false return@filter false
} }
@ -319,7 +320,11 @@ class KeyRingInfo(
* @return true, if the given user-ID is hard-revoked. * @return true, if the given user-ID is hard-revoked.
*/ */
fun isHardRevoked(userId: CharSequence): Boolean { fun isHardRevoked(userId: CharSequence): Boolean {
return signatures.userIdRevocations[userId]?.isHardRevocation ?: false return keys
.getUserId(userId.toString())
?.getLatestSelfSignature(referenceDate)
?.isHardRevocation
?: false
} }
/** /**
@ -425,13 +430,7 @@ class KeyRingInfo(
/** Return the most-recently created self-signature on the key. */ /** Return the most-recently created self-signature on the key. */
private fun getMostRecentSignature(): PGPSignature? = private fun getMostRecentSignature(): PGPSignature? =
setOfNotNull(latestDirectKeySelfSignature, revocationSelfSignature) keys.components.map { it.latestSelfSignature }.maxByOrNull { it.creationTime }?.signature
.asSequence()
.plus(signatures.userIdCertifications.values)
.plus(signatures.userIdRevocations.values)
.plus(signatures.subkeyBindings.values)
.plus(signatures.subkeyRevocations.values)
.maxByOrNull { it.creationTime }
/** /**
* Return the creation time of the latest added subkey. * Return the creation time of the latest added subkey.
* *
@ -463,7 +462,7 @@ class KeyRingInfo(
* @return current subkey binding signature * @return current subkey binding signature
*/ */
fun getCurrentSubkeyBindingSignature(keyId: Long): PGPSignature? = fun getCurrentSubkeyBindingSignature(keyId: Long): PGPSignature? =
signatures.subkeyBindings[keyId] keys.getKey(KeyIdentifier(keyId))?.getCertification(referenceDate)?.signature
/** /**
* Return the current revocation signature for the subkey with the given key-ID. * Return the current revocation signature for the subkey with the given key-ID.
@ -471,7 +470,7 @@ class KeyRingInfo(
* @return current subkey revocation signature * @return current subkey revocation signature
*/ */
fun getSubkeyRevocationSignature(keyId: Long): PGPSignature? = fun getSubkeyRevocationSignature(keyId: Long): PGPSignature? =
signatures.subkeyRevocations[keyId] keys.getKey(KeyIdentifier(keyId))?.getRevocation(referenceDate)?.signature
fun getKeyFlagsOf(keyIdentifier: KeyIdentifier): List<KeyFlag> = fun getKeyFlagsOf(keyIdentifier: KeyIdentifier): List<KeyFlag> =
getKeyFlagsOf(keyIdentifier.keyId) getKeyFlagsOf(keyIdentifier.keyId)
@ -619,36 +618,7 @@ class KeyRingInfo(
* @return true if key is bound validly * @return true if key is bound validly
*/ */
fun isKeyValidlyBound(keyId: Long): Boolean { fun isKeyValidlyBound(keyId: Long): Boolean {
val publicKey = keys.pgpKeyRing.getPublicKey(keyId) ?: return false return keys.getKey(KeyIdentifier(keyId))?.isBoundAt(referenceDate) ?: false
// Primary key -> Check Primary Key Revocation
if (publicKey.keyIdentifier.matches(this.publicKey.keyIdentifier)) {
return if (signatures.primaryKeyRevocation != null &&
signatures.primaryKeyRevocation.isHardRevocation) {
false
} else signatures.primaryKeyRevocation == null
}
// Else Subkey -> Check Subkey Revocation
val binding = signatures.subkeyBindings[keyId]
val revocation = signatures.subkeyRevocations[keyId]
// No valid binding
if (binding == null || binding.isExpired(referenceDate)) {
return false
}
// Revocation
return if (revocation != null) {
if (revocation.isHardRevocation) {
// Subkey is hard revoked
false
} else {
// Key is soft-revoked, not yet re-bound
(revocation.isExpired(referenceDate) ||
!revocation.creationTime.after(binding.creationTime))
}
} else true
} }
/** /**
@ -662,49 +632,20 @@ class KeyRingInfo(
* @return primary user-id or null * @return primary user-id or null
*/ */
private fun findPrimaryUserId(): String? { private fun findPrimaryUserId(): String? {
if (userIds.isEmpty()) { return keys.primaryKey.getExplicitOrImplicitPrimaryUserId(referenceDate)?.userId
return null
}
return signatures.userIdCertifications
.filter { (_, certification) -> certification.hashedSubPackets.isPrimaryUserID }
.entries
.maxByOrNull { (_, certification) -> certification.creationTime }
?.key
?: signatures.userIdCertifications.keys.firstOrNull()
} }
/** Return true, if the primary user-ID, as well as the given user-ID are valid and bound. */ /** Return true, if the primary user-ID, as well as the given user-ID are valid and bound. */
fun isUserIdValid(userId: CharSequence) = fun isUserIdValid(userId: CharSequence): Boolean {
if (primaryUserId == null) { var valid = isUserIdBound(userId)
false if (primaryUserId != null) valid = valid && isUserIdBound(primaryUserId)
} else { valid = valid && isKeyValidlyBound(publicKey.keyIdentifier)
isUserIdBound(primaryUserId) && return valid
(if (userId == primaryUserId) true else isUserIdBound(userId)) }
}
/** Return true, if the given user-ID is validly bound. */ /** Return true, if the given user-ID is validly bound. */
fun isUserIdBound(userId: CharSequence) = fun isUserIdBound(userId: CharSequence): Boolean =
signatures.userIdCertifications[userId]?.let { sig -> keys.getUserId(userId.toString())?.isBoundAt(referenceDate) ?: false
if (sig.isExpired(referenceDate)) {
// certification expired
return false
}
if (sig.hashedSubPackets.isPrimaryUserID) {
getKeyExpirationTimeAsDate(sig, publicKey.pgpPublicKey)?.let { expirationDate ->
// key expired?
if (expirationDate < referenceDate) return false
}
}
signatures.userIdRevocations[userId]?.let { rev ->
if (rev.isHardRevocation) {
return false // hard revoked -> invalid
}
sig.creationTime > rev.creationTime // re-certification after soft revocation?
}
?: true // certification, but no revocation
}
?: false // no certification
/** [HashAlgorithm] preferences of the given user-ID. */ /** [HashAlgorithm] preferences of the given user-ID. */
fun getPreferredHashAlgorithms(userId: CharSequence): Set<HashAlgorithm> { fun getPreferredHashAlgorithms(userId: CharSequence): Set<HashAlgorithm> {
@ -786,34 +727,4 @@ class KeyRingInfo(
@JvmStatic private val LOGGER = LoggerFactory.getLogger(KeyRingInfo::class.java) @JvmStatic private val LOGGER = LoggerFactory.getLogger(KeyRingInfo::class.java)
} }
private class Signatures(val keys: PGPKeyRing, val referenceDate: Date, val policy: Policy) {
val primaryKeyRevocation: PGPSignature? =
SignaturePicker.pickCurrentRevocationSelfSignature(keys, policy, referenceDate)
val primaryKeySelfSignature: PGPSignature? =
SignaturePicker.pickLatestDirectKeySignature(keys, policy, referenceDate)
val userIdRevocations = mutableMapOf<String, PGPSignature>()
val userIdCertifications = mutableMapOf<String, PGPSignature>()
val subkeyRevocations = mutableMapOf<Long, PGPSignature>()
val subkeyBindings = mutableMapOf<Long, PGPSignature>()
init {
KeyRingUtils.getUserIdsIgnoringInvalidUTF8(keys.publicKey).forEach { userId ->
SignaturePicker.pickCurrentUserIdRevocationSignature(
keys, userId, policy, referenceDate)
?.let { userIdRevocations[userId] = it }
SignaturePicker.pickLatestUserIdCertificationSignature(
keys, userId, policy, referenceDate)
?.let { userIdCertifications[userId] = it }
}
keys.publicKeys.asSequence().drop(1).forEach { subkey ->
SignaturePicker.pickCurrentSubkeyBindingRevocationSignature(
keys, subkey, policy, referenceDate)
?.let { subkeyRevocations[subkey.keyID] = it }
SignaturePicker.pickLatestSubkeyBindingSignature(
keys, subkey, policy, referenceDate)
?.let { subkeyBindings[subkey.keyID] = it }
}
}
}
} }

View file

@ -34,7 +34,9 @@ abstract class AbstractSignatureBuilder<B : AbstractSignatureBuilder<B>>(
protected abstract val signatureTypePredicate: Predicate<SignatureType> protected abstract val signatureTypePredicate: Predicate<SignatureType>
init { init {
require(signatureTypePredicate.test(_signatureType)) { "Invalid signature type." } require(signatureTypePredicate.test(_signatureType)) {
"Invalid signature type: $_signatureType"
}
} }
@Throws(PGPException::class) @Throws(PGPException::class)