From 6703a514c8ba6afa6be97bf233dc4ae35e7cc6bd Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 5 Feb 2024 14:08:16 +0100 Subject: [PATCH] Playing around with mixed opinionated/unopinionated API --- .../key/generation/GenerateOpenPgpKey.kt | 12 +- .../pgpainless/key/generation/Opinionated.kt | 243 ++++++++++++++++++ .../key/generation/GenerateOpenPgpKeyTest.kt | 38 +++ 3 files changed, 287 insertions(+), 6 deletions(-) create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/Opinionated.kt diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKey.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKey.kt index 0fdd5d7d..7759f814 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKey.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKey.kt @@ -36,7 +36,7 @@ open class GenerateOpenPgpKey( ) { /** Builder for OpenPGP secret keys. */ - abstract class OpenPgpKeyBuilder( + abstract class OpinionatedPgpKeyBuilder( protected val policy: Policy, protected val referenceTime: Date, protected val preferences: AlgorithmSuite @@ -67,13 +67,13 @@ open class GenerateOpenPgpKey( * * @param keyType type of the primary key * @param flags key flags for the primary key. Defaults to [KeyFlag.CERTIFY_OTHER]. - * @return [V4GenerateOpenPgpKey] which can be further modified, e.g. add subkeys, user-ids etc. + * @return [OpinionatedV4KeyBuilder] which can be further modified, e.g. add subkeys, user-ids etc. */ fun buildV4Key( keyType: KeyType, flags: List? = listOf(KeyFlag.CERTIFY_OTHER) - ): V4GenerateOpenPgpKey = - V4GenerateOpenPgpKey(keyType, flags, policy, referenceTime, preferences) + ): OpinionatedV4KeyBuilder = + OpinionatedV4KeyBuilder(keyType, flags, policy, referenceTime, preferences) /** * Builder for version 4 OpenPGP keys. @@ -84,14 +84,14 @@ open class GenerateOpenPgpKey( * @param referenceTime reference time for key generation * @param preferences set of algorithm preferences and enabled features for the key */ - class V4GenerateOpenPgpKey + class OpinionatedV4KeyBuilder internal constructor( primaryKeyType: KeyType, primaryFlags: List?, policy: Policy, referenceTime: Date, preferences: AlgorithmSuite - ) : OpenPgpKeyBuilder(policy, referenceTime, preferences) { + ) : OpinionatedPgpKeyBuilder(policy, referenceTime, preferences) { init { require(primaryKeyType.canCertify) { diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/Opinionated.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/Opinionated.kt new file mode 100644 index 00000000..a0d854e4 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/Opinionated.kt @@ -0,0 +1,243 @@ +package org.pgpainless.key.generation + +import org.bouncycastle.openpgp.PGPSecretKeyRing +import org.pgpainless.PGPainless +import org.pgpainless.key.generation.type.KeyType +import org.pgpainless.key.generation.type.eddsa.EdDSACurve +import org.pgpainless.key.generation.type.xdh.XDHSpec +import org.pgpainless.policy.Policy +import org.pgpainless.signature.subpackets.SelfSignatureSubpackets +import java.util.Date + +fun buildV4( + policy: Policy = PGPainless.getPolicy(), + creationTime: Date = Date() +): OpinionatedPrimaryKeyBuilder.V4 { + return OpinionatedPrimaryKeyBuilder.V4(policy, creationTime) +} + +fun buildV6( + policy: Policy = PGPainless.getPolicy(), + creationTime: Date = Date() +): OpinionatedPrimaryKeyBuilder.V6 { + return OpinionatedPrimaryKeyBuilder.V6(policy, creationTime) +} + +fun test() { + // Unopinionated + buildV4() + .unopinionated() + .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519), Date()) { + addDirectKeySignature(SelfSignatureSubpackets.nop()) + addUserId("Alice ") + } + .addSubkey(KeyType.EDDSA(EdDSACurve._Ed25519), Date()) { + addBindingSignature(SelfSignatureSubpackets.nop()) + } + .addSubkey(KeyType.XDH(XDHSpec._X25519), Date()) { + addBindingSignature(SelfSignatureSubpackets.nop()) + } + .build() + + // Opinionated + buildV4() + .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519), Date()) { + addDirectKeySignature(SelfSignatureSubpackets.nop()) + addUserId("Alice") + } + .addEncryptionSubkey(KeyType.XDH(XDHSpec._X25519), Date()) { + // + } + .build() + + // Unopinionated + buildV6() + .unopinionated() + .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519), Date()) + .addEncryptionSubkey(KeyType.XDH(XDHSpec._X25519), Date()) + .build() + + // Opinionated + buildV6() + .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519), Date()) + .addSubkey(KeyType.XDH(XDHSpec._X25519), Date()) + .build() +} + +abstract class PrimaryKeyBuilder>( + protected val creationTime: Date +) { + abstract fun setPrimaryKey( + type: KeyType, + creationTime: Date = this.creationTime, + function: ApplyToPrimaryKey.() -> Unit = {}): B +} + +abstract class OpinionatedPrimaryKeyBuilder( + protected val policy: Policy, + creationTime: Date, + protected val unopinionated: UnopinionatedPrimaryKeyBuilder +) : PrimaryKeyBuilder(creationTime) { + + fun unopinionated() = unopinionated + + class V4( + policy: Policy, + creationTime: Date + ) : OpinionatedPrimaryKeyBuilder( + policy, + creationTime, + UnopinionatedPrimaryKeyBuilder.V4(creationTime) + ) { + + override fun setPrimaryKey( + type: KeyType, + creationTime: Date, + function: ApplyToPrimaryKey.() -> Unit + ): Opinionated.V4 { + return Opinionated.V4( + policy, + unopinionated.setPrimaryKey(type, creationTime, function) as Unopinionated.V4) + } + } + + class V6( + policy: Policy, + creationTime: Date + ) : OpinionatedPrimaryKeyBuilder( + policy, + creationTime, + UnopinionatedPrimaryKeyBuilder.V6(creationTime) + ) { + override fun setPrimaryKey( + type: KeyType, + creationTime: Date, + function: ApplyToPrimaryKey.() -> Unit + ): Opinionated.V6 { + return Opinionated.V6( + policy, + unopinionated.setPrimaryKey(type, creationTime, function) as Unopinionated.V6) + } + } +} + +abstract class UnopinionatedPrimaryKeyBuilder( + creationTime: Date +) : PrimaryKeyBuilder( + creationTime +) { + + class V4(creationTime: Date) : UnopinionatedPrimaryKeyBuilder(creationTime) { + override fun setPrimaryKey( + type: KeyType, + creationTime: Date, + function: ApplyToPrimaryKey.() -> Unit + ): Unopinionated.V4 { + return Unopinionated.V4() + } + } + + class V6(creationTime: Date) : UnopinionatedPrimaryKeyBuilder(creationTime) { + override fun setPrimaryKey( + type: KeyType, + creationTime: Date, + function: ApplyToPrimaryKey.() -> Unit + ): Unopinionated.V6 { + return Unopinionated.V6() + } + } +} + +interface KeyBuilder> { + + fun addSubkey(type: KeyType, creationTime: Date, function: ApplyToSubkey.() -> Unit = {}): B + + fun addEncryptionSubkey(type: KeyType, creationTime: Date, function: ApplyToSubkey.() -> Unit = {}): B { + return addSubkey(type, creationTime, function) + } + + fun build(): PGPSecretKeyRing +} + +@JvmDefaultWithoutCompatibility +abstract class Opinionated( + protected val policy: Policy +) : KeyBuilder { + + abstract val unopinionated: Unopinionated + + override fun build(): PGPSecretKeyRing = unopinionated.build() + + class V4( + policy: Policy, + override val unopinionated: Unopinionated.V4 = Unopinionated.V4() + ) : Opinionated(policy) { + + override fun addSubkey( + type: KeyType, + creationTime: Date, + function: ApplyToSubkey.() -> Unit + ): V4 = apply { + unopinionated.addSubkey(type, creationTime, function) + } + + } + + class V6( + policy: Policy, + override val unopinionated: Unopinionated.V6 = Unopinionated.V6() + ) : Opinionated(policy) { + + override fun addSubkey( + type: KeyType, + creationTime: Date, + function: ApplyToSubkey.() -> Unit + ): V6 = apply { + unopinionated.addSubkey(type, creationTime, function) + } + } +} + +@JvmDefaultWithoutCompatibility +abstract class Unopinionated : KeyBuilder { + + class V4 : Unopinionated() { + + override fun addSubkey( + type: KeyType, + creationTime: Date, + function: ApplyToSubkey.() -> Unit + ): V4 = apply { + // Add key + } + + override fun build(): PGPSecretKeyRing { + TODO("Not yet implemented") + } + } + + class V6 : Unopinionated() { + + override fun addSubkey( + type: KeyType, + creationTime: Date, + function: ApplyToSubkey.() -> Unit + ): V6 = apply { + // Add Key + } + + override fun build(): PGPSecretKeyRing { + TODO("Not yet implemented") + } + } +} + +interface ApplyToPrimaryKey { + fun addUserId(userId: CharSequence) + + fun addDirectKeySignature(subpacketsCallback: SelfSignatureSubpackets.Callback) +} + +interface ApplyToSubkey { + fun addBindingSignature(subpacketsCallback: SelfSignatureSubpackets.Callback) +} diff --git a/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKeyTest.kt b/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKeyTest.kt index 5036f495..3de93932 100644 --- a/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKeyTest.kt +++ b/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKeyTest.kt @@ -124,4 +124,42 @@ class GenerateOpenPgpKeyTest { ?: throw TestAbortedException( "Cannot read resource $resourceName: InputStream is null.") } + + fun testAround() { + GenerateOpenPgpKey(Policy.getInstance()) + .buildKeyV4() + .primaryKey(RSA, listOf(KeyFlag.CERTIFY_OTHER)) + + GenerateOpenPgpKey(Policy.getInstance()) + .buildKey() + .unopinionated() + .apply { + primaryKey(RSA, creationTime) + .directKeySignature(callback) + } + + } + + interface TestInterface> { + fun doSomething(): T + } + + class LowerTestClass : TestInterface { + override fun doSomething(): LowerTestClass { + TODO("Not yet implemented") + } + + } + + class TestClass : TestInterface { + + override fun doSomething(): TestClass { + TODO("Not yet implemented") + } + + fun lower(): LowerTestClass { + return LowerTestClass() + } + + } }