From b4240ac9f79c59c81f314bf32da9eacaa909c219 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 29 Feb 2024 14:40:06 +0100 Subject: [PATCH] Improve Key Generation API --- .../kotlin/openpgp/CharSequenceExtensions.kt | 24 ++++ .../main/kotlin/org/pgpainless/PGPainless.kt | 16 ++- .../key/generation/KeyRingTemplates.kt | 101 ++++++---------- .../key/generation/OpenPgpKeyGenerator.kt | 78 +++++++----- .../KeyWithInacceptableSelfSignatureTest.kt | 30 +++-- .../generation/MalformedKeyGenerationTest.kt | 12 +- .../key/generation/OpenPgpKeyGeneratorTest.kt | 112 +++++++++++------- 7 files changed, 219 insertions(+), 154 deletions(-) create mode 100644 pgpainless-core/src/main/kotlin/openpgp/CharSequenceExtensions.kt diff --git a/pgpainless-core/src/main/kotlin/openpgp/CharSequenceExtensions.kt b/pgpainless-core/src/main/kotlin/openpgp/CharSequenceExtensions.kt new file mode 100644 index 00000000..060dba35 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/openpgp/CharSequenceExtensions.kt @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2024 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package openpgp + +import org.pgpainless.util.Passphrase + +/** + * Extension function to convert a nullable [CharSequence] into an [Array]. Iff [this] is `null`, + * then return an empty array, otherwise return an array only consisting of [this]. + * + * @return array + */ +fun CharSequence?.toArray(): Array = this?.let { arrayOf(it) } ?: emptyArray() + +/** + * Return a [Passphrase] from this [CharSequence]. Iff [this] is `null` or blank, then this method + * returns [Passphrase.emptyPassphrase], otherwise it returns [Passphrase.fromPassword]. + * + * @return passphrase + */ +fun CharSequence?.toPassphrase(): Passphrase = + this?.let { Passphrase.fromPassword(it) } ?: Passphrase.emptyPassphrase() diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt index d866ac93..a43f5922 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt @@ -15,6 +15,7 @@ import org.pgpainless.encryption_signing.EncryptionBuilder import org.pgpainless.key.certification.CertifyCertificate import org.pgpainless.key.generation.KeyRingBuilder import org.pgpainless.key.generation.KeyRingTemplates +import org.pgpainless.key.generation.OpenPgpKeyGenerator import org.pgpainless.key.info.KeyRingInfo import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditor import org.pgpainless.key.parsing.KeyRingReader @@ -26,19 +27,30 @@ class PGPainless private constructor() { companion object { + /** Generate an OpenPGP key. */ + @JvmOverloads + @JvmStatic + fun generateOpenPgpKey(policy: Policy = getPolicy()) = OpenPgpKeyGenerator(policy) + /** * Generate a fresh OpenPGP key ring from predefined templates. * * @return templates */ - @JvmStatic fun generateKeyRing() = KeyRingTemplates() + @Deprecated( + "Deprecated in favor of new API", + ReplaceWith("generateOpenPgpKey().buildV4Key().fromTemplate()")) + @JvmStatic + fun generateKeyRing() = KeyRingTemplates() /** * Build a custom OpenPGP key ring. * * @return builder */ - @JvmStatic fun buildKeyRing() = KeyRingBuilder() + @Deprecated("Deprecated in favor of new API", ReplaceWith("generateOpenPgpKey()")) + @JvmStatic + fun buildKeyRing() = KeyRingBuilder() /** * Read an existing OpenPGP key ring. diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingTemplates.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingTemplates.kt index e50312aa..38256307 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingTemplates.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingTemplates.kt @@ -4,6 +4,8 @@ package org.pgpainless.key.generation +import openpgp.toArray +import openpgp.toPassphrase import org.bouncycastle.openpgp.PGPSecretKeyRing import org.pgpainless.algorithm.KeyFlag import org.pgpainless.key.generation.type.KeyType @@ -30,15 +32,13 @@ class KeyRingTemplates { length: RsaLength, passphrase: Passphrase = Passphrase.emptyPassphrase() ): PGPSecretKeyRing = - OpenPgpKeyGenerator.buildV4Key() - .setPrimaryKey(KeyType.RSA(length), listOf(KeyFlag.CERTIFY_OTHER)) { - if (userId != null) { - addUserId(userId) - } - } - .addSigningSubkey(KeyType.RSA(length)) - .addEncryptionSubkey(KeyType.RSA(length)) - .build(passphrase) + OpenPgpKeyGenerator() + .buildV4Key() + .fromTemplate() + .composedRsa( + userId = userId.toArray(), + length = length, + protector = SecretKeyRingProtector.unlockAnyKeyWith(passphrase)) /** * Generate an RSA OpenPGP key consisting of an RSA primary key used for certification, a @@ -50,14 +50,11 @@ class KeyRingTemplates { * keys. * @return key */ - fun rsaKeyRing(userId: CharSequence?, length: RsaLength, password: String?): PGPSecretKeyRing = - password.let { - if (it.isNullOrBlank()) { - rsaKeyRing(userId, length, Passphrase.emptyPassphrase()) - } else { - rsaKeyRing(userId, length, Passphrase.fromPassword(it)) - } - } + fun rsaKeyRing( + userId: CharSequence?, + length: RsaLength, + password: CharSequence? + ): PGPSecretKeyRing = rsaKeyRing(userId, length, password.toPassphrase()) /** * Creates a simple RSA KeyPair of length {@code length} with user-id {@code userId}. The @@ -75,19 +72,13 @@ class KeyRingTemplates { length: RsaLength, passphrase: Passphrase = Passphrase.emptyPassphrase() ): PGPSecretKeyRing = - OpenPgpKeyGenerator.buildV4Key() - .setPrimaryKey( - KeyType.RSA(length), - listOf( - KeyFlag.CERTIFY_OTHER, - KeyFlag.SIGN_DATA, - KeyFlag.ENCRYPT_COMMS, - KeyFlag.ENCRYPT_STORAGE)) { - if (userId != null) { - addUserId(userId) - } - } - .build(passphrase) + OpenPgpKeyGenerator() + .buildV4Key() + .fromTemplate() + .singleRsa( + userId = userId.toArray(), + length = length, + protector = SecretKeyRingProtector.unlockAnyKeyWith(passphrase)) /** * Creates a simple RSA KeyPair of length {@code length} with user-id {@code userId}. The @@ -99,14 +90,8 @@ class KeyRingTemplates { * @param password Password of the key. Can be null or blank for unencrypted keys. * @return {@link PGPSecretKeyRing} containing the KeyPair. */ - fun simpleRsaKeyRing(userId: CharSequence?, length: RsaLength, password: String?) = - password.let { - if (it.isNullOrBlank()) { - simpleRsaKeyRing(userId, length, Passphrase.emptyPassphrase()) - } else { - simpleRsaKeyRing(userId, length, Passphrase.fromPassword(it)) - } - } + fun simpleRsaKeyRing(userId: CharSequence?, length: RsaLength, password: CharSequence?) = + simpleRsaKeyRing(userId, length, password.toPassphrase()) /** * Creates a key ring consisting of an ed25519 EdDSA primary key and a X25519 XDH subkey. The @@ -122,7 +107,8 @@ class KeyRingTemplates { userId: CharSequence?, passphrase: Passphrase = Passphrase.emptyPassphrase() ): PGPSecretKeyRing = - OpenPgpKeyGenerator.buildV4Key() + OpenPgpKeyGenerator() + .buildV4Key() .setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)) { @@ -142,14 +128,8 @@ class KeyRingTemplates { * @param password Password of the private key. Can be null or blank for an unencrypted key. * @return {@link PGPSecretKeyRing} containing the key pairs. */ - fun simpleEcKeyRing(userId: CharSequence?, password: String?): PGPSecretKeyRing = - password.let { - if (it.isNullOrBlank()) { - simpleEcKeyRing(userId, Passphrase.emptyPassphrase()) - } else { - simpleEcKeyRing(userId, Passphrase.fromPassword(it)) - } - } + fun simpleEcKeyRing(userId: CharSequence?, password: CharSequence?): PGPSecretKeyRing = + simpleEcKeyRing(userId, password.toPassphrase()) /** * Generate a modern PGP key ring consisting of an ed25519 EdDSA primary key which is used to @@ -164,17 +144,12 @@ class KeyRingTemplates { userId: CharSequence?, passphrase: Passphrase = Passphrase.emptyPassphrase() ): PGPSecretKeyRing = - OpenPgpKeyGenerator.buildV4Key() - .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.CERTIFY_OTHER)) { - if (userId != null) { - addUserId(userId) - } - } - .addEncryptionSubkey(KeyType.XDH(XDHSpec._X25519)) - .addSigningSubkey(KeyType.EDDSA(EdDSACurve._Ed25519)) - .build( - if (passphrase.isEmpty) SecretKeyRingProtector.unprotectedKeys() - else SecretKeyRingProtector.unlockAnyKeyWith(passphrase)) + OpenPgpKeyGenerator() + .buildV4Key() + .fromTemplate() + .ed25519Curve25519( + userId = userId.toArray(), + protector = SecretKeyRingProtector.unlockAnyKeyWith(passphrase)) /** * Generate a modern PGP key ring consisting of an ed25519 EdDSA primary key which is used to @@ -184,12 +159,6 @@ class KeyRingTemplates { * @param password passphrase for the private key. Can be null or blank for an unencrypted key. * @return key ring */ - fun modernKeyRing(userId: CharSequence?, password: String?): PGPSecretKeyRing = - password.let { - if (it.isNullOrBlank()) { - modernKeyRing(userId, Passphrase.emptyPassphrase()) - } else { - modernKeyRing(userId, Passphrase.fromPassword(it)) - } - } + fun modernKeyRing(userId: CharSequence?, password: CharSequence?): PGPSecretKeyRing = + modernKeyRing(userId, password.toPassphrase()) } diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/OpenPgpKeyGenerator.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/OpenPgpKeyGenerator.kt index bed88d69..1471cd30 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/OpenPgpKeyGenerator.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/OpenPgpKeyGenerator.kt @@ -24,6 +24,7 @@ import org.pgpainless.bouncycastle.extensions.plusCertification import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.key.generation.DefinePrimaryKey.PrimaryKeyBuilder import org.pgpainless.key.generation.DefineSubkeys.SubkeyBuilder +import org.pgpainless.key.generation.OpenPgpKeyTemplates.Companion.v4 import org.pgpainless.key.generation.type.KeyType import org.pgpainless.key.generation.type.eddsa.EdDSACurve import org.pgpainless.key.generation.type.rsa.RsaLength @@ -37,8 +38,16 @@ import org.pgpainless.signature.builder.SubkeyBindingSignatureBuilder import org.pgpainless.signature.subpackets.SelfSignatureSubpackets import org.pgpainless.util.Passphrase +/** + * Function block that is applied to the OpenPGP [PrimaryKeyBuilder]. Within this block, you add + * User-IDs, User-Attributes and Direct-Key signatures on the primary key. + */ typealias PrimaryKeyBlock = (PrimaryKeyBuilder.() -> Unit) +/** + * Function block that is applied to an OpenPGP [SubkeyBuilder]. Here you typically add + * subkey-binding signatures. + */ typealias SubkeyBlock = (SubkeyBuilder.() -> Unit) /** @@ -54,25 +63,20 @@ typealias SubkeyBlock = (SubkeyBuilder.() -> Unit) * You can switch from the opinionated API to the unopinionated API by calling `unopinionated()` on * the builder. */ -class OpenPgpKeyGenerator internal constructor() { +class OpenPgpKeyGenerator(private val policy: Policy = PGPainless.getPolicy()) { - companion object { - /** - * Build a version 4 OpenPGP secret key. - * - * @param policy policy to ensure algorithm compliance and to determine default algorithms - * @param creationTime creation time for the secret key - * @param preferences suite of algorithm preferences and enabled features - */ - @JvmStatic - @JvmOverloads - fun buildV4Key( - policy: Policy = PGPainless.getPolicy(), - creationTime: Date = Date(), - preferences: AlgorithmSuite = policy.keyGenerationAlgorithmSuite - ): OpinionatedDefinePrimaryKeyV4 { - return OpinionatedDefinePrimaryKeyV4(policy, creationTime, preferences) - } + /** + * Build a version 4 OpenPGP secret key. + * + * @param creationTime creation time for the secret key + * @param preferences suite of algorithm preferences and enabled features + */ + @JvmOverloads + fun buildV4Key( + creationTime: Date = Date(), + preferences: AlgorithmSuite = policy.keyGenerationAlgorithmSuite + ): OpinionatedDefinePrimaryKeyV4 { + return OpinionatedDefinePrimaryKeyV4(policy, creationTime, preferences) } } @@ -211,6 +215,14 @@ internal constructor(val policy: Policy, val creationTime: Date, val preferences // Do nothing } + /** + * Return a [OpenPgpKeyTemplates] object which provides factory methods for generating OpenPGP + * keys from templates. + * + * @return templates + */ + abstract fun fromTemplate(): OpenPgpKeyTemplates + /** * Function that can be applied to the primary key. * @@ -1094,11 +1106,13 @@ class PrimaryKeyBuilderV4 internal constructor(keyPair: PGPKeyPair, builder: Def this.keyPair } } + + override fun fromTemplate(): OpenPgpKeyTemplates.V4 = v4() } } /** Templates for OpenPGP key generation. */ -class OpenPgpKeyTemplates private constructor() { +open class OpenPgpKeyTemplates private constructor() { companion object { @@ -1111,7 +1125,7 @@ class OpenPgpKeyTemplates private constructor() { } /** Templates for version 4 OpenPGP keys. Version 4 keys are compliant to RFC4880. */ - class V4 internal constructor() { + class V4 : OpenPgpKeyTemplates() { /** * Generate an OpenPGP key that consists of an Ed25519 primary key used for certification of @@ -1123,9 +1137,11 @@ class OpenPgpKeyTemplates private constructor() { */ fun ed25519Curve25519( vararg userId: CharSequence, - creationTime: Date = Date() + creationTime: Date = Date(), + protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys() ): PGPSecretKeyRing = - OpenPgpKeyGenerator.buildV4Key(creationTime = creationTime) + OpenPgpKeyGenerator() + .buildV4Key(creationTime = creationTime) .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { // Add UserIDs userId.forEachIndexed { index, uid -> @@ -1145,7 +1161,7 @@ class OpenPgpKeyTemplates private constructor() { .addSigningSubkey(KeyType.EDDSA(EdDSACurve._Ed25519)) // encryption key .addEncryptionSubkey(KeyType.XDH(XDHSpec._X25519)) - .build() + .build(protector) /** * Generate an OpenPGP key that consists of an RSA primary key used for certification of @@ -1159,9 +1175,11 @@ class OpenPgpKeyTemplates private constructor() { fun composedRsa( vararg userId: CharSequence, creationTime: Date = Date(), - length: RsaLength = RsaLength._4096 + length: RsaLength = RsaLength._4096, + protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys() ): PGPSecretKeyRing = - OpenPgpKeyGenerator.buildV4Key(creationTime = creationTime) + OpenPgpKeyGenerator() + .buildV4Key(creationTime = creationTime) .setPrimaryKey(KeyType.RSA(length)) { // Add UserIDs userId.forEachIndexed { index, uid -> @@ -1181,7 +1199,7 @@ class OpenPgpKeyTemplates private constructor() { .addSigningSubkey(KeyType.RSA(length)) // encryption key .addEncryptionSubkey(KeyType.RSA(length)) - .build() + .build(protector) /** * Generate an OpenPGP key consisting of a single RSA key that is used for certification of @@ -1194,9 +1212,11 @@ class OpenPgpKeyTemplates private constructor() { fun singleRsa( vararg userId: CharSequence, creationTime: Date = Date(), - length: RsaLength = RsaLength._4096 + length: RsaLength = RsaLength._4096, + protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys() ): PGPSecretKeyRing = - OpenPgpKeyGenerator.buildV4Key(creationTime = creationTime) + OpenPgpKeyGenerator() + .buildV4Key(creationTime = creationTime) .setPrimaryKey(KeyType.RSA(length)) { userId.forEach { addUserId(it) } addDirectKeySignature( @@ -1208,6 +1228,6 @@ class OpenPgpKeyTemplates private constructor() { KeyFlag.ENCRYPT_STORAGE) }) } - .build() + .build(protector) } } diff --git a/pgpainless-core/src/test/kotlin/org/pgpainless/key/KeyWithInacceptableSelfSignatureTest.kt b/pgpainless-core/src/test/kotlin/org/pgpainless/key/KeyWithInacceptableSelfSignatureTest.kt index eb3f6d3c..215bf973 100644 --- a/pgpainless-core/src/test/kotlin/org/pgpainless/key/KeyWithInacceptableSelfSignatureTest.kt +++ b/pgpainless-core/src/test/kotlin/org/pgpainless/key/KeyWithInacceptableSelfSignatureTest.kt @@ -11,7 +11,6 @@ import org.pgpainless.PGPainless import org.pgpainless.algorithm.HashAlgorithm import org.pgpainless.algorithm.KeyFlag import org.pgpainless.bouncycastle.extensions.directKeySignatures -import org.pgpainless.key.generation.OpenPgpKeyGenerator import org.pgpainless.key.generation.type.KeyType import org.pgpainless.key.generation.type.eddsa.EdDSACurve import org.pgpainless.key.generation.type.xdh.XDHSpec @@ -21,19 +20,26 @@ class KeyWithInacceptableSelfSignatureTest { @Test fun `key with inacceptable self-signature is not usable`() { - val genPolicy = Policy().apply { - certificationSignatureHashAlgorithmPolicy = Policy.HashAlgorithmPolicy( - HashAlgorithm.SHA1, listOf(HashAlgorithm.SHA1)) - } + val genPolicy = + Policy().apply { + certificationSignatureHashAlgorithmPolicy = + Policy.HashAlgorithmPolicy(HashAlgorithm.SHA1, listOf(HashAlgorithm.SHA1)) + } - val key = OpenPgpKeyGenerator.buildV4Key(genPolicy) - .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)) - .addEncryptionSubkey(KeyType.XDH(XDHSpec._X25519)) - .build() + val key = + PGPainless.generateOpenPgpKey(genPolicy) + .buildV4Key() + .setPrimaryKey( + KeyType.EDDSA(EdDSACurve._Ed25519), + listOf(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)) + .addEncryptionSubkey(KeyType.XDH(XDHSpec._X25519)) + .build() - assertEquals(HashAlgorithm.SHA1, - key.publicKey.directKeySignatures.single().hashAlgorithm - .let { HashAlgorithm.requireFromId(it) }) + assertEquals( + HashAlgorithm.SHA1, + key.publicKey.directKeySignatures.single().hashAlgorithm.let { + HashAlgorithm.requireFromId(it) + }) val info = PGPainless.inspectKeyRing(key) assertFalse(info.isUsableForSigning) 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 index 07e91775..d3ad2c12 100644 --- a/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/MalformedKeyGenerationTest.kt +++ b/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/MalformedKeyGenerationTest.kt @@ -24,7 +24,8 @@ class MalformedKeyGenerationTest { fun malformedPrimaryUserIdSubpacket() { val userId = "Alice " val key = - OpenPgpKeyGenerator.buildV4Key(Policy()) + PGPainless.generateOpenPgpKey(Policy()) + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserId( userId, @@ -43,7 +44,8 @@ class MalformedKeyGenerationTest { @Test fun malformedExportableSubpacket() { val key = - OpenPgpKeyGenerator.buildV4Key(Policy()) + PGPainless.generateOpenPgpKey(Policy()) + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserId( "Alice ", @@ -62,7 +64,8 @@ class MalformedKeyGenerationTest { @Test fun malformedRevocableSubpacket() { val key = - OpenPgpKeyGenerator.buildV4Key(Policy()) + PGPainless.generateOpenPgpKey(Policy()) + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserId( "Alice ", @@ -82,7 +85,8 @@ class MalformedKeyGenerationTest { fun primaryUserIdOnDirectKeySig() { val policy = Policy() val key = - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)) { diff --git a/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/OpenPgpKeyGeneratorTest.kt b/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/OpenPgpKeyGeneratorTest.kt index b83601cf..05aebf55 100644 --- a/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/OpenPgpKeyGeneratorTest.kt +++ b/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/OpenPgpKeyGeneratorTest.kt @@ -38,7 +38,8 @@ class OpenPgpKeyGeneratorTest { @Test fun `minimal call with opinionated builder adds a default DK sig but no user info`() { val key = - OpenPgpKeyGenerator.buildV4Key() + PGPainless.generateOpenPgpKey() + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) .build() @@ -55,7 +56,8 @@ class OpenPgpKeyGeneratorTest { @Test fun `minimal call with unopinionated builder does not add a default DK sig`() { val key = - OpenPgpKeyGenerator.buildV4Key() + PGPainless.generateOpenPgpKey() + .buildV4Key() .unopinionated() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) .build() @@ -68,7 +70,8 @@ class OpenPgpKeyGeneratorTest { @Test fun `adding a direct-key signature with the opinionated builder omits the default DK sig`() { val key = - OpenPgpKeyGenerator.buildV4Key() + PGPainless.generateOpenPgpKey() + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { addDirectKeySignature() // "overwrites" the default dk sig } @@ -82,7 +85,8 @@ class OpenPgpKeyGeneratorTest { @Test fun `adding two user-ids will mark the first one as primary`() { val key = - OpenPgpKeyGenerator.buildV4Key() + PGPainless.generateOpenPgpKey() + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserId("Primary ") addUserId("Non Primary ") @@ -96,7 +100,8 @@ class OpenPgpKeyGeneratorTest { @Test fun `adding two user-ids but mark the first as non-primary will mark the second one as primary`() { val key = - OpenPgpKeyGenerator.buildV4Key() + PGPainless.generateOpenPgpKey() + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserId( "Non Primary ", @@ -126,7 +131,8 @@ class OpenPgpKeyGeneratorTest { .generate() val key = - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserAttribute(attr1) // primary, since it is the first addUserAttribute(attr2) // non-primary @@ -160,7 +166,8 @@ class OpenPgpKeyGeneratorTest { .generate() val key = - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserId(userId) addUserAttribute(userAttribute) @@ -186,7 +193,8 @@ class OpenPgpKeyGeneratorTest { @Test fun `adding signing key will add embedded back-signature`() { val key = - OpenPgpKeyGenerator.buildV4Key() + PGPainless.generateOpenPgpKey() + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) .addSubkey(KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.SIGN_DATA)) .build() @@ -205,7 +213,8 @@ class OpenPgpKeyGeneratorTest { @Test fun testUnopinionatedV4() { // Unopinionated - OpenPgpKeyGenerator.buildV4Key() + PGPainless.generateOpenPgpKey() + .buildV4Key() .unopinionated() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { addDirectKeySignature() @@ -221,7 +230,8 @@ class OpenPgpKeyGeneratorTest { fun testOpinionatedV4() { // Opinionated val time = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC") - OpenPgpKeyGenerator.buildV4Key(creationTime = time) + PGPainless.generateOpenPgpKey() + .buildV4Key(creationTime = time) .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.CERTIFY_OTHER)) { addUserId("Alice ") } @@ -254,7 +264,8 @@ class OpenPgpKeyGeneratorTest { @Test fun test() { - OpenPgpKeyGenerator.buildV4Key() + PGPainless.generateOpenPgpKey() + .buildV4Key() .setPrimaryKey(KeyType.RSA(RsaLength._3072), keyFlags = listOf(KeyFlag.CERTIFY_OTHER)) .build() .toAsciiArmor() @@ -268,7 +279,8 @@ class OpenPgpKeyGeneratorTest { Policy.PublicKeyAlgorithmPolicy(buildMap { put(PublicKeyAlgorithm.RSA_GENERAL, 3072) }) assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() // opinionated builder verifies PK parameters .setPrimaryKey(KeyType.RSA(RsaLength._2048)) // too weak } @@ -280,7 +292,8 @@ class OpenPgpKeyGeneratorTest { policy.publicKeyAlgorithmPolicy = Policy.PublicKeyAlgorithmPolicy(buildMap { put(PublicKeyAlgorithm.RSA_GENERAL, 3072) }) - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .unopinionated() // unopinionated builder allows for non-compliant configurations .setPrimaryKey(KeyType.RSA(RsaLength._2048)) } @@ -288,7 +301,8 @@ class OpenPgpKeyGeneratorTest { @Test fun `skip default DirectKey signature will not add one`() { val key = - OpenPgpKeyGenerator.buildV4Key() + PGPainless.generateOpenPgpKey() + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { skipDefaultSignature() } .build() @@ -304,7 +318,7 @@ class OpenPgpKeyGeneratorTest { fun `opinionated add UserID with weak hash algorithm fails`() { val policy = Policy() assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy).setPrimaryKey( + PGPainless.generateOpenPgpKey(policy).buildV4Key().setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserId("Alice ", hashAlgorithm = HashAlgorithm.SHA1) } @@ -314,7 +328,7 @@ class OpenPgpKeyGeneratorTest { @Test fun `unopinionated add UserID with weak hash algorithm is okay`() { val policy = Policy() - OpenPgpKeyGenerator.buildV4Key(policy).unopinionated().setPrimaryKey( + PGPainless.generateOpenPgpKey(policy).buildV4Key().unopinionated().setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserId("Alice ", hashAlgorithm = HashAlgorithm.SHA1) } @@ -324,7 +338,7 @@ class OpenPgpKeyGeneratorTest { fun `opinionated add UserAttribute with weak hash algorithm fails`() { val policy = Policy() assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy).setPrimaryKey( + PGPainless.generateOpenPgpKey(policy).buildV4Key().setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserAttribute( PGPUserAttributeSubpacketVectorGenerator().generate(), @@ -336,7 +350,7 @@ class OpenPgpKeyGeneratorTest { @Test fun `unopinionated add UserAttribute with weak hash algorithm is okay`() { val policy = Policy() - OpenPgpKeyGenerator.buildV4Key(policy).unopinionated().setPrimaryKey( + PGPainless.generateOpenPgpKey(policy).buildV4Key().unopinionated().setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserAttribute( PGPUserAttributeSubpacketVectorGenerator().generate(), @@ -348,7 +362,7 @@ class OpenPgpKeyGeneratorTest { fun `opinionated add DK sig with weak hash algorithm fails`() { val policy = Policy() assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy).setPrimaryKey( + PGPainless.generateOpenPgpKey(policy).buildV4Key().setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519)) { addDirectKeySignature(hashAlgorithm = HashAlgorithm.SHA1) } @@ -358,7 +372,7 @@ class OpenPgpKeyGeneratorTest { @Test fun `unopinionated add DK sig with weak hash algorithm is okay`() { val policy = Policy() - OpenPgpKeyGenerator.buildV4Key(policy).unopinionated().setPrimaryKey( + PGPainless.generateOpenPgpKey(policy).buildV4Key().unopinionated().setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519)) { addDirectKeySignature(hashAlgorithm = HashAlgorithm.SHA1) } @@ -371,7 +385,7 @@ class OpenPgpKeyGeneratorTest { val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC") assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy, t1).setPrimaryKey( + PGPainless.generateOpenPgpKey(policy).buildV4Key(t1).setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserId("Alice ", bindingTime = t0) } @@ -384,7 +398,7 @@ class OpenPgpKeyGeneratorTest { val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC") val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC") - OpenPgpKeyGenerator.buildV4Key(policy, t1).unopinionated().setPrimaryKey( + PGPainless.generateOpenPgpKey(policy).buildV4Key(t1).unopinionated().setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserId("Alice ", bindingTime = t0) } @@ -397,7 +411,7 @@ class OpenPgpKeyGeneratorTest { val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC") assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy, t1).setPrimaryKey( + PGPainless.generateOpenPgpKey(policy).buildV4Key(t1).setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserAttribute( PGPUserAttributeSubpacketVectorGenerator().generate(), bindingTime = t0) @@ -411,7 +425,7 @@ class OpenPgpKeyGeneratorTest { val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC") val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC") - OpenPgpKeyGenerator.buildV4Key(policy, t1).unopinionated().setPrimaryKey( + PGPainless.generateOpenPgpKey(policy).buildV4Key(t1).unopinionated().setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519)) { addUserAttribute( PGPUserAttributeSubpacketVectorGenerator().generate(), bindingTime = t0) @@ -425,7 +439,7 @@ class OpenPgpKeyGeneratorTest { val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC") assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy, t1).setPrimaryKey( + PGPainless.generateOpenPgpKey(policy).buildV4Key(t1).setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519)) { addDirectKeySignature(bindingTime = t0) } @@ -438,7 +452,7 @@ class OpenPgpKeyGeneratorTest { val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC") val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC") - OpenPgpKeyGenerator.buildV4Key(policy, t1).unopinionated().setPrimaryKey( + PGPainless.generateOpenPgpKey(policy).buildV4Key(t1).unopinionated().setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519)) { addDirectKeySignature(bindingTime = t0) } @@ -451,7 +465,8 @@ class OpenPgpKeyGeneratorTest { val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC") assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy, t1) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key(t1) .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) .addSubkey(KeyType.XDH(XDHSpec._X25519), null, t0) } @@ -463,7 +478,8 @@ class OpenPgpKeyGeneratorTest { val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC") val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC") - OpenPgpKeyGenerator.buildV4Key(policy, t1) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key(t1) .unopinionated() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) .addSubkey(KeyType.XDH(XDHSpec._X25519), null, t0) @@ -476,7 +492,8 @@ class OpenPgpKeyGeneratorTest { val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC") assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy, t1) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key(t1) .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) .addSubkey(KeyType.XDH(XDHSpec._X25519)) { addBindingSignature(bindingTime = t0) } } @@ -488,7 +505,8 @@ class OpenPgpKeyGeneratorTest { val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC") val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC") - OpenPgpKeyGenerator.buildV4Key(policy, t1) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key(t1) .unopinionated() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) .addSubkey(KeyType.XDH(XDHSpec._X25519)) { addBindingSignature(bindingTime = t0) } @@ -499,7 +517,8 @@ class OpenPgpKeyGeneratorTest { val policy = Policy() assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) .addSubkey(KeyType.XDH(XDHSpec._X25519)) { addBindingSignature(hashAlgorithm = HashAlgorithm.SHA1) @@ -511,7 +530,8 @@ class OpenPgpKeyGeneratorTest { fun `unopinionated add subkey with weak binding signature hash algorithm is okay`() { val policy = Policy() - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) .unopinionated() .addSubkey(KeyType.XDH(XDHSpec._X25519)) { @@ -524,7 +544,9 @@ class OpenPgpKeyGeneratorTest { val policy = Policy() assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy).setPrimaryKey(KeyType.XDH(XDHSpec._X25519)) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() + .setPrimaryKey(KeyType.XDH(XDHSpec._X25519)) } } @@ -533,7 +555,8 @@ class OpenPgpKeyGeneratorTest { val policy = Policy() assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.CERTIFY_OTHER, KeyFlag.ENCRYPT_STORAGE)) @@ -544,7 +567,8 @@ class OpenPgpKeyGeneratorTest { fun `unopinionated set primary key to sign-only algorithm but with encryption flag is okay`() { val policy = Policy() - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .unopinionated() .setPrimaryKey( KeyType.EDDSA(EdDSACurve._Ed25519), @@ -556,7 +580,8 @@ class OpenPgpKeyGeneratorTest { val policy = Policy() val key = - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519), keyFlags = null) .build() @@ -568,7 +593,8 @@ class OpenPgpKeyGeneratorTest { val policy = Policy() assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) .addSubkey( KeyType.XDH(XDHSpec._X25519), listOf(KeyFlag.ENCRYPT_COMMS, KeyFlag.SIGN_DATA)) @@ -580,7 +606,8 @@ class OpenPgpKeyGeneratorTest { val policy = Policy() assertThrows { - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) .addSubkey( KeyType.EDDSA(EdDSACurve._Ed25519), @@ -592,7 +619,8 @@ class OpenPgpKeyGeneratorTest { fun `unopinionated add sign-only sukey but with additional encryption flag is okay`() { val policy = Policy() - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) .unopinionated() .addSubkey( @@ -609,7 +637,8 @@ class OpenPgpKeyGeneratorTest { "ffd8ffe000104a46494600010101004800480000ffdb004300030202020202030202020303030304060404040404080606050609080a0a090809090a0c0f0c0a0b0e0b09090d110d0e0f101011100a0c12131210130f101010ffc9000b080001000101011100ffcc000600101005ffda0008010100003f00d2cf20ffd9") val key = - OpenPgpKeyGenerator.buildV4Key() + PGPainless.generateOpenPgpKey() + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { addImageAttribute(jpegBytes.inputStream()) } @@ -622,7 +651,8 @@ class OpenPgpKeyGeneratorTest { fun `generate key with expiration time`() { val policy = Policy() - OpenPgpKeyGenerator.buildV4Key(policy) + PGPainless.generateOpenPgpKey(policy) + .buildV4Key() .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { addDirectKeySignature( SelfSignatureSubpackets.applyHashed {