diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/OpenPgpKeyBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKey.kt similarity index 82% rename from pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/OpenPgpKeyBuilder.kt rename to pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKey.kt index 16408c2c..9419693d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/OpenPgpKeyBuilder.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKey.kt @@ -24,31 +24,51 @@ import org.pgpainless.signature.subpackets.SelfSignatureSubpackets * @param referenceTime reference time for key generation * @param preferences set of preferred algorithms and enabled features */ -open class OpenPgpKeyBuilder( - protected val policy: Policy, - protected val referenceTime: Date = Date(), - protected val preferences: AlgorithmSuite = policy.keyGenerationAlgorithmSuite +open class GenerateOpenPgpKey( + private val policy: Policy, + private val referenceTime: Date = Date(), + private val preferences: AlgorithmSuite = policy.keyGenerationAlgorithmSuite ) { + abstract class OpenPgpKeyBuilder( + protected val policy: Policy, + protected val referenceTime: Date, + protected val preferences: AlgorithmSuite + ) { + + /** + * Make sure, that the chosen [KeyType] is allowed. + */ + open fun sanitizePublicKeyAlgorithms(keyType: KeyType, policy: Policy) { + verifyAlgorithmComplianceWithPolicy(keyType, policy) + } + + /** + * Make sure, that the chosen [KeyType] complies to the given [Policy] by comparing it to the + * [Policy.PublicKeyAlgorithmPolicy]. + * + * @throws IllegalArgumentException if [keyType] fails to be accepted by [policy] + */ + private fun verifyAlgorithmComplianceWithPolicy(keyType: KeyType, policy: Policy) { + val algorithm = keyType.algorithm + val bitStrength = keyType.bitStrength + require(policy.publicKeyAlgorithmPolicy.isAcceptable(algorithm, bitStrength)) { + "Public key algorithm policy violation: $algorithm with bit strength $bitStrength is not acceptable." + } + } + } + /** * Build an OpenPGP v4 key. The result will be a key compliant to RFC4880, RFC6637. * * @param keyType type of the primary key * @param flags key flags for the primary key. Defaults to [KeyFlag.CERTIFY_OTHER]. - * @return [V4OpenPgpKeyBuilder] which can be further modified, e.g. add subkeys, user-ids etc. + * @return [V4GenerateOpenPgpKey] which can be further modified, e.g. add subkeys, user-ids etc. */ fun buildV4Key( keyType: KeyType, flags: List? = listOf(KeyFlag.CERTIFY_OTHER) - ): V4OpenPgpKeyBuilder = V4OpenPgpKeyBuilder(keyType, flags, policy, referenceTime, preferences) - - internal fun verifyAlgorithmComplianceWithPolicy(keyType: KeyType, policy: Policy) { - val algorithm = keyType.algorithm - val bitStrength = keyType.bitStrength - require(policy.publicKeyAlgorithmPolicy.isAcceptable(algorithm, bitStrength)) { - "Public key algorithm policy violation: $algorithm with bit strength $bitStrength is not acceptable." - } - } + ): V4GenerateOpenPgpKey = V4GenerateOpenPgpKey(keyType, flags, policy, referenceTime, preferences) /** * Builder for version 4 OpenPGP keys. @@ -59,7 +79,7 @@ open class OpenPgpKeyBuilder( * @param referenceTime reference time for key generation * @param preferences set of algorithm preferences and enabled features for the key */ - class V4OpenPgpKeyBuilder + class V4GenerateOpenPgpKey internal constructor( primaryKeyType: KeyType, primaryFlags: List?, @@ -69,7 +89,7 @@ open class OpenPgpKeyBuilder( ) : OpenPgpKeyBuilder(policy, referenceTime, preferences) { init { - verifyAlgorithmComplianceWithPolicy(primaryKeyType, policy) + sanitizePublicKeyAlgorithms(primaryKeyType, policy) } private val primaryKey = @@ -152,7 +172,7 @@ open class OpenPgpKeyBuilder( subkeyBuilder: BaseOpenPgpKeyBuilder.BaseV4SubkeyBuilder, subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop() ) = apply { - verifyAlgorithmComplianceWithPolicy(subkeyBuilder.type, policy) + sanitizePublicKeyAlgorithms(subkeyBuilder.type, policy) subkeys.add(subkeyBuilder.bindingSignature(subpacketsCallback = subpacketsCallback)) } diff --git a/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/OpenPgpKeyBuilderTest.kt b/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKeyTest.kt similarity index 91% rename from pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/OpenPgpKeyBuilderTest.kt rename to pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKeyTest.kt index 9cbad1d5..65a3956b 100644 --- a/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/OpenPgpKeyBuilderTest.kt +++ b/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKeyTest.kt @@ -15,13 +15,13 @@ import org.pgpainless.key.protection.SecretKeyRingProtector import org.pgpainless.policy.Policy import org.pgpainless.util.DateUtil -class OpenPgpKeyBuilderTest { +class GenerateOpenPgpKeyTest { @Test fun test() { val date = DateUtil.parseUTCDate("2020-04-01 10:00:00 UTC") val key = - OpenPgpKeyBuilder(Policy.getInstance(), date) + GenerateOpenPgpKey(Policy.getInstance(), date) .buildV4Key(KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.CERTIFY_OTHER)) .addUserId("Alice") .addUserAttribute( @@ -37,7 +37,7 @@ class OpenPgpKeyBuilderTest { @Test fun minimal() { val key = - OpenPgpKeyBuilder(Policy.getInstance()) + GenerateOpenPgpKey(Policy.getInstance()) .buildV4Key(KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.CERTIFY_OTHER)) .build() println(PGPainless.asciiArmor(key)) @@ -46,7 +46,7 @@ class OpenPgpKeyBuilderTest { @Test fun minimalWithUserId() { val key = - OpenPgpKeyBuilder(Policy.getInstance()) + GenerateOpenPgpKey(Policy.getInstance()) .buildV4Key(KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.CERTIFY_OTHER)) .addUserId("Alice ") .build() @@ -60,7 +60,7 @@ class OpenPgpKeyBuilderTest { Policy( publicKeyAlgorithmPolicy = Policy.PublicKeyAlgorithmPolicy(mapOf(PublicKeyAlgorithm.RSA_GENERAL to 4096))) - val builder = OpenPgpKeyBuilder(policy) + val builder = GenerateOpenPgpKey(policy) assertThrows { builder.buildV4Key(KeyType.RSA(RsaLength._3072)) // too weak diff --git a/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/MalformedKeyGenerationTest.kt b/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/MalformedKeyGenerationTest.kt new file mode 100644 index 00000000..c3c16a7b --- /dev/null +++ b/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/MalformedKeyGenerationTest.kt @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2024 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.key.generation + +import org.bouncycastle.bcpg.sig.PrimaryUserID +import org.junit.jupiter.api.Test +import org.pgpainless.PGPainless +import org.pgpainless.key.generation.type.KeyType +import org.pgpainless.key.generation.type.eddsa.EdDSACurve +import org.pgpainless.policy.Policy +import org.pgpainless.signature.subpackets.SelfSignatureSubpackets + +class MalformedKeyGenerationTest { + + @Test + fun malformedPrimaryUserIdSubpacket() { + val userId = "Alice " + val key = GenerateOpenPgpKey(Policy.getInstance()) + .buildV4Key(KeyType.EDDSA(EdDSACurve._Ed25519)) + .addUserId(userId, + SelfSignatureSubpackets.applyHashed { + setPrimaryUserId(PrimaryUserID(false, false, byteArrayOf(0x02))) + }) + .build() + + println(PGPainless.asciiArmor(key)) + + PGPainless.readKeyRing().secretKeyRing(key.encoded)!! + // TODO: Check interpretation of faulty PrimaryUserID packet + } +}