Expand the experiment

This commit is contained in:
Paul Schaub 2023-10-05 16:41:36 +02:00
parent 6f3d2cb9ef
commit 68b9496381
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
4 changed files with 86 additions and 11 deletions

View File

@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package openpgp
/**
* Filter `null` values from a [Map<K, V?>], turning it into a [Map<K, V>].
*/
@Suppress("UNCHECKED_CAST")
fun <K, V> Map<K, V?>.filterNotNullValues(): Map<K, V> = filterValues { it != null } as Map<K, V>

View File

@ -12,6 +12,13 @@ import org.bouncycastle.openpgp.PGPSignature
import org.pgpainless.PGPainless
import org.pgpainless.key.OpenPgpFingerprint
import org.pgpainless.key.SubkeyIdentifier
import java.util.*
import kotlin.NoSuchElementException
import kotlin.math.max
import kotlin.reflect.KProperty
val PGPKeyRing.primaryPublicKey: PGPPublicKey
get() = requireNotNull(publicKey).also { require(it.isMasterKey) }
/**
* Return true, if this [PGPKeyRing] contains the subkey identified by the [SubkeyIdentifier].
@ -72,9 +79,44 @@ fun PGPKeyRing.getPublicKeyFor(onePassSignature: PGPOnePassSignature): PGPPublic
* Return the [OpenPgpFingerprint] of this OpenPGP key.
*/
val PGPKeyRing.openPgpFingerprint: OpenPgpFingerprint
get() = OpenPgpFingerprint.of(this)
get() = OpenPgpFingerprint.of(primaryPublicKey)
/**
* Return this OpenPGP key as an ASCII armored String.
*/
fun PGPKeyRing.toAsciiArmor(): String = PGPainless.asciiArmor(this)
fun PGPKeyRing.toAsciiArmor(): String = PGPainless.asciiArmor(this)
val PGPKeyRing.goodDirectKeySignatures: List<PGPSignature> by LazyPGPKeyRing {
it.primaryPublicKey.goodDirectKeySignatures
}
val PGPKeyRing.goodDirectKeySignature: PGPSignature? by LazyPGPKeyRing {
it.primaryPublicKey.goodDirectKeySignature
}
val PGPKeyRing.expirationDate: Date? by LazyPGPKeyRing {
val dkExp = it.goodDirectKeySignature?.getKeyExpirationDate(it.primaryPublicKey.creationTime)
val puExp = it.primaryPublicKey.goodUserIds[it.primaryPublicKey.primaryUserId]
?.getKeyExpirationDate(it.primaryPublicKey.creationTime)
dkExp ?: return@LazyPGPKeyRing puExp // direct-key exp null ? -> userId exp
puExp ?: return@LazyPGPKeyRing dkExp // userId exp null ? -> direct-key exp
return@LazyPGPKeyRing if (dkExp < puExp) dkExp else puExp // max direct-key exp, userId exp
}
internal class LazyPGPKeyRing<T>(val function: (PGPKeyRing) -> T) {
private var value: Result<T>? = null
operator fun getValue(keys: PGPKeyRing, property: KProperty<*>): T {
if (value == null) {
value = try {
Result.success(function(keys))
} catch (e : Throwable) {
Result.failure(e)
}
}
return value!!.getOrThrow()
}
}

View File

@ -4,6 +4,7 @@
package org.bouncycastle.extensions
import openpgp.filterNotNullValues
import org.bouncycastle.asn1.gnu.GNUObjectIdentifiers
import org.bouncycastle.bcpg.ECDHPublicBCPGKey
import org.bouncycastle.bcpg.ECDSAPublicBCPGKey
@ -12,11 +13,8 @@ import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil
import org.bouncycastle.openpgp.PGPPublicKey
import org.bouncycastle.openpgp.PGPSignature
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.KeyFlag
import org.pgpainless.algorithm.PublicKeyAlgorithm
import org.pgpainless.algorithm.SignatureType
import org.pgpainless.decryption_verification.SignatureVerification
import org.pgpainless.exception.SignatureValidationException
import org.pgpainless.key.OpenPgpFingerprint
import org.pgpainless.key.generation.type.eddsa.EdDSACurve
import org.pgpainless.signature.consumer.SignatureVerifier
@ -55,9 +53,9 @@ val PGPPublicKey.publicKeyAlgorithm: PublicKeyAlgorithm
/**
* Return the [OpenPgpFingerprint] of this key.
*/
val PGPPublicKey.openPgpFingerprint: OpenPgpFingerprint by Lazy { OpenPgpFingerprint.of(it) }
val PGPPublicKey.openPgpFingerprint: OpenPgpFingerprint by LazyPGPPublicKey { OpenPgpFingerprint.of(it) }
val PGPPublicKey.goodDirectKeySignatures: List<PGPSignature> by Lazy { key ->
val PGPPublicKey.goodDirectKeySignatures: List<PGPSignature> by LazyPGPPublicKey { key ->
key.getSignaturesOfType(SignatureType.DIRECT_KEY.code)
.asSequence()
.filter { it.keyID == key.keyID }
@ -69,13 +67,13 @@ val PGPPublicKey.goodDirectKeySignatures: List<PGPSignature> by Lazy { key ->
.toList()
}
val PGPPublicKey.goodDirectKeySignature: PGPSignature? by Lazy {
val PGPPublicKey.goodDirectKeySignature: PGPSignature? by LazyPGPPublicKey {
it.goodDirectKeySignatures
.sortedBy { sig -> sig.creationTime }
.lastOrNull()
}
val PGPPublicKey.goodKeyRevocations: List<PGPSignature> by Lazy { key ->
val PGPPublicKey.goodKeyRevocations: List<PGPSignature> by LazyPGPPublicKey { key ->
key.getSignaturesOfType(SignatureType.KEY_REVOCATION.code)
.asSequence()
.filter { it.keyID == key.keyID }
@ -87,13 +85,34 @@ val PGPPublicKey.goodKeyRevocations: List<PGPSignature> by Lazy { key ->
.toList()
}
val PGPPublicKey.goodKeyRevocation: PGPSignature? by Lazy {
val PGPPublicKey.goodKeyRevocation: PGPSignature? by LazyPGPPublicKey {
it.goodKeyRevocations
.sortedBy { sig -> sig.creationTime }
.lastOrNull()
}
internal class Lazy<T>(val function: (PGPPublicKey) -> T) {
val PGPPublicKey.goodUserIds: Map<String, PGPSignature> by LazyPGPPublicKey { it.getGoodUserIds(Date()) }
fun PGPPublicKey.getGoodUserIds(referenceTime: Date): Map<String, PGPSignature> {
return userIDs.asSequence().associateWith { userId ->
getSignaturesForID(userId).asSequence()
.filter { it.wasIssuedBy(this) }
.filter { it.isCertification }
.filter { it.isEffective(referenceTime)}
.sortedBy { it.creationTime }
.lastOrNull()
}.filterNotNullValues()
}
val PGPPublicKey.primaryUserId: String? by LazyPGPPublicKey { it.getPrimaryUserId(Date()) }
fun PGPPublicKey.getPrimaryUserId(referenceTime: Date): String? =
getGoodUserIds(referenceTime).entries
.sortedBy { it.value.creationTime }
.lastOrNull { it.value.hashedSubPackets.isPrimaryUserID }?.key // latest primary User ID
?: getGoodUserIds(referenceTime).keys.firstOrNull() // else first User ID
internal class LazyPGPPublicKey<T>(val function: (PGPPublicKey) -> T) {
private var value: Result<T>? = null
operator fun getValue(pgpPublicKey: PGPPublicKey, property: KProperty<*>): T {

View File

@ -36,6 +36,9 @@ val PGPSignature.signatureExpirationDate: Date?
fun PGPSignature.isExpired(referenceTime: Date = Date()) =
signatureExpirationDate?.let { referenceTime >= it } ?: false
fun PGPSignature.isEffective(referenceTime: Date = Date()) =
!isExpired(referenceTime) && creationTime < referenceTime
/**
* Return the key-ID of the issuer, determined by examining the IssuerKeyId and IssuerFingerprint
* subpackets of the signature.