mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-09 10:19:39 +02:00
KeyRingInfo: Replace PGPainless signature evaluation with BCs
This commit is contained in:
parent
b8bb5de2a2
commit
7217eda924
2 changed files with 36 additions and 123 deletions
|
@ -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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue