1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-06-17 17:14:51 +02:00
pgpainless/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

770 lines
29 KiB
Kotlin
Raw Normal View History

2023-10-09 12:49:04 +02:00
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key.info
import java.util.*
import openpgp.openPgpKeyId
import org.bouncycastle.openpgp.*
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.*
import org.pgpainless.bouncycastle.extensions.*
2023-10-09 12:49:04 +02:00
import org.pgpainless.exception.KeyException.UnboundUserIdException
import org.pgpainless.key.OpenPgpFingerprint
import org.pgpainless.key.SubkeyIdentifier
import org.pgpainless.key.util.KeyRingUtils
import org.pgpainless.policy.Policy
import org.pgpainless.signature.consumer.SignaturePicker
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil.Companion.getKeyExpirationTimeAsDate
import org.pgpainless.util.DateUtil
import org.slf4j.LoggerFactory
class KeyRingInfo(
val keys: PGPKeyRing,
val policy: Policy = PGPainless.getPolicy(),
val referenceDate: Date = Date()
) {
@JvmOverloads
constructor(
keys: PGPKeyRing,
referenceDate: Date = Date()
) : this(keys, PGPainless.getPolicy(), referenceDate)
private val signatures: Signatures = Signatures(keys, referenceDate, policy)
/** Primary {@link PGPPublicKey}.´ */
val publicKey: PGPPublicKey = KeyRingUtils.requirePrimaryPublicKeyFrom(keys)
/** Primary key ID. */
val keyId: Long = publicKey.keyID
/** Primary key fingerprint. */
val fingerprint: OpenPgpFingerprint = OpenPgpFingerprint.of(keys)
/** All User-IDs (valid, expired, revoked). */
val userIds: List<String> = KeyRingUtils.getUserIdsIgnoringInvalidUTF8(publicKey)
/** Primary User-ID. */
val primaryUserId = findPrimaryUserId()
/** Revocation State. */
val revocationState = signatures.primaryKeyRevocation.toRevocationState()
/**
* Return the date on which the primary key was revoked, or null if it has not yet been revoked.
*
* @return revocation date or null
*/
val revocationDate: Date? =
if (revocationState.isSoftRevocation()) revocationState.date else null
/**
* Primary [PGPSecretKey] of this key ring or null if the key ring is not a [PGPSecretKeyRing].
*/
val secretKey: PGPSecretKey? =
when (keys) {
is PGPSecretKeyRing -> keys.secretKey!!
else -> null
}
/** OpenPGP key version. */
val version: Int = publicKey.version
/**
* Return all {@link PGPPublicKey PGPPublicKeys} of this key ring. The first key in the list
* being the primary key. Note that the list is unmodifiable.
*
* @return list of public keys
*/
val publicKeys: List<PGPPublicKey> = keys.publicKeys.asSequence().toList()
/** All secret keys. If the key ring is a [PGPPublicKeyRing], then return an empty list. */
val secretKeys: List<PGPSecretKey> =
when (keys) {
is PGPSecretKeyRing -> keys.secretKeys.asSequence().toList()
else -> listOf()
}
/** List of valid public subkeys. */
val validSubkeys: List<PGPPublicKey> =
keys.publicKeys.asSequence().filter { isKeyValidlyBound(it.keyID) }.toList()
/** List of valid user-IDs. */
val validUserIds: List<String> = userIds.filter { isUserIdBound(it) }
/** List of valid and expired user-IDs. */
val validAndExpiredUserIds: List<String> =
userIds.filter {
val certification = signatures.userIdCertifications[it] ?: return@filter false
val revocation = signatures.userIdRevocations[it] ?: return@filter true
2023-09-12 21:55:39 +02:00
return@filter !revocation.isHardRevocation &&
certification.creationTime > revocation.creationTime
2023-10-09 12:49:04 +02:00
}
/** List of email addresses that can be extracted from the user-IDs. */
val emailAddresses: List<String> =
userIds.mapNotNull {
PATTERN_EMAIL_FROM_USERID.matcher(it).let { m1 ->
if (m1.find()) m1.group(1)
else
PATTERN_EMAIL_EXPLICIT.matcher(it).let { m2 ->
if (m2.find()) m2.group(1) else null
}
2023-10-09 12:49:04 +02:00
}
}
/** Newest direct-key self-signature on the primary key. */
val latestDirectKeySelfSignature: PGPSignature? = signatures.primaryKeySelfSignature
/** Newest primary-key revocation self-signature. */
val revocationSelfSignature: PGPSignature? = signatures.primaryKeyRevocation
/** Public-key encryption-algorithm of the primary key. */
val algorithm: PublicKeyAlgorithm = PublicKeyAlgorithm.requireFromId(publicKey.algorithm)
/** Creation date of the primary key. */
val creationDate: Date = publicKey.creationTime!!
/** Latest date at which the key was modified (either by adding a subkey or self-signature). */
val lastModified: Date = getMostRecentSignature()?.creationTime ?: getLatestKeyCreationDate()
/** True, if the underlying keyring is a [PGPSecretKeyRing]. */
val isSecretKey: Boolean = keys is PGPSecretKeyRing
/** True, if there are no encrypted secret keys. */
val isFullyDecrypted: Boolean =
!isSecretKey || secretKeys.all { it.hasDummyS2K() || it.isDecrypted() }
2023-10-09 12:49:04 +02:00
/** True, if there are only encrypted secret keys. */
val isFullyEncrypted: Boolean =
isSecretKey && secretKeys.none { !it.hasDummyS2K() && it.isDecrypted() }
2023-10-09 12:49:04 +02:00
/** List of public keys, whose secret key counterparts can be used to decrypt messages. */
val decryptionSubkeys: List<PGPPublicKey> =
keys.publicKeys
.asSequence()
.filter {
if (it.keyID != keyId) {
if (signatures.subkeyBindings[it.keyID] == null) {
LOGGER.debug("Subkey ${it.keyID.openPgpKeyId()} has no binding signature.")
return@filter false
}
}
2023-10-09 12:49:04 +02:00
if (!it.isEncryptionKey) {
LOGGER.debug("(Sub-?)Key ${it.keyID.openPgpKeyId()} is not encryption-capable.")
return@filter false
}
2023-10-09 12:49:04 +02:00
return@filter true
}
.toList()
/** Expiration date of the primary key. */
val primaryKeyExpirationDate: Date?
get() {
val directKeyExpirationDate: Date? =
latestDirectKeySelfSignature?.let { getKeyExpirationTimeAsDate(it, publicKey) }
val possiblyExpiredPrimaryUserId = getPossiblyExpiredPrimaryUserId()
val primaryUserIdCertification =
possiblyExpiredPrimaryUserId?.let { getLatestUserIdCertification(it) }
val userIdExpirationDate: Date? =
primaryUserIdCertification?.let { getKeyExpirationTimeAsDate(it, publicKey) }
2023-10-09 12:49:04 +02:00
if (latestDirectKeySelfSignature == null && primaryUserIdCertification == null) {
/*
2023-10-09 12:49:04 +02:00
throw NoSuchElementException(
"No direct-key signature and no user-id signature found.")
*/
return null
2023-10-09 12:49:04 +02:00
}
if (directKeyExpirationDate != null && userIdExpirationDate == null) {
return directKeyExpirationDate
}
if (directKeyExpirationDate == null) {
return userIdExpirationDate
}
2023-10-09 12:49:04 +02:00
return if (directKeyExpirationDate < userIdExpirationDate) directKeyExpirationDate
else userIdExpirationDate
}
2023-09-13 15:05:58 +02:00
/** List of all subkeys that can be used to sign a message. */
val signingSubkeys: List<PGPPublicKey> =
validSubkeys.filter { getKeyFlagsOf(it.keyID).contains(KeyFlag.SIGN_DATA) }
/** Whether the key is usable for encryption. */
val isUsableForEncryption: Boolean = isUsableForEncryption(EncryptionPurpose.ANY)
/**
* Whether the key is capable of signing messages. This field is also true, if the key contains
* a subkey that is capable of signing messages, but where the secret key is unavailable, e.g.
* because it was moved to a smart-card.
*
* To check for keys that are actually usable to sign messages, use [isUsableForSigning].
*/
val isSigningCapable: Boolean = isKeyValidlyBound(keyId) && signingSubkeys.isNotEmpty()
/** Whether the key is actually usable to sign messages. */
val isUsableForSigning: Boolean =
isSigningCapable && signingSubkeys.any { isSecretKeyAvailable(it.keyID) }
/** [HashAlgorithm] preferences of the primary user-ID or if absent, of the primary key. */
val preferredHashAlgorithms: Set<HashAlgorithm>
get() =
primaryUserId?.let { getPreferredHashAlgorithms(it) }
?: getPreferredHashAlgorithms(keyId)
/**
* [SymmetricKeyAlgorithm] preferences of the primary user-ID or if absent of the primary key.
*/
val preferredSymmetricKeyAlgorithms: Set<SymmetricKeyAlgorithm>
get() =
primaryUserId?.let { getPreferredSymmetricKeyAlgorithms(it) }
?: getPreferredSymmetricKeyAlgorithms(keyId)
/** [CompressionAlgorithm] preferences of the primary user-ID or if absent, the primary key. */
val preferredCompressionAlgorithms: Set<CompressionAlgorithm>
get() =
primaryUserId?.let { getPreferredCompressionAlgorithms(it) }
?: getPreferredCompressionAlgorithms(keyId)
2023-10-09 12:49:04 +02:00
/**
* Return the expiration date of the subkey with the provided fingerprint.
*
* @param fingerprint subkey fingerprint
* @return expiration date or null
*/
fun getSubkeyExpirationDate(fingerprint: OpenPgpFingerprint): Date? {
return getSubkeyExpirationDate(fingerprint.keyId)
}
/**
* Return the expiration date of the subkey with the provided keyId.
*
* @param keyId subkey keyId
* @return expiration date
*/
fun getSubkeyExpirationDate(keyId: Long): Date? {
if (publicKey.keyID == keyId) return primaryKeyExpirationDate
val subkey =
getPublicKey(keyId)
?: throw NoSuchElementException(
"No subkey with key-ID ${keyId.openPgpKeyId()} found.")
val bindingSig =
getCurrentSubkeyBindingSignature(keyId)
?: throw AssertionError("Subkey has no valid binding signature.")
2023-09-12 22:07:17 +02:00
return bindingSig.getKeyExpirationDate(subkey.creationTime)
2023-10-09 12:49:04 +02:00
}
/**
* Return the date after which the key can no longer be used to perform the given use-case,
* caused by expiration.
*
* @return expiration date for the given use-case
*/
fun getExpirationDateForUse(use: KeyFlag): Date? {
require(use != KeyFlag.SPLIT && use != KeyFlag.SHARED) {
"SPLIT and SHARED are not uses, but properties."
}
val primaryKeyExpiration = primaryKeyExpirationDate
val keysWithFlag: List<PGPPublicKey> = getKeysWithKeyFlag(use)
if (keysWithFlag.isEmpty())
throw NoSuchElementException("No key with the required key flag found.")
var nonExpiring = false
val latestSubkeyExpiration =
keysWithFlag
.map { key ->
getSubkeyExpirationDate(key.keyID).also { if (it == null) nonExpiring = true }
}
.filterNotNull()
.maxByOrNull { it }
if (nonExpiring) return primaryKeyExpiration
return if (primaryKeyExpiration == null) latestSubkeyExpiration
else if (latestSubkeyExpiration == null) primaryKeyExpiration
else minOf(primaryKeyExpiration, latestSubkeyExpiration)
}
/**
* 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 {
2023-09-12 21:55:39 +02:00
return signatures.userIdRevocations[userId]?.isHardRevocation ?: false
2023-10-09 12:49:04 +02:00
}
/**
* Return a list of all keys which carry the provided key flag in their signature.
*
* @param flag flag
* @return keys with flag
*/
fun getKeysWithKeyFlag(flag: KeyFlag): List<PGPPublicKey> =
publicKeys.filter { getKeyFlagsOf(it.keyID).contains(flag) }
/**
* Return a list of all subkeys which can be used to encrypt a message for the given user-ID.
*
* @return encryption subkeys
*/
fun getEncryptionSubkeys(
userId: CharSequence?,
purpose: EncryptionPurpose
): List<PGPPublicKey> {
if (userId != null && !isUserIdValid(userId)) {
throw UnboundUserIdException(
OpenPgpFingerprint.of(keys),
userId.toString(),
getLatestUserIdCertification(userId),
getUserIdRevocation(userId))
}
return getEncryptionSubkeys(purpose)
}
/**
* Return a list of all subkeys which can be used to encrypt a message, given the purpose.
*
* @return subkeys which can be used for encryption
*/
fun getEncryptionSubkeys(purpose: EncryptionPurpose): List<PGPPublicKey> {
primaryKeyExpirationDate?.let {
if (it < referenceDate) {
LOGGER.debug(
"Certificate is expired: Primary key is expired on ${DateUtil.formatUTCDate(it)}")
return listOf()
}
}
return keys.publicKeys
.asSequence()
.filter {
if (!isKeyValidlyBound(it.keyID)) {
LOGGER.debug("(Sub?)-Key ${it.keyID.openPgpKeyId()} is not validly bound.")
return@filter false
}
getSubkeyExpirationDate(it.keyID)?.let { exp ->
if (exp < referenceDate) {
LOGGER.debug(
"(Sub?)-Key ${it.keyID.openPgpKeyId()} is expired on ${DateUtil.formatUTCDate(exp)}.")
return@filter false
}
}
2023-10-09 12:49:04 +02:00
if (!it.isEncryptionKey) {
LOGGER.debug(
"(Sub?)-Key ${it.keyID.openPgpKeyId()} algorithm is not capable of encryption.")
return@filter false
}
val keyFlags = getKeyFlagsOf(it.keyID)
when (purpose) {
EncryptionPurpose.COMMUNICATIONS ->
return@filter keyFlags.contains(KeyFlag.ENCRYPT_COMMS)
EncryptionPurpose.STORAGE ->
return@filter keyFlags.contains(KeyFlag.ENCRYPT_STORAGE)
EncryptionPurpose.ANY ->
return@filter keyFlags.contains(KeyFlag.ENCRYPT_COMMS) ||
keyFlags.contains(KeyFlag.ENCRYPT_STORAGE)
}
2023-10-09 12:49:04 +02:00
}
.toList()
}
/**
* Return, whether the key is usable for encryption, given the purpose.
*
* @return true, if the key can be used to encrypt a message according to the
* encryption-purpose.
*/
fun isUsableForEncryption(purpose: EncryptionPurpose): Boolean {
return isKeyValidlyBound(keyId) && getEncryptionSubkeys(purpose).isNotEmpty()
}
/**
* Return the primary user-ID, even if it is possibly expired.
*
* @return possibly expired primary user-ID
*/
fun getPossiblyExpiredPrimaryUserId(): String? =
primaryUserId
?: userIds
.mapNotNull { userId -> getLatestUserIdCertification(userId)?.let { userId to it } }
.sortedByDescending { it.second.creationTime }
.maxByOrNull { it.second.hashedSubPackets.isPrimaryUserID }
?.first
/** Return the most-recently created self-signature on the key. */
private fun getMostRecentSignature(): PGPSignature? =
setOfNotNull(latestDirectKeySelfSignature, revocationSelfSignature)
.asSequence()
.plus(signatures.userIdCertifications.values)
.plus(signatures.userIdRevocations.values)
.plus(signatures.subkeyBindings.values)
.plus(signatures.subkeyRevocations.values)
.maxByOrNull { creationDate }
/**
* Return the creation time of the latest added subkey.
*
* @return latest key creation time
*/
fun getLatestKeyCreationDate(): Date =
validSubkeys.maxByOrNull { creationDate }?.creationTime
?: throw AssertionError("Apparently there is no validly bound key in this key ring.")
/**
* Return the latest certification self-signature for the given user-ID.
*
* @return latest self-certification for the given user-ID.
*/
fun getLatestUserIdCertification(userId: CharSequence): PGPSignature? =
signatures.userIdCertifications[userId]
/**
* Return the latest revocation self-signature for the given user-ID
*
* @return latest user-ID revocation for the given user-ID
*/
fun getUserIdRevocation(userId: CharSequence): PGPSignature? =
signatures.userIdRevocations[userId]
/**
* Return the current binding signature for the subkey with the given key-ID.
*
* @return current subkey binding signature
*/
fun getCurrentSubkeyBindingSignature(keyId: Long): PGPSignature? =
signatures.subkeyBindings[keyId]
/**
* Return the current revocation signature for the subkey with the given key-ID.
*
* @return current subkey revocation signature
*/
fun getSubkeyRevocationSignature(keyId: Long): PGPSignature? =
signatures.subkeyRevocations[keyId]
/**
* Return a list of {@link KeyFlag KeyFlags} that apply to the subkey with the provided key id.
*
2023-10-09 12:49:04 +02:00
* @param keyId key-id
* @return list of key flags
*/
fun getKeyFlagsOf(keyId: Long): List<KeyFlag> =
if (keyId == publicKey.keyID) {
latestDirectKeySelfSignature?.let { sig ->
SignatureSubpacketsUtil.parseKeyFlags(sig)?.let { flags ->
return flags
}
}
2023-10-09 12:49:04 +02:00
primaryUserId?.let {
SignatureSubpacketsUtil.parseKeyFlags(getLatestUserIdCertification(it))?.let { flags
->
return flags
}
}
2023-10-09 12:49:04 +02:00
listOf()
} else {
getCurrentSubkeyBindingSignature(keyId)?.let {
SignatureSubpacketsUtil.parseKeyFlags(it)?.let { flags ->
return flags
}
}
listOf()
}
2023-10-09 12:49:04 +02:00
/**
* Return a list of {@link KeyFlag KeyFlags} that apply to the given user-id.
*
* @param userId user-id
* @return key flags
*/
fun getKeyFlagsOf(userId: CharSequence): List<KeyFlag> =
if (!isUserIdValid(userId)) {
listOf()
} else {
getLatestUserIdCertification(userId)?.let {
SignatureSubpacketsUtil.parseKeyFlags(it) ?: listOf()
}
?: throw AssertionError(
"While user-id '$userId' was reported as valid, there appears to be no certification for it.")
}
2023-10-09 12:49:04 +02:00
/**
* Return the public key with the given key id from the provided key ring.
*
* @param keyId key id
* @return public key or null
*/
fun getPublicKey(keyId: Long): PGPPublicKey? = keys.getPublicKey(keyId)
/**
* Return the secret key with the given key id.
*
* @param keyId key id
* @return secret key or null
*/
fun getSecretKey(keyId: Long): PGPSecretKey? =
when (keys) {
is PGPSecretKeyRing -> keys.getSecretKey(keyId)
else -> null
}
/**
* Return true, if the secret-key with the given key-ID is available (i.e. not moved to a
* smart-card).
*
* @return availability of the secret key
*/
fun isSecretKeyAvailable(keyId: Long): Boolean {
return getSecretKey(keyId)?.let {
return if (it.s2K == null) true // Unencrypted key
else it.s2K.type !in 100..110 // Secret key on smart-card
}
?: false // Missing secret key
}
/**
* Return the public key with the given fingerprint.
*
* @param fingerprint fingerprint
* @return public key or null
*/
fun getPublicKey(fingerprint: OpenPgpFingerprint): PGPPublicKey? =
keys.getPublicKey(fingerprint.keyId)
/**
* Return the secret key with the given fingerprint.
*
* @param fingerprint fingerprint
* @return secret key or null
*/
fun getSecretKey(fingerprint: OpenPgpFingerprint): PGPSecretKey? =
when (keys) {
is PGPSecretKeyRing -> keys.getSecretKey(fingerprint.keyId)
else -> null
}
/**
* Return the public key matching the given [SubkeyIdentifier].
*
* @return public key
* @throws IllegalArgumentException if the identifier's primary key does not match the primary
* key of the key.
*/
fun getPublicKey(identifier: SubkeyIdentifier): PGPPublicKey? {
require(identifier.primaryKeyId == publicKey.keyID) { "Mismatching primary key ID." }
return getPublicKey(identifier.subkeyId)
}
/**
* Return the secret key matching the given [SubkeyIdentifier].
*
* @return secret key
* @throws IllegalArgumentException if the identifier's primary key does not match the primary
* key of the key.
*/
fun getSecretKey(identifier: SubkeyIdentifier): PGPSecretKey? =
when (keys) {
is PGPSecretKeyRing -> {
require(identifier.primaryKeyId == publicKey.keyID) {
"Mismatching primary key ID."
}
2023-10-09 12:49:04 +02:00
keys.getSecretKey(identifier.subkeyId)
}
else -> null
}
/**
* Return true if the public key with the given key id is bound to the key ring properly.
*
* @param keyId key id
* @return true if key is bound validly
*/
fun isKeyValidlyBound(keyId: Long): Boolean {
val publicKey = keys.getPublicKey(keyId) ?: return false
// Primary key -> Check Primary Key Revocation
if (publicKey.keyID == this.publicKey.keyID) {
2023-09-12 22:07:17 +02:00
return if (signatures.primaryKeyRevocation != null &&
signatures.primaryKeyRevocation.isHardRevocation) {
2023-10-09 12:49:04 +02:00
false
} else signatures.primaryKeyRevocation == null
}
// Else Subkey -> Check Subkey Revocation
val binding = signatures.subkeyBindings[keyId]
val revocation = signatures.subkeyRevocations[keyId]
// No valid binding
2023-09-12 22:07:17 +02:00
if (binding == null || binding.isExpired(referenceDate)) {
2023-10-09 12:49:04 +02:00
return false
}
// Revocation
return if (revocation != null) {
2023-09-12 22:07:17 +02:00
if (revocation.isHardRevocation) {
2023-10-09 12:49:04 +02:00
// Subkey is hard revoked
false
} else {
// Key is soft-revoked, not yet re-bound
2023-09-12 22:07:17 +02:00
(revocation.isExpired(referenceDate) ||
!revocation.creationTime.after(binding.creationTime))
2023-10-09 12:49:04 +02:00
}
} else true
}
/**
* Return the current primary user-id of the key ring.
*
2023-10-09 12:49:04 +02:00
* <p>
* Note: If no user-id is marked as primary key using a {@link PrimaryUserID} packet, this
* method returns the first user-id on the key, otherwise null.
*
* @return primary user-id or null
*/
private fun findPrimaryUserId(): String? {
if (userIds.isEmpty()) {
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. */
fun isUserIdValid(userId: CharSequence) =
if (primaryUserId == null) {
false
} else {
isUserIdBound(primaryUserId) &&
(if (userId == primaryUserId) true else isUserIdBound(userId))
}
/** Return true, if the given user-ID is validly bound. */
fun isUserIdBound(userId: CharSequence) =
signatures.userIdCertifications[userId]?.let { sig ->
if (sig.isExpired(referenceDate)) {
// certification expired
return false
}
2023-10-09 12:49:04 +02:00
if (sig.hashedSubPackets.isPrimaryUserID) {
2023-09-13 15:05:58 +02:00
getKeyExpirationTimeAsDate(sig, publicKey)?.let { expirationDate ->
2023-10-09 12:49:04 +02:00
// key expired?
if (expirationDate < referenceDate) return false
}
}
2023-09-13 15:05:58 +02:00
signatures.userIdRevocations[userId]?.let { rev ->
2023-10-09 12:49:04 +02:00
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. */
fun getPreferredHashAlgorithms(userId: CharSequence): Set<HashAlgorithm> {
return getKeyAccessor(userId, keyId).preferredHashAlgorithms
}
/** [HashAlgorithm] preferences of the given key. */
fun getPreferredHashAlgorithms(keyId: Long): Set<HashAlgorithm> {
return KeyAccessor.SubKey(this, SubkeyIdentifier(keys, keyId)).preferredHashAlgorithms
}
/** [SymmetricKeyAlgorithm] preferences of the given user-ID. */
fun getPreferredSymmetricKeyAlgorithms(userId: CharSequence): Set<SymmetricKeyAlgorithm> {
return getKeyAccessor(userId, keyId).preferredSymmetricKeyAlgorithms
}
/** [SymmetricKeyAlgorithm] preferences of the given key. */
fun getPreferredSymmetricKeyAlgorithms(keyId: Long): Set<SymmetricKeyAlgorithm> {
return KeyAccessor.SubKey(this, SubkeyIdentifier(keys, keyId))
.preferredSymmetricKeyAlgorithms
}
/** [CompressionAlgorithm] preferences of the given user-ID. */
fun getPreferredCompressionAlgorithms(userId: CharSequence): Set<CompressionAlgorithm> {
return getKeyAccessor(userId, keyId).preferredCompressionAlgorithms
}
/** [CompressionAlgorithm] preferences of the given key. */
fun getPreferredCompressionAlgorithms(keyId: Long): Set<CompressionAlgorithm> {
return KeyAccessor.SubKey(this, SubkeyIdentifier(keys, keyId))
.preferredCompressionAlgorithms
}
val isUsableForThirdPartyCertification: Boolean =
isKeyValidlyBound(keyId) && getKeyFlagsOf(keyId).contains(KeyFlag.CERTIFY_OTHER)
private fun getKeyAccessor(userId: CharSequence?, keyId: Long): KeyAccessor {
if (getPublicKey(keyId) == null) {
throw NoSuchElementException(
"No subkey with key-id ${keyId.openPgpKeyId()} found on this key.")
}
if (userId != null && !userIds.contains(userId)) {
throw NoSuchElementException("No user-id '$userId' found on this key.")
}
return if (userId != null) {
KeyAccessor.ViaUserId(this, SubkeyIdentifier(keys, keyId), userId)
} else {
KeyAccessor.ViaKeyId(this, SubkeyIdentifier(keys, keyId))
}
}
companion object {
/** Evaluate the key for the given signature. */
@JvmStatic
fun evaluateForSignature(keys: PGPKeyRing, signature: PGPSignature) =
KeyRingInfo(keys, signature.creationTime!!)
private val PATTERN_EMAIL_FROM_USERID =
"<([a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+)>".toPattern()
private val PATTERN_EMAIL_EXPLICIT =
"^([a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+)$".toPattern()
@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 }
}
}
}
}