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

Further integration of OpenPGPCertificate into KeyRingInfo

This commit is contained in:
Paul Schaub 2025-02-05 09:56:56 +01:00
parent c9a7accec8
commit 5938ea9cff
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
5 changed files with 65 additions and 31 deletions

View file

@ -6,6 +6,7 @@ package org.pgpainless.key.info
import java.util.* import java.util.*
import openpgp.openPgpKeyId import openpgp.openPgpKeyId
import org.bouncycastle.bcpg.KeyIdentifier
import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.*
import org.bouncycastle.openpgp.api.OpenPGPCertificate import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.pgpainless.PGPainless import org.pgpainless.PGPainless
@ -46,7 +47,12 @@ class KeyRingInfo(
val publicKey: PGPPublicKey = keys.primaryKey.pgpPublicKey val publicKey: PGPPublicKey = keys.primaryKey.pgpPublicKey
/** Primary key ID. */ /** Primary key ID. */
val keyId: Long = publicKey.keyID val keyIdentifier: KeyIdentifier = publicKey.keyIdentifier
@Deprecated(
"Use of raw key-ids is deprecated in favor of key-identifiers",
replaceWith = ReplaceWith("keyIdentifier"))
val keyId: Long = keyIdentifier.keyId
/** Primary key fingerprint. */ /** Primary key fingerprint. */
val fingerprint: OpenPgpFingerprint = OpenPgpFingerprint.of(publicKey) val fingerprint: OpenPgpFingerprint = OpenPgpFingerprint.of(publicKey)
@ -154,7 +160,7 @@ class KeyRingInfo(
keys.pgpKeyRing.publicKeys keys.pgpKeyRing.publicKeys
.asSequence() .asSequence()
.filter { .filter {
if (it.keyID != keyId) { if (!it.keyIdentifier.matches(keyIdentifier)) {
if (signatures.subkeyBindings[it.keyID] == null) { if (signatures.subkeyBindings[it.keyID] == null) {
LOGGER.debug("Subkey ${it.keyID.openPgpKeyId()} has no binding signature.") LOGGER.debug("Subkey ${it.keyID.openPgpKeyId()} has no binding signature.")
return@filter false return@filter false
@ -208,7 +214,7 @@ class KeyRingInfo(
* *
* To check for keys that are actually usable to sign messages, use [isUsableForSigning]. * To check for keys that are actually usable to sign messages, use [isUsableForSigning].
*/ */
val isSigningCapable: Boolean = isKeyValidlyBound(keyId) && signingSubkeys.isNotEmpty() val isSigningCapable: Boolean = isKeyValidlyBound(keyIdentifier) && signingSubkeys.isNotEmpty()
/** Whether the key is actually usable to sign messages. */ /** Whether the key is actually usable to sign messages. */
val isUsableForSigning: Boolean = val isUsableForSigning: Boolean =
@ -218,7 +224,7 @@ class KeyRingInfo(
val preferredHashAlgorithms: Set<HashAlgorithm> val preferredHashAlgorithms: Set<HashAlgorithm>
get() = get() =
primaryUserId?.let { getPreferredHashAlgorithms(it) } primaryUserId?.let { getPreferredHashAlgorithms(it) }
?: getPreferredHashAlgorithms(keyId) ?: getPreferredHashAlgorithms(keyIdentifier)
/** /**
* [SymmetricKeyAlgorithm] preferences of the primary user-ID or if absent of the primary key. * [SymmetricKeyAlgorithm] preferences of the primary user-ID or if absent of the primary key.
@ -226,13 +232,13 @@ class KeyRingInfo(
val preferredSymmetricKeyAlgorithms: Set<SymmetricKeyAlgorithm> val preferredSymmetricKeyAlgorithms: Set<SymmetricKeyAlgorithm>
get() = get() =
primaryUserId?.let { getPreferredSymmetricKeyAlgorithms(it) } primaryUserId?.let { getPreferredSymmetricKeyAlgorithms(it) }
?: getPreferredSymmetricKeyAlgorithms(keyId) ?: getPreferredSymmetricKeyAlgorithms(keyIdentifier)
/** [CompressionAlgorithm] preferences of the primary user-ID or if absent, the primary key. */ /** [CompressionAlgorithm] preferences of the primary user-ID or if absent, the primary key. */
val preferredCompressionAlgorithms: Set<CompressionAlgorithm> val preferredCompressionAlgorithms: Set<CompressionAlgorithm>
get() = get() =
primaryUserId?.let { getPreferredCompressionAlgorithms(it) } primaryUserId?.let { getPreferredCompressionAlgorithms(it) }
?: getPreferredCompressionAlgorithms(keyId) ?: getPreferredCompressionAlgorithms(keyIdentifier)
/** /**
* Return the expiration date of the subkey with the provided fingerprint. * Return the expiration date of the subkey with the provided fingerprint.
@ -387,7 +393,7 @@ class KeyRingInfo(
* encryption-purpose. * encryption-purpose.
*/ */
fun isUsableForEncryption(purpose: EncryptionPurpose): Boolean { fun isUsableForEncryption(purpose: EncryptionPurpose): Boolean {
return isKeyValidlyBound(keyId) && getEncryptionSubkeys(purpose).isNotEmpty() return isKeyValidlyBound(keyIdentifier) && getEncryptionSubkeys(purpose).isNotEmpty()
} }
/** /**
@ -453,6 +459,9 @@ class KeyRingInfo(
fun getSubkeyRevocationSignature(keyId: Long): PGPSignature? = fun getSubkeyRevocationSignature(keyId: Long): PGPSignature? =
signatures.subkeyRevocations[keyId] signatures.subkeyRevocations[keyId]
fun getKeyFlagsOf(keyIdentifier: KeyIdentifier): List<KeyFlag> =
getKeyFlagsOf(keyIdentifier.keyId)
/** /**
* Return a list of [KeyFlags][KeyFlag] that apply to the subkey with the provided key id. * Return a list of [KeyFlags][KeyFlag] that apply to the subkey with the provided key id.
* *
@ -541,7 +550,7 @@ class KeyRingInfo(
* @return public key or null * @return public key or null
*/ */
fun getPublicKey(fingerprint: OpenPgpFingerprint): PGPPublicKey? = fun getPublicKey(fingerprint: OpenPgpFingerprint): PGPPublicKey? =
keys.pgpKeyRing.getPublicKey(fingerprint.keyId) keys.pgpKeyRing.getPublicKey(fingerprint.bytes)
/** /**
* Return the secret key with the given fingerprint. * Return the secret key with the given fingerprint.
@ -552,10 +561,14 @@ class KeyRingInfo(
fun getSecretKey(fingerprint: OpenPgpFingerprint): PGPSecretKey? = fun getSecretKey(fingerprint: OpenPgpFingerprint): PGPSecretKey? =
when (keys.pgpKeyRing) { when (keys.pgpKeyRing) {
is PGPSecretKeyRing -> is PGPSecretKeyRing ->
(keys.pgpKeyRing as PGPSecretKeyRing).getSecretKey(fingerprint.keyId) (keys.pgpKeyRing as PGPSecretKeyRing).getSecretKey(fingerprint.bytes)
else -> null else -> null
} }
fun getPublicKey(keyIdentifier: KeyIdentifier): PGPPublicKey? {
return keys.pgpKeyRing.getPublicKey(keyIdentifier)
}
/** /**
* Return the public key matching the given [SubkeyIdentifier]. * Return the public key matching the given [SubkeyIdentifier].
* *
@ -564,10 +577,10 @@ class KeyRingInfo(
* key of the key. * key of the key.
*/ */
fun getPublicKey(identifier: SubkeyIdentifier): PGPPublicKey? { fun getPublicKey(identifier: SubkeyIdentifier): PGPPublicKey? {
require(identifier.primaryKeyIdentifier.keyId == publicKey.keyID) { require(publicKey.keyIdentifier.equals(identifier.keyIdentifier)) {
"Mismatching primary key ID." "Mismatching primary key ID."
} }
return getPublicKey(identifier.subkeyId) return keys.pgpKeyRing.getPublicKey(identifier.subkeyIdentifier)
} }
/** /**
@ -580,7 +593,7 @@ class KeyRingInfo(
fun getSecretKey(identifier: SubkeyIdentifier): PGPSecretKey? = fun getSecretKey(identifier: SubkeyIdentifier): PGPSecretKey? =
when (keys.pgpKeyRing) { when (keys.pgpKeyRing) {
is PGPSecretKeyRing -> { is PGPSecretKeyRing -> {
require(identifier.primaryKeyIdentifier.keyId == publicKey.keyID) { require(publicKey.keyIdentifier.equals(identifier.keyIdentifier)) {
"Mismatching primary key ID." "Mismatching primary key ID."
} }
(keys.pgpKeyRing as PGPSecretKeyRing).getSecretKey(identifier.subkeyIdentifier) (keys.pgpKeyRing as PGPSecretKeyRing).getSecretKey(identifier.subkeyIdentifier)
@ -588,6 +601,10 @@ class KeyRingInfo(
else -> null else -> null
} }
fun isKeyValidlyBound(keyIdentifier: KeyIdentifier): Boolean {
return isKeyValidlyBound(keyIdentifier.keyId)
}
/** /**
* Return true if the public key with the given key id is bound to the key ring properly. * Return true if the public key with the given key id is bound to the key ring properly.
* *
@ -598,7 +615,7 @@ class KeyRingInfo(
val publicKey = keys.pgpKeyRing.getPublicKey(keyId) ?: return false val publicKey = keys.pgpKeyRing.getPublicKey(keyId) ?: return false
// Primary key -> Check Primary Key Revocation // Primary key -> Check Primary Key Revocation
if (publicKey.keyID == this.publicKey.keyID) { if (publicKey.keyIdentifier.matches(this.publicKey.keyIdentifier)) {
return if (signatures.primaryKeyRevocation != null && return if (signatures.primaryKeyRevocation != null &&
signatures.primaryKeyRevocation.isHardRevocation) { signatures.primaryKeyRevocation.isHardRevocation) {
false false
@ -631,8 +648,9 @@ class KeyRingInfo(
* Return the current primary user-id of the key ring. * Return the current primary user-id of the key ring.
* *
* <p> * <p>
* Note: If no user-id is marked as primary key using a [PrimaryUserID] packet, this method * Note: If no user-id is marked as primary key using a
* returns the first user-id on the key, otherwise null. * [org.bouncycastle.bcpg.sig.PrimaryUserID] packet, this method returns the first user-id on
* the key, otherwise null.
* *
* @return primary user-id or null * @return primary user-id or null
*/ */
@ -683,7 +701,11 @@ class KeyRingInfo(
/** [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> {
return getKeyAccessor(userId, keyId).preferredHashAlgorithms return getKeyAccessor(userId, keyIdentifier).preferredHashAlgorithms
}
fun getPreferredHashAlgorithms(keyIdentifier: KeyIdentifier): Set<HashAlgorithm> {
return getPreferredHashAlgorithms(keyIdentifier.keyId)
} }
/** [HashAlgorithm] preferences of the given key. */ /** [HashAlgorithm] preferences of the given key. */
@ -694,7 +716,13 @@ class KeyRingInfo(
/** [SymmetricKeyAlgorithm] preferences of the given user-ID. */ /** [SymmetricKeyAlgorithm] preferences of the given user-ID. */
fun getPreferredSymmetricKeyAlgorithms(userId: CharSequence): Set<SymmetricKeyAlgorithm> { fun getPreferredSymmetricKeyAlgorithms(userId: CharSequence): Set<SymmetricKeyAlgorithm> {
return getKeyAccessor(userId, keyId).preferredSymmetricKeyAlgorithms return getKeyAccessor(userId, keyIdentifier).preferredSymmetricKeyAlgorithms
}
fun getPreferredSymmetricKeyAlgorithms(
keyIdentifier: KeyIdentifier
): Set<SymmetricKeyAlgorithm> {
return getPreferredSymmetricKeyAlgorithms(keyIdentifier.keyId)
} }
/** [SymmetricKeyAlgorithm] preferences of the given key. */ /** [SymmetricKeyAlgorithm] preferences of the given key. */
@ -705,7 +733,11 @@ class KeyRingInfo(
/** [CompressionAlgorithm] preferences of the given user-ID. */ /** [CompressionAlgorithm] preferences of the given user-ID. */
fun getPreferredCompressionAlgorithms(userId: CharSequence): Set<CompressionAlgorithm> { fun getPreferredCompressionAlgorithms(userId: CharSequence): Set<CompressionAlgorithm> {
return getKeyAccessor(userId, keyId).preferredCompressionAlgorithms return getKeyAccessor(userId, keyIdentifier).preferredCompressionAlgorithms
}
fun getPreferredCompressionAlgorithms(keyIdentifier: KeyIdentifier): Set<CompressionAlgorithm> {
return getPreferredCompressionAlgorithms(keyIdentifier.keyId)
} }
/** [CompressionAlgorithm] preferences of the given key. */ /** [CompressionAlgorithm] preferences of the given key. */
@ -715,20 +747,21 @@ class KeyRingInfo(
} }
val isUsableForThirdPartyCertification: Boolean = val isUsableForThirdPartyCertification: Boolean =
isKeyValidlyBound(keyId) && getKeyFlagsOf(keyId).contains(KeyFlag.CERTIFY_OTHER) isKeyValidlyBound(keyIdentifier) &&
getKeyFlagsOf(keyIdentifier).contains(KeyFlag.CERTIFY_OTHER)
private fun getKeyAccessor(userId: CharSequence?, keyId: Long): KeyAccessor { private fun getKeyAccessor(userId: CharSequence?, keyIdentifier: KeyIdentifier): KeyAccessor {
if (getPublicKey(keyId) == null) { if (getPublicKey(keyIdentifier) == null) {
throw NoSuchElementException( throw NoSuchElementException("No subkey with key-id $keyIdentifier found on this key.")
"No subkey with key-id ${keyId.openPgpKeyId()} found on this key.")
} }
if (userId != null && !userIds.contains(userId)) { if (userId != null && !userIds.contains(userId)) {
throw NoSuchElementException("No user-id '$userId' found on this key.") throw NoSuchElementException("No user-id '$userId' found on this key.")
} }
return if (userId != null) { return if (userId != null) {
KeyAccessor.ViaUserId(this, SubkeyIdentifier(keys.pgpKeyRing, keyId), userId) KeyAccessor.ViaUserId(
this, SubkeyIdentifier(keys.pgpKeyRing, keyIdentifier.keyId), userId)
} else { } else {
KeyAccessor.ViaKeyId(this, SubkeyIdentifier(keys.pgpKeyRing, keyId)) KeyAccessor.ViaKeyId(this, SubkeyIdentifier(keys.pgpKeyRing, keyIdentifier.keyId))
} }
} }

View file

@ -54,7 +54,7 @@ public class ModifyKeys {
.modernKeyRing(userId, originalPassphrase); .modernKeyRing(userId, originalPassphrase);
KeyRingInfo info = PGPainless.inspectKeyRing(secretKey); KeyRingInfo info = PGPainless.inspectKeyRing(secretKey);
primaryKeyId = info.getKeyId(); primaryKeyId = info.getKeyIdentifier().getKeyId();
encryptionSubkeyId = info.getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getKeyID(); encryptionSubkeyId = info.getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getKeyID();
signingSubkeyId = info.getSigningSubkeys().get(0).getKeyID(); signingSubkeyId = info.getSigningSubkeys().get(0).getKeyID();
} }

View file

@ -15,6 +15,7 @@ import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import org.bouncycastle.bcpg.KeyIdentifier;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing;
@ -53,9 +54,9 @@ public class GenerateKeyWithoutPrimaryKeyFlagsTest {
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys); KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
assertTrue(info.getValidUserIds().contains("Alice")); assertTrue(info.getValidUserIds().contains("Alice"));
long primaryKeyId = info.getKeyId(); KeyIdentifier primaryKeyIdentifier = info.getKeyIdentifier();
assertTrue(info.getKeyFlagsOf("Alice").isEmpty()); assertTrue(info.getKeyFlagsOf("Alice").isEmpty());
assertTrue(info.getKeyFlagsOf(primaryKeyId).isEmpty()); assertTrue(info.getKeyFlagsOf(primaryKeyIdentifier).isEmpty());
assertFalse(info.isUsableForThirdPartyCertification()); assertFalse(info.isUsableForThirdPartyCertification());
// Key without CERTIFY_OTHER flag cannot be used to certify other keys // Key without CERTIFY_OTHER flag cannot be used to certify other keys

View file

@ -66,8 +66,8 @@ public class KeyRingInfoTest {
KeyRingInfo sInfo = PGPainless.inspectKeyRing(secretKeys); KeyRingInfo sInfo = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo pInfo = PGPainless.inspectKeyRing(publicKeys); KeyRingInfo pInfo = PGPainless.inspectKeyRing(publicKeys);
assertEquals(TestKeys.EMIL_KEY_ID, sInfo.getKeyId()); assertEquals(TestKeys.EMIL_KEY_ID, sInfo.getKeyIdentifier().getKeyId());
assertEquals(TestKeys.EMIL_KEY_ID, pInfo.getKeyId()); assertEquals(TestKeys.EMIL_KEY_ID, pInfo.getKeyIdentifier().getKeyId());
assertEquals(TestKeys.EMIL_FINGERPRINT, sInfo.getFingerprint()); assertEquals(TestKeys.EMIL_FINGERPRINT, sInfo.getFingerprint());
assertEquals(TestKeys.EMIL_FINGERPRINT, pInfo.getFingerprint()); assertEquals(TestKeys.EMIL_FINGERPRINT, pInfo.getFingerprint());
assertEquals(PublicKeyAlgorithm.ECDSA, sInfo.getAlgorithm()); assertEquals(PublicKeyAlgorithm.ECDSA, sInfo.getAlgorithm());

View file

@ -90,7 +90,7 @@ public class FixUserIdDoesNotBreakEncryptionCapabilityTest {
assertEquals(userIdBefore, before.getPrimaryUserId()); assertEquals(userIdBefore, before.getPrimaryUserId());
assertEquals(userIdAfter, after.getPrimaryUserId()); assertEquals(userIdAfter, after.getPrimaryUserId());
assertTrue(after.isKeyValidlyBound(after.getKeyId())); assertTrue(after.isKeyValidlyBound(after.getKeyIdentifier()));
assertTrue(before.isUsableForEncryption()); assertTrue(before.isUsableForEncryption());
assertTrue(before.isUsableForSigning()); assertTrue(before.isUsableForSigning());
assertTrue(before.isUserIdValid(userIdBefore)); assertTrue(before.isUserIdValid(userIdBefore));