mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-05 12:05:58 +01:00
WIP: flexible, generic API
This commit is contained in:
parent
6703a514c8
commit
a368a44f11
5 changed files with 761 additions and 258 deletions
|
@ -0,0 +1,569 @@
|
||||||
|
package org.pgpainless.key.generation
|
||||||
|
|
||||||
|
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.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.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.security.KeyPair
|
||||||
|
import java.security.KeyPairGenerator
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<B : DefineSubkeys<B>>(
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
protected val unopinionated: UnopinionatedDefinePrimaryKey<U>
|
||||||
|
) : DefinePrimaryKey<OpinionatedDefineSubkeys>(policy, creationTime) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn this builder into an unopinionated one by returning the underlying unopinionated
|
||||||
|
* implementation.
|
||||||
|
*
|
||||||
|
* @return unopinionated implementation
|
||||||
|
*/
|
||||||
|
abstract fun unopinionated(): UnopinionatedDefinePrimaryKey<U>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<OpinionatedDefineSubkeys.V4, UnopinionatedDefineSubkeys.V4>(
|
||||||
|
policy,
|
||||||
|
creationTime,
|
||||||
|
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 {
|
||||||
|
|
||||||
|
val applier = applyToPrimaryKey ?: {
|
||||||
|
addDirectKeySignature()
|
||||||
|
}
|
||||||
|
|
||||||
|
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<OpinionatedDefineSubkeys.V6, UnopinionatedDefineSubkeys.V6>(
|
||||||
|
policy,
|
||||||
|
creationTime,
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
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<U : UnopinionatedDefineSubkeys>(
|
||||||
|
policy: Policy,
|
||||||
|
creationTime: Date,
|
||||||
|
) : DefinePrimaryKey<UnopinionatedDefineSubkeys>(
|
||||||
|
policy,
|
||||||
|
creationTime
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of an [UnopinionatedDefinePrimaryKey] for OpenPGP v4 keys.
|
||||||
|
*
|
||||||
|
* @param creationTime creation time of the primary key
|
||||||
|
*/
|
||||||
|
class V4(policy: Policy, creationTime: Date) : UnopinionatedDefinePrimaryKey<UnopinionatedDefineSubkeys.V4>(policy, creationTime) {
|
||||||
|
|
||||||
|
override fun setPrimaryKey(
|
||||||
|
type: KeyType,
|
||||||
|
creationTime: Date,
|
||||||
|
applyToPrimaryKey: (ApplyToPrimaryKey.() -> PGPKeyPair)?
|
||||||
|
): UnopinionatedDefineSubkeys.V4 {
|
||||||
|
var primaryKey = OpenPgpKeyPairGenerator.V4().generatePrimaryKey(type, creationTime)
|
||||||
|
val applier = ApplyToV4PrimaryKey(primaryKey, this)
|
||||||
|
if (applyToPrimaryKey != null) {
|
||||||
|
primaryKey = applier.applyToPrimaryKey()
|
||||||
|
}
|
||||||
|
return UnopinionatedDefineSubkeys.V4(primaryKey, policy, creationTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class ApplyToV4PrimaryKey(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 addDirectKeySignature(
|
||||||
|
subpacketsCallback: SelfSignatureSubpackets.Callback,
|
||||||
|
hashAlgorithm: HashAlgorithm,
|
||||||
|
bindingTime: Date
|
||||||
|
): PGPKeyPair {
|
||||||
|
val sig = buildDirectKeySignature(keyPair, bindingTime, Policy.getInstance().keyGenerationAlgorithmSuite, hashAlgorithm, subpacketsCallback)
|
||||||
|
keyPair = PGPKeyPair(
|
||||||
|
PGPPublicKey.addCertification(keyPair.publicKey, sig),
|
||||||
|
keyPair.privateKey
|
||||||
|
)
|
||||||
|
return keyPair
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildDirectKeySignature(
|
||||||
|
pair: PGPKeyPair,
|
||||||
|
bindingTime: Date,
|
||||||
|
algorithmSuite: AlgorithmSuite,
|
||||||
|
hashAlgorithm: HashAlgorithm,
|
||||||
|
subpacketsCallback: SelfSignatureSubpackets.Callback
|
||||||
|
): PGPSignature {
|
||||||
|
val builder =
|
||||||
|
DirectKeySelfSignatureBuilder(pair.privateKey, pair.publicKey, hashAlgorithm)
|
||||||
|
|
||||||
|
builder.hashedSubpackets.apply {
|
||||||
|
setSignatureCreationTime(bindingTime)
|
||||||
|
setPreferredHashAlgorithms(algorithmSuite.hashAlgorithms)
|
||||||
|
setPreferredSymmetricKeyAlgorithms(algorithmSuite.symmetricKeyAlgorithms)
|
||||||
|
setPreferredCompressionAlgorithms(algorithmSuite.compressionAlgorithms)
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.applyCallback(subpacketsCallback)
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ApplyToV4Subkey(
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of an [UnopinionatedDefinePrimaryKey] for OpenPGP v6 keys.
|
||||||
|
*
|
||||||
|
* @param creationTime creation time of the primary key
|
||||||
|
*/
|
||||||
|
class V6(policy: Policy, creationTime: Date)
|
||||||
|
: UnopinionatedDefinePrimaryKey<UnopinionatedDefineSubkeys.V6>(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<B : DefineSubkeys<B>>(
|
||||||
|
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<OpinionatedDefineSubkeys>(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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<UnopinionatedDefineSubkeys>(policy, creationTime) {
|
||||||
|
|
||||||
|
class V4(val primaryKey: PGPKeyPair, policy: Policy, creationTime: Date) : UnopinionatedDefineSubkeys(policy, creationTime) {
|
||||||
|
|
||||||
|
val subkeys: MutableList<PGPKeyPair> = mutableListOf()
|
||||||
|
|
||||||
|
override fun addSubkey(
|
||||||
|
type: KeyType,
|
||||||
|
creationTime: Date,
|
||||||
|
applyToSubkey: (ApplyToSubkey.() -> PGPKeyPair)?
|
||||||
|
): V4 = apply {
|
||||||
|
val subkey = OpenPgpKeyPairGenerator.V4().generateSubkey(type, creationTime)
|
||||||
|
val applier = UnopinionatedDefinePrimaryKey.V4.ApplyToV4Subkey(primaryKey, subkey, this)
|
||||||
|
if (applyToSubkey != null) {
|
||||||
|
subkeys.add(applier.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.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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 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 = SelfSignatureSubpackets.nop(),
|
||||||
|
hashAlgorithm: HashAlgorithm = builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm,
|
||||||
|
bindingTime: Date = builder.creationTime
|
||||||
|
): PGPKeyPair
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function that can be applied to subkeys.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
abstract fun addBindingSignature(
|
||||||
|
subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop(),
|
||||||
|
hashAlgorithm: HashAlgorithm = builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm,
|
||||||
|
bindingTime: Date = subkey.publicKey.creationTime
|
||||||
|
): PGPKeyPair
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun generateKeyPair(type: KeyType): KeyPair =
|
||||||
|
KeyPairGenerator.getInstance(type.name, ProviderFactory.provider)
|
||||||
|
.also { it.initialize(type.algorithmSpec) }
|
||||||
|
.generateKeyPair()
|
||||||
|
|
||||||
|
class Templates {
|
||||||
|
|
||||||
|
class V4 {
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun ed25519Curve25519(
|
||||||
|
vararg userId: CharSequence,
|
||||||
|
creationTime: Date = Date()
|
||||||
|
): PGPSecretKeyRing {
|
||||||
|
return buildV4(creationTime = creationTime)
|
||||||
|
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
|
||||||
|
userId.forEach {
|
||||||
|
addUserId(it)
|
||||||
|
}
|
||||||
|
addDirectKeySignature(SelfSignatureSubpackets.applyHashed {
|
||||||
|
setKeyFlags(KeyFlag.CERTIFY_OTHER)
|
||||||
|
})
|
||||||
|
keyPair
|
||||||
|
}
|
||||||
|
.addSubkey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
|
||||||
|
addBindingSignature(SelfSignatureSubpackets.applyHashed {
|
||||||
|
setKeyFlags(KeyFlag.SIGN_DATA)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.addSubkey(KeyType.XDH(XDHSpec._X25519)) {
|
||||||
|
addBindingSignature(SelfSignatureSubpackets.applyHashed {
|
||||||
|
setKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package org.pgpainless.key.generation
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.PublicSubkeyPacket
|
||||||
|
import org.bouncycastle.openpgp.PGPKeyPair
|
||||||
|
import org.bouncycastle.openpgp.PGPPrivateKey
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKey
|
||||||
|
import org.pgpainless.implementation.ImplementationFactory
|
||||||
|
import org.pgpainless.key.generation.type.KeyType
|
||||||
|
import org.pgpainless.provider.ProviderFactory
|
||||||
|
import java.security.KeyPair
|
||||||
|
import java.security.KeyPairGenerator
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generator interface for [PGPKeyPair] objects.
|
||||||
|
*/
|
||||||
|
internal interface OpenPgpKeyPairGenerator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a [PGPKeyPair] in primary key format.
|
||||||
|
*
|
||||||
|
* @param type key type
|
||||||
|
* @param creationTime creation time of the key
|
||||||
|
* @return primary key pair
|
||||||
|
*/
|
||||||
|
fun generatePrimaryKey(type: KeyType, creationTime: Date): PGPKeyPair
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a [PGPKeyPair] in subkey format.
|
||||||
|
*
|
||||||
|
* @param type key type
|
||||||
|
* @param creationTime creation time of the key
|
||||||
|
* @return subkey pair
|
||||||
|
*/
|
||||||
|
fun generateSubkey(type: KeyType, creationTime: Date): PGPKeyPair
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of [OpenPgpKeyPairGenerator] which generates OpenPGP v4 keys.
|
||||||
|
*/
|
||||||
|
class V4 : OpenPgpKeyPairGenerator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate an asymmetric cipher key pair.
|
||||||
|
*
|
||||||
|
* @param type algorithm specification
|
||||||
|
* @return key pair
|
||||||
|
*/
|
||||||
|
private fun generateKeyPair(type: KeyType): KeyPair {
|
||||||
|
return KeyPairGenerator.getInstance(type.name, ProviderFactory.provider)
|
||||||
|
.also { it.initialize(type.algorithmSpec) }
|
||||||
|
.generateKeyPair()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a PGP key pair.
|
||||||
|
*
|
||||||
|
* @param type key type
|
||||||
|
* @param creationTime creation time of the key
|
||||||
|
* @return pgp key pair
|
||||||
|
*/
|
||||||
|
private fun generatePgpKeyPair(type: KeyType, creationTime: Date): PGPKeyPair {
|
||||||
|
return ImplementationFactory.getInstance()
|
||||||
|
.getPGPV4KeyPair(type.algorithm, generateKeyPair(type), creationTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun generatePrimaryKey(type: KeyType, creationTime: Date): PGPKeyPair {
|
||||||
|
// already in primary key format
|
||||||
|
return generatePgpKeyPair(type, creationTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun generateSubkey(type: KeyType, creationTime: Date): PGPKeyPair {
|
||||||
|
val keyPair = generatePgpKeyPair(type, creationTime)
|
||||||
|
|
||||||
|
// We need to convert the keyPair which is in primary key format into subkey format
|
||||||
|
val fpCalc = ImplementationFactory.getInstance().keyFingerprintCalculator
|
||||||
|
val pubkey = keyPair.publicKey
|
||||||
|
val privkey = keyPair.privateKey
|
||||||
|
// transform to subkey packet
|
||||||
|
val subkey =
|
||||||
|
PublicSubkeyPacket(
|
||||||
|
pubkey.algorithm, pubkey.creationTime, pubkey.publicKeyPacket.key)
|
||||||
|
// return as PGP key pair
|
||||||
|
return PGPKeyPair(
|
||||||
|
PGPPublicKey(subkey, fpCalc),
|
||||||
|
PGPPrivateKey(pubkey.keyID, subkey, privkey.privateKeyDataPacket)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,243 +0,0 @@
|
||||||
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 <alice@pgpainless.org>")
|
|
||||||
}
|
|
||||||
.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<B : KeyBuilder<B>>(
|
|
||||||
protected val creationTime: Date
|
|
||||||
) {
|
|
||||||
abstract fun setPrimaryKey(
|
|
||||||
type: KeyType,
|
|
||||||
creationTime: Date = this.creationTime,
|
|
||||||
function: ApplyToPrimaryKey.() -> Unit = {}): B
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class OpinionatedPrimaryKeyBuilder<B : Opinionated, U : Unopinionated>(
|
|
||||||
protected val policy: Policy,
|
|
||||||
creationTime: Date,
|
|
||||||
protected val unopinionated: UnopinionatedPrimaryKeyBuilder<U>
|
|
||||||
) : PrimaryKeyBuilder<Opinionated>(creationTime) {
|
|
||||||
|
|
||||||
fun unopinionated() = unopinionated
|
|
||||||
|
|
||||||
class V4(
|
|
||||||
policy: Policy,
|
|
||||||
creationTime: Date
|
|
||||||
) : OpinionatedPrimaryKeyBuilder<Opinionated.V4, Unopinionated.V4>(
|
|
||||||
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<Opinionated.V6, Unopinionated.V6>(
|
|
||||||
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<B : Unopinionated>(
|
|
||||||
creationTime: Date
|
|
||||||
) : PrimaryKeyBuilder<Unopinionated>(
|
|
||||||
creationTime
|
|
||||||
) {
|
|
||||||
|
|
||||||
class V4(creationTime: Date) : UnopinionatedPrimaryKeyBuilder<Unopinionated.V4>(creationTime) {
|
|
||||||
override fun setPrimaryKey(
|
|
||||||
type: KeyType,
|
|
||||||
creationTime: Date,
|
|
||||||
function: ApplyToPrimaryKey.() -> Unit
|
|
||||||
): Unopinionated.V4 {
|
|
||||||
return Unopinionated.V4()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class V6(creationTime: Date) : UnopinionatedPrimaryKeyBuilder<Unopinionated.V6>(creationTime) {
|
|
||||||
override fun setPrimaryKey(
|
|
||||||
type: KeyType,
|
|
||||||
creationTime: Date,
|
|
||||||
function: ApplyToPrimaryKey.() -> Unit
|
|
||||||
): Unopinionated.V6 {
|
|
||||||
return Unopinionated.V6()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface KeyBuilder<B : KeyBuilder<B>> {
|
|
||||||
|
|
||||||
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<Opinionated> {
|
|
||||||
|
|
||||||
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<Unopinionated> {
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
|
@ -125,21 +125,6 @@ class GenerateOpenPgpKeyTest {
|
||||||
"Cannot read resource $resourceName: InputStream is null.")
|
"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<T : TestInterface<T>> {
|
interface TestInterface<T : TestInterface<T>> {
|
||||||
fun doSomething(): T
|
fun doSomething(): T
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
package org.pgpainless.key.generation
|
||||||
|
|
||||||
|
import openpgp.plusSeconds
|
||||||
|
import org.bouncycastle.extensions.toAsciiArmor
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertFalse
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.pgpainless.algorithm.CertificationType
|
||||||
|
import org.pgpainless.algorithm.HashAlgorithm
|
||||||
|
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.util.DateUtil
|
||||||
|
|
||||||
|
class OpenPgpKeyGeneratorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `minimal call with opinionated builder adds a default DK sig but no user info`() {
|
||||||
|
val key = buildV4()
|
||||||
|
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||||
|
.build()
|
||||||
|
|
||||||
|
assertFalse(key.publicKey.userIDs.hasNext(),
|
||||||
|
"Key MUST NOT have a UserID")
|
||||||
|
assertFalse(key.publicKey.userAttributes.hasNext(),
|
||||||
|
"Key MUST NOT have a UserAttribute")
|
||||||
|
assertEquals(1, key.publicKey.keySignatures.asSequence().toList().size,
|
||||||
|
"Opinionated builder adds exactly one DirectKey signature")
|
||||||
|
|
||||||
|
println(key.toAsciiArmor())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `minimal call with unopinionated builder does not add a default DK sig`() {
|
||||||
|
val key = buildV4()
|
||||||
|
.unopinionated()
|
||||||
|
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||||
|
.build()
|
||||||
|
|
||||||
|
assertFalse(key.publicKey.keySignatures.hasNext())
|
||||||
|
|
||||||
|
println(key.toAsciiArmor())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `adding a direct-key signature with the opinionated builder omits the default DK sig`() {
|
||||||
|
val key = buildV4()
|
||||||
|
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
|
||||||
|
addDirectKeySignature() // "overwrites" the default dk sig
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
|
assertEquals(1, key.publicKey.keySignatures.asSequence().toList().size)
|
||||||
|
|
||||||
|
println(key.toAsciiArmor())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUnopinionatedV4() {
|
||||||
|
// Unopinionated
|
||||||
|
buildV4()
|
||||||
|
.unopinionated()
|
||||||
|
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
|
||||||
|
addDirectKeySignature()
|
||||||
|
addUserId("Alice <alice@pgpainless.org>")
|
||||||
|
}
|
||||||
|
.addSubkey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
|
||||||
|
addBindingSignature()
|
||||||
|
}
|
||||||
|
.addSubkey(KeyType.XDH(XDHSpec._X25519)) {
|
||||||
|
addBindingSignature()
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOpinionatedV4() {
|
||||||
|
// Opinionated
|
||||||
|
val time = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC")
|
||||||
|
buildV4(creationTime = time)
|
||||||
|
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
|
||||||
|
addDirectKeySignature()
|
||||||
|
addUserId("Alice",
|
||||||
|
bindingTime = time.plusSeconds(60L)!!,
|
||||||
|
hashAlgorithm = HashAlgorithm.SHA384,
|
||||||
|
certificationType = CertificationType.GENERIC)
|
||||||
|
}
|
||||||
|
.addSubkey(KeyType.XDH(XDHSpec._X25519)) {
|
||||||
|
addBindingSignature()
|
||||||
|
}
|
||||||
|
.build().let { println(it.toAsciiArmor()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testV4Ed25519Curve25519Template() {
|
||||||
|
Templates.V4.ed25519Curve25519("Alice <alice@example.org>", "Alice <alice@pgpainless.org>")
|
||||||
|
.let {
|
||||||
|
println(it.toAsciiArmor())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue