From 5938ea9cff7d8e2a7a662dc3468feef0ee5242db Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 5 Feb 2025 09:56:56 +0100 Subject: [PATCH] Further integration of OpenPGPCertificate into KeyRingInfo --- .../org/pgpainless/key/info/KeyRingInfo.kt | 83 +++++++++++++------ .../org/pgpainless/example/ModifyKeys.java | 2 +- ...GenerateKeyWithoutPrimaryKeyFlagsTest.java | 5 +- .../pgpainless/key/info/KeyRingInfoTest.java | 4 +- ...dDoesNotBreakEncryptionCapabilityTest.java | 2 +- 5 files changed, 65 insertions(+), 31 deletions(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt index 4f4d7587..007756a6 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt @@ -6,6 +6,7 @@ package org.pgpainless.key.info import java.util.* import openpgp.openPgpKeyId +import org.bouncycastle.bcpg.KeyIdentifier import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.api.OpenPGPCertificate import org.pgpainless.PGPainless @@ -46,7 +47,12 @@ class KeyRingInfo( val publicKey: PGPPublicKey = keys.primaryKey.pgpPublicKey /** 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. */ val fingerprint: OpenPgpFingerprint = OpenPgpFingerprint.of(publicKey) @@ -154,7 +160,7 @@ class KeyRingInfo( keys.pgpKeyRing.publicKeys .asSequence() .filter { - if (it.keyID != keyId) { + if (!it.keyIdentifier.matches(keyIdentifier)) { if (signatures.subkeyBindings[it.keyID] == null) { LOGGER.debug("Subkey ${it.keyID.openPgpKeyId()} has no binding signature.") return@filter false @@ -208,7 +214,7 @@ class KeyRingInfo( * * 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. */ val isUsableForSigning: Boolean = @@ -218,7 +224,7 @@ class KeyRingInfo( val preferredHashAlgorithms: Set get() = primaryUserId?.let { getPreferredHashAlgorithms(it) } - ?: getPreferredHashAlgorithms(keyId) + ?: getPreferredHashAlgorithms(keyIdentifier) /** * [SymmetricKeyAlgorithm] preferences of the primary user-ID or if absent of the primary key. @@ -226,13 +232,13 @@ class KeyRingInfo( val preferredSymmetricKeyAlgorithms: Set get() = primaryUserId?.let { getPreferredSymmetricKeyAlgorithms(it) } - ?: getPreferredSymmetricKeyAlgorithms(keyId) + ?: getPreferredSymmetricKeyAlgorithms(keyIdentifier) /** [CompressionAlgorithm] preferences of the primary user-ID or if absent, the primary key. */ val preferredCompressionAlgorithms: Set get() = primaryUserId?.let { getPreferredCompressionAlgorithms(it) } - ?: getPreferredCompressionAlgorithms(keyId) + ?: getPreferredCompressionAlgorithms(keyIdentifier) /** * Return the expiration date of the subkey with the provided fingerprint. @@ -387,7 +393,7 @@ class KeyRingInfo( * encryption-purpose. */ 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? = signatures.subkeyRevocations[keyId] + fun getKeyFlagsOf(keyIdentifier: KeyIdentifier): List = + getKeyFlagsOf(keyIdentifier.keyId) + /** * 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 */ fun getPublicKey(fingerprint: OpenPgpFingerprint): PGPPublicKey? = - keys.pgpKeyRing.getPublicKey(fingerprint.keyId) + keys.pgpKeyRing.getPublicKey(fingerprint.bytes) /** * Return the secret key with the given fingerprint. @@ -552,10 +561,14 @@ class KeyRingInfo( fun getSecretKey(fingerprint: OpenPgpFingerprint): PGPSecretKey? = when (keys.pgpKeyRing) { is PGPSecretKeyRing -> - (keys.pgpKeyRing as PGPSecretKeyRing).getSecretKey(fingerprint.keyId) + (keys.pgpKeyRing as PGPSecretKeyRing).getSecretKey(fingerprint.bytes) else -> null } + fun getPublicKey(keyIdentifier: KeyIdentifier): PGPPublicKey? { + return keys.pgpKeyRing.getPublicKey(keyIdentifier) + } + /** * Return the public key matching the given [SubkeyIdentifier]. * @@ -564,10 +577,10 @@ class KeyRingInfo( * key of the key. */ fun getPublicKey(identifier: SubkeyIdentifier): PGPPublicKey? { - require(identifier.primaryKeyIdentifier.keyId == publicKey.keyID) { + require(publicKey.keyIdentifier.equals(identifier.keyIdentifier)) { "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? = when (keys.pgpKeyRing) { is PGPSecretKeyRing -> { - require(identifier.primaryKeyIdentifier.keyId == publicKey.keyID) { + require(publicKey.keyIdentifier.equals(identifier.keyIdentifier)) { "Mismatching primary key ID." } (keys.pgpKeyRing as PGPSecretKeyRing).getSecretKey(identifier.subkeyIdentifier) @@ -588,6 +601,10 @@ class KeyRingInfo( 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. * @@ -598,7 +615,7 @@ class KeyRingInfo( val publicKey = keys.pgpKeyRing.getPublicKey(keyId) ?: return false // Primary key -> Check Primary Key Revocation - if (publicKey.keyID == this.publicKey.keyID) { + if (publicKey.keyIdentifier.matches(this.publicKey.keyIdentifier)) { return if (signatures.primaryKeyRevocation != null && signatures.primaryKeyRevocation.isHardRevocation) { false @@ -631,8 +648,9 @@ class KeyRingInfo( * Return the current primary user-id of the key ring. * *

- * Note: If no user-id is marked as primary key using a [PrimaryUserID] packet, this method - * returns the first user-id on the key, otherwise null. + * Note: If no user-id is marked as primary key using a + * [org.bouncycastle.bcpg.sig.PrimaryUserID] packet, this method returns the first user-id on + * the key, otherwise null. * * @return primary user-id or null */ @@ -683,7 +701,11 @@ class KeyRingInfo( /** [HashAlgorithm] preferences of the given user-ID. */ fun getPreferredHashAlgorithms(userId: CharSequence): Set { - return getKeyAccessor(userId, keyId).preferredHashAlgorithms + return getKeyAccessor(userId, keyIdentifier).preferredHashAlgorithms + } + + fun getPreferredHashAlgorithms(keyIdentifier: KeyIdentifier): Set { + return getPreferredHashAlgorithms(keyIdentifier.keyId) } /** [HashAlgorithm] preferences of the given key. */ @@ -694,7 +716,13 @@ class KeyRingInfo( /** [SymmetricKeyAlgorithm] preferences of the given user-ID. */ fun getPreferredSymmetricKeyAlgorithms(userId: CharSequence): Set { - return getKeyAccessor(userId, keyId).preferredSymmetricKeyAlgorithms + return getKeyAccessor(userId, keyIdentifier).preferredSymmetricKeyAlgorithms + } + + fun getPreferredSymmetricKeyAlgorithms( + keyIdentifier: KeyIdentifier + ): Set { + return getPreferredSymmetricKeyAlgorithms(keyIdentifier.keyId) } /** [SymmetricKeyAlgorithm] preferences of the given key. */ @@ -705,7 +733,11 @@ class KeyRingInfo( /** [CompressionAlgorithm] preferences of the given user-ID. */ fun getPreferredCompressionAlgorithms(userId: CharSequence): Set { - return getKeyAccessor(userId, keyId).preferredCompressionAlgorithms + return getKeyAccessor(userId, keyIdentifier).preferredCompressionAlgorithms + } + + fun getPreferredCompressionAlgorithms(keyIdentifier: KeyIdentifier): Set { + return getPreferredCompressionAlgorithms(keyIdentifier.keyId) } /** [CompressionAlgorithm] preferences of the given key. */ @@ -715,20 +747,21 @@ class KeyRingInfo( } 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 { - if (getPublicKey(keyId) == null) { - throw NoSuchElementException( - "No subkey with key-id ${keyId.openPgpKeyId()} found on this key.") + private fun getKeyAccessor(userId: CharSequence?, keyIdentifier: KeyIdentifier): KeyAccessor { + if (getPublicKey(keyIdentifier) == null) { + throw NoSuchElementException("No subkey with key-id $keyIdentifier 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.pgpKeyRing, keyId), userId) + KeyAccessor.ViaUserId( + this, SubkeyIdentifier(keys.pgpKeyRing, keyIdentifier.keyId), userId) } else { - KeyAccessor.ViaKeyId(this, SubkeyIdentifier(keys.pgpKeyRing, keyId)) + KeyAccessor.ViaKeyId(this, SubkeyIdentifier(keys.pgpKeyRing, keyIdentifier.keyId)) } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/example/ModifyKeys.java b/pgpainless-core/src/test/java/org/pgpainless/example/ModifyKeys.java index 110761e6..fdc91cd2 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/example/ModifyKeys.java +++ b/pgpainless-core/src/test/java/org/pgpainless/example/ModifyKeys.java @@ -54,7 +54,7 @@ public class ModifyKeys { .modernKeyRing(userId, originalPassphrase); KeyRingInfo info = PGPainless.inspectKeyRing(secretKey); - primaryKeyId = info.getKeyId(); + primaryKeyId = info.getKeyIdentifier().getKeyId(); encryptionSubkeyId = info.getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getKeyID(); signingSubkeyId = info.getSigningSubkeys().get(0).getKeyID(); } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithoutPrimaryKeyFlagsTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithoutPrimaryKeyFlagsTest.java index e477aeef..ea0dbc73 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithoutPrimaryKeyFlagsTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithoutPrimaryKeyFlagsTest.java @@ -15,6 +15,7 @@ import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; +import org.bouncycastle.bcpg.KeyIdentifier; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; @@ -53,9 +54,9 @@ public class GenerateKeyWithoutPrimaryKeyFlagsTest { KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys); assertTrue(info.getValidUserIds().contains("Alice")); - long primaryKeyId = info.getKeyId(); + KeyIdentifier primaryKeyIdentifier = info.getKeyIdentifier(); assertTrue(info.getKeyFlagsOf("Alice").isEmpty()); - assertTrue(info.getKeyFlagsOf(primaryKeyId).isEmpty()); + assertTrue(info.getKeyFlagsOf(primaryKeyIdentifier).isEmpty()); assertFalse(info.isUsableForThirdPartyCertification()); // Key without CERTIFY_OTHER flag cannot be used to certify other keys diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/info/KeyRingInfoTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/info/KeyRingInfoTest.java index edf40b6d..ca284cdc 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/info/KeyRingInfoTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/info/KeyRingInfoTest.java @@ -66,8 +66,8 @@ public class KeyRingInfoTest { KeyRingInfo sInfo = PGPainless.inspectKeyRing(secretKeys); KeyRingInfo pInfo = PGPainless.inspectKeyRing(publicKeys); - assertEquals(TestKeys.EMIL_KEY_ID, sInfo.getKeyId()); - assertEquals(TestKeys.EMIL_KEY_ID, pInfo.getKeyId()); + assertEquals(TestKeys.EMIL_KEY_ID, sInfo.getKeyIdentifier().getKeyId()); + assertEquals(TestKeys.EMIL_KEY_ID, pInfo.getKeyIdentifier().getKeyId()); assertEquals(TestKeys.EMIL_FINGERPRINT, sInfo.getFingerprint()); assertEquals(TestKeys.EMIL_FINGERPRINT, pInfo.getFingerprint()); assertEquals(PublicKeyAlgorithm.ECDSA, sInfo.getAlgorithm()); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/FixUserIdDoesNotBreakEncryptionCapabilityTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/FixUserIdDoesNotBreakEncryptionCapabilityTest.java index c9683ab6..3053bc7b 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/FixUserIdDoesNotBreakEncryptionCapabilityTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/FixUserIdDoesNotBreakEncryptionCapabilityTest.java @@ -90,7 +90,7 @@ public class FixUserIdDoesNotBreakEncryptionCapabilityTest { assertEquals(userIdBefore, before.getPrimaryUserId()); assertEquals(userIdAfter, after.getPrimaryUserId()); - assertTrue(after.isKeyValidlyBound(after.getKeyId())); + assertTrue(after.isKeyValidlyBound(after.getKeyIdentifier())); assertTrue(before.isUsableForEncryption()); assertTrue(before.isUsableForSigning()); assertTrue(before.isUserIdValid(userIdBefore));