package org.pgpainless.key.generation import org.bouncycastle.bcpg.attr.ImageAttribute import java.security.KeyPair import java.security.KeyPairGenerator import java.util.Date import org.bouncycastle.openpgp.PGPKeyPair import org.bouncycastle.openpgp.PGPPublicKey import org.bouncycastle.openpgp.PGPSecretKey import org.bouncycastle.openpgp.PGPSecretKeyRing import org.bouncycastle.openpgp.PGPSignature import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVectorGenerator import org.bouncycastle.util.io.Streams import org.pgpainless.PGPainless import org.pgpainless.algorithm.AlgorithmSuite import org.pgpainless.algorithm.CertificationType import org.pgpainless.algorithm.HashAlgorithm import org.pgpainless.algorithm.KeyFlag import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.key.generation.type.KeyType import org.pgpainless.key.generation.type.eddsa.EdDSACurve import org.pgpainless.key.generation.type.rsa.RsaLength import org.pgpainless.key.generation.type.xdh.XDHSpec import org.pgpainless.policy.Policy import org.pgpainless.provider.ProviderFactory import org.pgpainless.signature.builder.DirectKeySelfSignatureBuilder import org.pgpainless.signature.builder.SelfSignatureBuilder import org.pgpainless.signature.builder.SubkeyBindingSignatureBuilder import org.pgpainless.signature.subpackets.SelfSignatureSubpackets import java.io.InputStream /** * 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 */ fun buildV4( policy: Policy = PGPainless.getPolicy(), creationTime: Date = Date(), preferences: AlgorithmSuite = policy.keyGenerationAlgorithmSuite ): OpinionatedDefinePrimaryKey.V4 { return OpinionatedDefinePrimaryKey.V4(policy, creationTime, preferences) } /** * Build a version 6 OpenPGP secret key. * * @param policy policy to ensure algorithm compliance and to determine default algorithms * @param creationTime creation time for the secret key */ fun buildV6( policy: Policy = PGPainless.getPolicy(), creationTime: Date = Date(), preferences: AlgorithmSuite = policy.keyGenerationAlgorithmSuite ): OpinionatedDefinePrimaryKey.V6 { return OpinionatedDefinePrimaryKey.V6(policy, creationTime, preferences) } /** * Builder that allows the user to define the primary key of the OpenPGP key. * * @param policy algorithm policy * @param creationTime default value for the creation time of the primary key. */ abstract class DefinePrimaryKey>( val policy: Policy, val creationTime: Date, ) { /** * Define the primary key of the OpenPGP key. * * @param type primary key type * @param creationTime creation time of the primary key. Defaults to the [DefinePrimaryKey]'s * [creationTime]. * @param applyToPrimaryKey function that gets applied to the primary key. Is used to add * binding signatures, UserIDs and user-attributes on the primary key. * @return next step key builder */ abstract fun setPrimaryKey( type: KeyType, creationTime: Date = this.creationTime, applyToPrimaryKey: (ApplyToPrimaryKey.() -> PGPKeyPair)? = null // default: do nothing ): B abstract fun preferencesSubpackets(): SelfSignatureSubpackets.Callback } /** * Opinionated implementation of [DefinePrimaryKey]. Contrary to an unopinionated implementation, an * opinionated [DefinePrimaryKey] will sanity check user-provided parameters and make sure, required * signatures are placed on the key etc. * * @param policy policy to sanity check algorithms, key strengths etc. and to determine fallback * algorithms with * @param creationTime creation time of the primary key * @param unopinionated unopinionated implementation * @param B opinionated builder type * @param U unopinionated builder type */ abstract class OpinionatedDefinePrimaryKey< B : OpinionatedDefineSubkeys, U : UnopinionatedDefineSubkeys>( policy: Policy, creationTime: Date, val preferences: AlgorithmSuite, protected val unopinionated: UnopinionatedDefinePrimaryKey ) : DefinePrimaryKey(policy, creationTime) { /** * Turn this builder into an unopinionated one by returning the underlying unopinionated * implementation. * * @return unopinionated implementation */ abstract fun unopinionated(): UnopinionatedDefinePrimaryKey override fun preferencesSubpackets(): SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.applyHashed { setSignatureCreationTime(creationTime) setPreferredHashAlgorithms(preferences.hashAlgorithms) setPreferredSymmetricKeyAlgorithms(preferences.symmetricKeyAlgorithms) setPreferredCompressionAlgorithms(preferences.compressionAlgorithms) setFeatures(preferences.features) } fun setCertificationKey( type: KeyType, creationTime: Date = this.creationTime, applyToPrimaryKey: (ApplyToPrimaryKey.() -> PGPKeyPair)? = { keyPair } ): B { return setPrimaryKey(type, creationTime) { addDirectKeySignature( preferencesSubpackets() .then( SelfSignatureSubpackets.applyHashed { setKeyFlags(KeyFlag.CERTIFY_OTHER) })) then(applyToPrimaryKey) } as B } /** * Implementation of an [OpinionatedDefinePrimaryKey] for OpenPGP v4 keys. * * @param policy policy for algorithm compliance and fallbacks * @param creationTime creation time of the primary key */ class V4(policy: Policy, creationTime: Date, preferences: AlgorithmSuite) : OpinionatedDefinePrimaryKey( policy, creationTime, preferences, UnopinionatedDefinePrimaryKey.V4(policy, creationTime)) { override fun unopinionated(): UnopinionatedDefinePrimaryKey.V4 = unopinionated as UnopinionatedDefinePrimaryKey.V4 override fun setPrimaryKey( type: KeyType, creationTime: Date, applyToPrimaryKey: (ApplyToPrimaryKey.() -> PGPKeyPair)? ): OpinionatedDefineSubkeys.V4 { require(policy.publicKeyAlgorithmPolicy.isAcceptable(type.algorithm, type.bitStrength)) { "Public Key algorithm ${type.algorithm} with ${type.bitStrength} is too weak" + " for the current public key algorithm policy." } val applier = applyToPrimaryKey ?: { addDirectKeySignature(preferencesSubpackets()) } val unopinionatedSubkeys = unopinionated().setPrimaryKey(type, creationTime, applier) return OpinionatedDefineSubkeys.V4( unopinionatedSubkeys.primaryKey, policy, creationTime, unopinionatedSubkeys) } } /** * Implementation of an [OpinionatedDefinePrimaryKey] for OpenPGP v6 keys. * * @param policy policy for algorithm compliance and fallbacks * @param creationTime creation time of the primary key */ class V6(policy: Policy, creationTime: Date, preferences: AlgorithmSuite) : OpinionatedDefinePrimaryKey( policy, creationTime, preferences, UnopinionatedDefinePrimaryKey.V6(policy, creationTime)) { override fun unopinionated(): UnopinionatedDefinePrimaryKey.V6 = unopinionated as UnopinionatedDefinePrimaryKey.V6 override fun setPrimaryKey( type: KeyType, creationTime: Date, applyToPrimaryKey: (ApplyToPrimaryKey.() -> PGPKeyPair)? ): OpinionatedDefineSubkeys.V6 { val applier = applyToPrimaryKey ?: { addDirectKeySignature(preferencesSubpackets()) } return OpinionatedDefineSubkeys.V6( policy, creationTime, unopinionated.setPrimaryKey(type, creationTime, applier) as UnopinionatedDefineSubkeys.V6) } } } /** * Unopinionated implementation of [DefinePrimaryKey]. An unopinionated [DefinePrimaryKey] will not * perform any sanity checks on user-provided algorithms. * * @param creationTime creation time of the primary key * @param U unopinionated builder type */ abstract class UnopinionatedDefinePrimaryKey( policy: Policy, creationTime: Date, ) : DefinePrimaryKey(policy, creationTime) { override fun preferencesSubpackets(): SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop() /** * Implementation of an [UnopinionatedDefinePrimaryKey] for OpenPGP v4 keys. * * @param creationTime creation time of the primary key */ class V4(policy: Policy, creationTime: Date) : UnopinionatedDefinePrimaryKey(policy, creationTime) { override fun setPrimaryKey( type: KeyType, creationTime: Date, applyToPrimaryKey: (ApplyToPrimaryKey.() -> PGPKeyPair)? ): UnopinionatedDefineSubkeys.V4 { var primaryKey = OpenPgpKeyPairGenerator.V4().generatePrimaryKey(type, creationTime) if (applyToPrimaryKey != null) { primaryKey = ApplyToPrimaryKey.V4(primaryKey, this).applyToPrimaryKey() } return UnopinionatedDefineSubkeys.V4(primaryKey, policy, creationTime) } } /** * Implementation of an [UnopinionatedDefinePrimaryKey] for OpenPGP v6 keys. * * @param creationTime creation time of the primary key */ class V6(policy: Policy, creationTime: Date) : UnopinionatedDefinePrimaryKey(policy, creationTime) { override fun setPrimaryKey( type: KeyType, creationTime: Date, applyToPrimaryKey: (ApplyToPrimaryKey.() -> PGPKeyPair)? ): UnopinionatedDefineSubkeys.V6 { return UnopinionatedDefineSubkeys.V6(policy, creationTime) } } } /** Interface for key builder that can */ abstract class DefineSubkeys>(val policy: Policy, val creationTime: Date) { /** * Add a subkey to the OpenPGP key. * * @param type subkey type * @param creationTime creation time of the subkey * @param function function to apply to the subkey. Used to add binding signatures. * @return this */ abstract fun addSubkey( type: KeyType, creationTime: Date = this.creationTime, function: (ApplyToSubkey.() -> PGPKeyPair)? = null ): B /** * Finish the key generation and return the OpenPGP [PGPSecretKeyRing]. * * @return finished [PGPSecretKeyRing] */ abstract fun build(): PGPSecretKeyRing } abstract class OpinionatedDefineSubkeys(policy: Policy, creationTime: Date) : DefineSubkeys(policy, creationTime) { abstract val unopinionated: UnopinionatedDefineSubkeys override fun build(): PGPSecretKeyRing = unopinionated.build() class V4( primaryKey: PGPKeyPair, policy: Policy, creationTime: Date, override val unopinionated: UnopinionatedDefineSubkeys.V4 = UnopinionatedDefineSubkeys.V4(primaryKey, policy, creationTime) ) : OpinionatedDefineSubkeys(policy, creationTime) { override fun addSubkey( type: KeyType, creationTime: Date, applyToSubkey: (ApplyToSubkey.() -> PGPKeyPair)? ): V4 = apply { unopinionated.addSubkey(type, creationTime, applyToSubkey) } fun addSigningSubkey( type: KeyType, creationTime: Date = this.creationTime, applyToSubkey: (ApplyToSubkey.() -> PGPKeyPair)? = { addBindingSignature( SelfSignatureSubpackets.applyHashed { setSignatureCreationTime(creationTime) setKeyFlags(KeyFlag.SIGN_DATA) }) } ) = addSubkey(type, creationTime, applyToSubkey) fun addEncryptionSubkey( type: KeyType, creationTime: Date = this.creationTime, applyToSubkey: (ApplyToSubkey.() -> PGPKeyPair)? = { addBindingSignature( SelfSignatureSubpackets.applyHashed { setSignatureCreationTime(creationTime) setKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) }) } ) = addSubkey(type, creationTime, applyToSubkey) } class V6( policy: Policy, creationTime: Date, override val unopinionated: UnopinionatedDefineSubkeys.V6 = UnopinionatedDefineSubkeys.V6(policy, creationTime) ) : OpinionatedDefineSubkeys(policy, creationTime) { override fun addSubkey( type: KeyType, creationTime: Date, function: (ApplyToSubkey.() -> PGPKeyPair)? ): V6 = apply { unopinionated.addSubkey(type, creationTime, function) } } } abstract class UnopinionatedDefineSubkeys(policy: Policy, creationTime: Date) : DefineSubkeys(policy, creationTime) { class V4(val primaryKey: PGPKeyPair, policy: Policy, creationTime: Date) : UnopinionatedDefineSubkeys(policy, creationTime) { val subkeys: MutableList = mutableListOf() override fun addSubkey( type: KeyType, creationTime: Date, applyToSubkey: (ApplyToSubkey.() -> PGPKeyPair)? ): V4 = apply { val subkey = OpenPgpKeyPairGenerator.V4().generateSubkey(type, creationTime) subkeys.add( if (applyToSubkey == null) { subkey } else { ApplyToSubkey.V4(primaryKey, subkey, this).applyToSubkey() }) } override fun build(): PGPSecretKeyRing { val primary = PGPSecretKey( primaryKey.privateKey, primaryKey.publicKey, ImplementationFactory.getInstance().v4FingerprintCalculator, true, null) return PGPSecretKeyRing( buildList { add(primary) subkeys.forEach { add( PGPSecretKey( it.privateKey, it.publicKey, ImplementationFactory.getInstance().v4FingerprintCalculator, false, null)) } }) } } class V6(policy: Policy, creationTime: Date) : UnopinionatedDefineSubkeys(policy, creationTime) { override fun addSubkey( type: KeyType, creationTime: Date, function: (ApplyToSubkey.() -> PGPKeyPair)? ): V6 = apply { // Add Key } override fun build(): PGPSecretKeyRing { TODO("Not yet implemented") } } } /** * Function that can be applied to the primary key. * * @param keyPair primary key pair * @param builder builder instance that generated the primary key */ abstract class ApplyToPrimaryKey(var keyPair: PGPKeyPair, val builder: DefinePrimaryKey<*>) { /** * Add a UserID to the primary key. * * @param userId UserID to be bound to the primary key * @param subpacketsCallback callback to modify the binding signatures subpackets * @param certificationType type of the certification signature. Defaults to [CertificationType.POSITIVE] * @param hashAlgorithm hash algorithm to be used during signature calculation * @param bindingTime creation time of the binding signature * * @return modified key pair */ abstract fun addUserId( userId: CharSequence, subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop(), certificationType: CertificationType = CertificationType.POSITIVE, hashAlgorithm: HashAlgorithm = builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm, bindingTime: Date = builder.creationTime ): PGPKeyPair /** * Add a UserAttribute to the primary key. * * @param userAttribute UserAttribute to be bound to the primary key * @param subpacketsCallback callback to modify the binding signature subpackets * @param certificationType type of the binding signature. Default to [CertificationType.POSITIVE] * @param hashAlgorithm hash algorithm to be used during signature calculation * @param bindingTime creation time of the binding signature * * @return modified key pair */ abstract fun addUserAttribute( userAttribute: PGPUserAttributeSubpacketVector, subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop(), certificationType: CertificationType = CertificationType.POSITIVE, hashAlgorithm: HashAlgorithm = builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm, bindingTime: Date = builder.creationTime ): PGPKeyPair /** * Add a JPEG image as UserAttribute to the primary key. * This may for example be a profile picture of the key owner. * * @param jpegInputStream input stream containing the JPEG encoded image * @param subpacketsCallback callback to modify the subpackets of the binding signature * @param certificationType type of the binding signature. Defaults to [CertificationType.POSITIVE] * @param hashAlgorithm hash algorithm to be used during signature calculation * @param bindingTime creation time of the binding signature * * @return modified key pair */ fun addImageAttribute( jpegInputStream: InputStream, subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop(), certificationType: CertificationType = CertificationType.POSITIVE, hashAlgorithm: HashAlgorithm = builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm, bindingTime: Date = builder.creationTime ): PGPKeyPair = addUserAttribute( PGPUserAttributeSubpacketVectorGenerator().apply { setImageAttribute(ImageAttribute.JPEG, Streams.readAll(jpegInputStream)) }.generate(), subpacketsCallback, certificationType, hashAlgorithm, bindingTime) /** * Add a DirectKeySignature to the primary key. Such a signature usually carries information * that applies to the whole OpenPGP key, such as algorithm preferences etc. */ abstract fun addDirectKeySignature( subpacketsCallback: SelfSignatureSubpackets.Callback = builder.preferencesSubpackets(), hashAlgorithm: HashAlgorithm = builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm, bindingTime: Date = builder.creationTime ): PGPKeyPair /** * Schedule the execution of another [ApplyToPrimaryKey] function instance right after * this one has been executed. * * @param other second instance * @return modified key pair after this and [other] have been executed */ abstract fun then(other: (ApplyToPrimaryKey.() -> PGPKeyPair)?): PGPKeyPair class V4(keyPair: PGPKeyPair, builder: DefinePrimaryKey<*>) : ApplyToPrimaryKey(keyPair, builder) { override fun addUserId( userId: CharSequence, subpacketsCallback: SelfSignatureSubpackets.Callback, certificationType: CertificationType, hashAlgorithm: HashAlgorithm, bindingTime: Date ): PGPKeyPair { val sig = buildCertificationFor( keyPair, userId, certificationType, bindingTime, hashAlgorithm, subpacketsCallback) keyPair = PGPKeyPair( PGPPublicKey.addCertification(keyPair.publicKey, userId.toString(), sig), keyPair.privateKey) return keyPair } override fun addUserAttribute( userAttribute: PGPUserAttributeSubpacketVector, subpacketsCallback: SelfSignatureSubpackets.Callback, certificationType: CertificationType, hashAlgorithm: HashAlgorithm, bindingTime: Date ): PGPKeyPair { val sig = buildCertificationFor( keyPair, userAttribute, certificationType, bindingTime, hashAlgorithm, subpacketsCallback) keyPair = PGPKeyPair( PGPPublicKey.addCertification(keyPair.publicKey, userAttribute, sig), keyPair.privateKey) return keyPair } override fun addDirectKeySignature( subpacketsCallback: SelfSignatureSubpackets.Callback, hashAlgorithm: HashAlgorithm, bindingTime: Date ): PGPKeyPair { val sig = buildDirectKeySignature( keyPair, hashAlgorithm, subpacketsCallback.then( SelfSignatureSubpackets.applyHashed { setSignatureCreationTime(bindingTime) })) keyPair = PGPKeyPair( PGPPublicKey.addCertification(keyPair.publicKey, sig), keyPair.privateKey) return keyPair } override fun then(other: (ApplyToPrimaryKey.() -> PGPKeyPair)?): PGPKeyPair { if (other != null) { keyPair = V4(keyPair, builder).other() } return keyPair } /** * Build a certification signature that binds the given [userId] to the given * primary key [pair]. * * @param pair primary key pair * @param userId UserID to be bound * @param certificationType type of the binding signature * @param bindingTime creation time of the binding signature * @param hashAlgorithm hash algorithm to be used during signature calculation * @param subpacketsCallback callback to modify the binding signatures subpackets * * @return binding signature */ fun buildCertificationFor( pair: PGPKeyPair, userId: CharSequence, certificationType: CertificationType, bindingTime: Date, hashAlgorithm: HashAlgorithm, subpacketsCallback: SelfSignatureSubpackets.Callback ): PGPSignature { val builder = SelfSignatureBuilder( pair.privateKey, pair.publicKey, certificationType.signatureType, hashAlgorithm) builder.hashedSubpackets.apply { setSignatureCreationTime(bindingTime) } builder.applyCallback(subpacketsCallback) return builder.build(userId) } /** * Build a certification signature that binds the given [userAttribute] to the given * primary key [pair]. * * @param pair primary key pair * @param userAttribute UserAttribute to be bound * @param certificationType type of the binding signature * @param bindingTime creation time of the binding signature * @param hashAlgorithm hash algorithm to be used during signature calculation * @param subpacketsCallback callback to modify the binding signatures subpackets * * @return binding signature */ fun buildCertificationFor( pair: PGPKeyPair, userAttribute: PGPUserAttributeSubpacketVector, certificationType: CertificationType, bindingTime: Date, hashAlgorithm: HashAlgorithm, subpacketsCallback: SelfSignatureSubpackets.Callback ): PGPSignature { val builder = SelfSignatureBuilder( pair.privateKey, pair.publicKey, certificationType.signatureType, hashAlgorithm) builder.hashedSubpackets.apply { setSignatureCreationTime(bindingTime) } builder.applyCallback(subpacketsCallback) return builder.build(userAttribute) } /** * Build a DirectKey self signature over the primary key [pair]. * * @param pair primary key pair * @param hashAlgorithm hash algorithm to be used during signature calculation * @param subpacketsCallback callback to modify the subpackets of the direct-key signature * * @return direct-key self signature */ fun buildDirectKeySignature( pair: PGPKeyPair, hashAlgorithm: HashAlgorithm, subpacketsCallback: SelfSignatureSubpackets.Callback ): PGPSignature { val builder = DirectKeySelfSignatureBuilder(pair.privateKey, pair.publicKey, hashAlgorithm) builder.applyCallback(subpacketsCallback) return builder.build() } } } /** * Function that can be applied to subkeys. * * @param primaryKey primary key pair * @param subkey subkey pair * @param builder builder instance that generated the subkey */ abstract class ApplyToSubkey( val primaryKey: PGPKeyPair, val subkey: PGPKeyPair, val builder: DefineSubkeys<*> ) { /** * Add a binding signature to the subkey. * * @param subpacketsCallback callback to modify the binding signatures subpackets * @param hashAlgorithm hash algorithm to be used during signature calculation * @param bindingTime creation time of the binding signature * * @return modified subkey pair */ abstract fun addBindingSignature( subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop(), hashAlgorithm: HashAlgorithm = builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm, bindingTime: Date = subkey.publicKey.creationTime ): PGPKeyPair /** * Implementation of the [ApplyToSubkey] function tailored to OpenPGP v4 keys. * * @param primaryKey primary key pair * @param subkey subkey pair * @param builder builder instance that generated the subkey */ class V4(primaryKey: PGPKeyPair, subkey: PGPKeyPair, builder: DefineSubkeys<*>) : ApplyToSubkey(primaryKey, subkey, builder) { override fun addBindingSignature( subpacketsCallback: SelfSignatureSubpackets.Callback, hashAlgorithm: HashAlgorithm, bindingTime: Date ): PGPKeyPair { val sig = buildBindingSignature( primaryKey, subkey, hashAlgorithm, bindingTime, subpacketsCallback) return PGPKeyPair( PGPPublicKey.addCertification(subkey.publicKey, sig), subkey.privateKey) } /** * Generate a version 4 binding signature that binds the [subkey] to the [primaryKey]. * * @param primaryKey primary key pair * @param subkey subkey pair * @param hashAlgorithm hash algorithm to be used during signature calculation * @param bindingTime creation time of the subkey * @param subpacketsCallback callback to modify the subpackets of the binding signature * * @return subkey binding signature */ fun buildBindingSignature( primaryKey: PGPKeyPair, subkey: PGPKeyPair, hashAlgorithm: HashAlgorithm, bindingTime: Date, subpacketsCallback: SelfSignatureSubpackets.Callback ): PGPSignature { return SubkeyBindingSignatureBuilder( primaryKey.privateKey, primaryKey.publicKey, hashAlgorithm) .applyCallback( subpacketsCallback.then( SelfSignatureSubpackets.applyHashed { setSignatureCreationTime(bindingTime) })) .build(subkey.publicKey) } } } /** * Templates for OpenPGP key generation. */ class OpenPgpKeyTemplates { companion object { /** * Templates for version 4 OpenPGP keys. * * @return templates */ @JvmStatic fun v4() = V4() } /** * Templates for version 4 OpenPGP keys. * Version 4 keys are compliant to RFC4880. */ class V4 { /** * Generate an OpenPGP key that consists of an Ed25519 primary key used for certification * of third-party keys, a dedicated Ed25519 subkey for message signing, and an X25519 * subkey used for message encryption. * * @param userId an arbitrary number of user-ids. The first UserID will be marked as primary * @param creationTime creation time for the OpenPGP key */ fun ed25519Curve25519( vararg userId: CharSequence, creationTime: Date = Date() ): PGPSecretKeyRing = buildV4(creationTime = creationTime) .setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { // Add UserIDs userId.forEachIndexed { index, uid -> if (index == 0) { // Mark first UserID as primary addUserId(uid, SelfSignatureSubpackets.applyHashed { setPrimaryUserId() }) } else { addUserId(uid) } } // Add Direct-Key signature with preferences and keyFlags addDirectKeySignature( SelfSignatureSubpackets.applyHashed { setKeyFlags(KeyFlag.CERTIFY_OTHER) }) } // singing key .addSubkey(KeyType.EDDSA(EdDSACurve._Ed25519)) { addBindingSignature( SelfSignatureSubpackets.applyHashed { setKeyFlags(KeyFlag.SIGN_DATA) }) } // encryption key .addSubkey(KeyType.XDH(XDHSpec._X25519)) { addBindingSignature( SelfSignatureSubpackets.applyHashed { setKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) }) } .build() /** * Generate an OpenPGP key that consists of an RSA primary key used for certification * of third-party keys, a dedicated RSA subkey for message signing, and an RSA * subkey used for message encryption. * * @param userId an arbitrary number of user-ids. The first UserID will be marked as primary * @param creationTime creation time for the OpenPGP key * @param length RSA bit strength */ fun composedRsa( vararg userId: CharSequence, creationTime: Date = Date(), length: RsaLength = RsaLength._4096 ): PGPSecretKeyRing = buildV4(creationTime = creationTime) .setPrimaryKey(KeyType.RSA(length)) { // Add UserIDs userId.forEachIndexed { index, uid -> if (index == 0) { // Mark first UserID as primary addUserId(uid, SelfSignatureSubpackets.applyHashed { setPrimaryUserId() }) } else { addUserId(uid) } } // Add Direct-Key signature with preferences and keyFlags addDirectKeySignature( SelfSignatureSubpackets.applyHashed { setKeyFlags(KeyFlag.CERTIFY_OTHER) }) } // signing key .addSubkey(KeyType.RSA(length)) { addBindingSignature( SelfSignatureSubpackets.applyHashed { setKeyFlags(KeyFlag.SIGN_DATA) }) } // encryption key .addSubkey(KeyType.RSA(length)) { addBindingSignature( SelfSignatureSubpackets.applyHashed { setKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) }) } .build() /** * Generate an OpenPGP key consisting of a single RSA key that is used for certification of * third-party keys, signing and encryption of messages. * * @param userId an arbitrary number of UserIDs. The first one will be marked as primary. * @param creationTime creation time of the OpenPGP key * @param length bit-strength of the RSA key */ fun singleRsa( vararg userId: CharSequence, creationTime: Date = Date(), length: RsaLength = RsaLength._4096 ): PGPSecretKeyRing = buildV4(creationTime = creationTime) .setPrimaryKey(KeyType.RSA(length)) { userId.forEach { addUserId(it) } addDirectKeySignature( SelfSignatureSubpackets.applyHashed { setKeyFlags( KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) }) keyPair } .build() } }