1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-12-25 04:17:59 +01:00

Further progress

This commit is contained in:
Paul Schaub 2024-01-24 18:47:27 +01:00
parent 6416ef1e07
commit 02f6e37c4f
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
3 changed files with 96 additions and 92 deletions

View file

@ -68,11 +68,8 @@ class BaseOpenPgpKeyBuilder {
class BaseV4PrimaryKeyBuilder(type: KeyType, creationTime: Date, policy: Policy) : class BaseV4PrimaryKeyBuilder(type: KeyType, creationTime: Date, policy: Policy) :
BaseV4KeyBuilder<BaseV4PrimaryKeyBuilder>(type, creationTime, policy = policy) { BaseV4KeyBuilder<BaseV4PrimaryKeyBuilder>(type, creationTime, policy = policy) {
internal fun isWithoutUserIds() = !key.publicKey.userIDs.hasNext()
fun userId( fun userId(
userId: CharSequence, userId: CharSequence,
algorithmSuite: AlgorithmSuite = policy.keyGenerationAlgorithmSuite,
certificationType: CertificationType = CertificationType.POSITIVE, certificationType: CertificationType = CertificationType.POSITIVE,
bindingTime: Date = creationTime, bindingTime: Date = creationTime,
hashAlgorithm: HashAlgorithm = hashAlgorithm: HashAlgorithm =
@ -81,12 +78,7 @@ class BaseOpenPgpKeyBuilder {
) = apply { ) = apply {
val sig = val sig =
buildCertificationFor( buildCertificationFor(
userId, userId, certificationType, bindingTime, hashAlgorithm, subpacketsCallback)
algorithmSuite,
certificationType,
bindingTime,
hashAlgorithm,
subpacketsCallback)
key = key =
PGPKeyPair( PGPKeyPair(
PGPPublicKey.addCertification(key.publicKey, userId.toString(), sig), PGPPublicKey.addCertification(key.publicKey, userId.toString(), sig),
@ -95,7 +87,6 @@ class BaseOpenPgpKeyBuilder {
fun buildCertificationFor( fun buildCertificationFor(
userId: CharSequence, userId: CharSequence,
algorithmSuite: AlgorithmSuite,
certificationType: CertificationType, certificationType: CertificationType,
bindingTime: Date, bindingTime: Date,
hashAlgorithm: HashAlgorithm, hashAlgorithm: HashAlgorithm,
@ -104,19 +95,13 @@ class BaseOpenPgpKeyBuilder {
val builder = val builder =
SelfSignatureBuilder( SelfSignatureBuilder(
key.privateKey, key.publicKey, certificationType.signatureType, hashAlgorithm) key.privateKey, key.publicKey, certificationType.signatureType, hashAlgorithm)
builder.hashedSubpackets.apply { builder.hashedSubpackets.apply { setSignatureCreationTime(bindingTime) }
setSignatureCreationTime(bindingTime)
setPreferredHashAlgorithms(algorithmSuite.hashAlgorithms)
setPreferredSymmetricKeyAlgorithms(algorithmSuite.symmetricKeyAlgorithms)
setPreferredCompressionAlgorithms(algorithmSuite.compressionAlgorithms)
}
builder.applyCallback(subpacketsCallback) builder.applyCallback(subpacketsCallback)
return builder.build(userId) return builder.build(userId)
} }
fun userAttribute( fun userAttribute(
userAttribute: PGPUserAttributeSubpacketVector, userAttribute: PGPUserAttributeSubpacketVector,
algorithmSuite: AlgorithmSuite = policy.keyGenerationAlgorithmSuite,
certificationType: CertificationType = CertificationType.POSITIVE, certificationType: CertificationType = CertificationType.POSITIVE,
bindingTime: Date = creationTime, bindingTime: Date = creationTime,
hashAlgorithm: HashAlgorithm = hashAlgorithm: HashAlgorithm =
@ -126,7 +111,6 @@ class BaseOpenPgpKeyBuilder {
val sig = val sig =
buildCertificationFor( buildCertificationFor(
userAttribute, userAttribute,
algorithmSuite,
certificationType, certificationType,
bindingTime, bindingTime,
hashAlgorithm, hashAlgorithm,
@ -139,7 +123,6 @@ class BaseOpenPgpKeyBuilder {
fun buildCertificationFor( fun buildCertificationFor(
userAttribute: PGPUserAttributeSubpacketVector, userAttribute: PGPUserAttributeSubpacketVector,
algorithmSuite: AlgorithmSuite,
certificationType: CertificationType, certificationType: CertificationType,
bindingTime: Date, bindingTime: Date,
hashAlgorithm: HashAlgorithm, hashAlgorithm: HashAlgorithm,
@ -148,12 +131,7 @@ class BaseOpenPgpKeyBuilder {
val builder = val builder =
SelfSignatureBuilder( SelfSignatureBuilder(
key.privateKey, key.publicKey, certificationType.signatureType, hashAlgorithm) key.privateKey, key.publicKey, certificationType.signatureType, hashAlgorithm)
builder.hashedSubpackets.apply { builder.hashedSubpackets.apply { setSignatureCreationTime(bindingTime) }
setSignatureCreationTime(bindingTime)
setPreferredHashAlgorithms(algorithmSuite.hashAlgorithms)
setPreferredSymmetricKeyAlgorithms(algorithmSuite.symmetricKeyAlgorithms)
setPreferredCompressionAlgorithms(algorithmSuite.compressionAlgorithms)
}
builder.applyCallback(subpacketsCallback) builder.applyCallback(subpacketsCallback)
return builder.build(userAttribute) return builder.build(userAttribute)
} }

View file

@ -17,79 +17,130 @@ import org.pgpainless.key.protection.SecretKeyRingProtector
import org.pgpainless.policy.Policy import org.pgpainless.policy.Policy
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets import org.pgpainless.signature.subpackets.SelfSignatureSubpackets
/**
* OpenPGP key builder. This implementation supersedes the old [KeyRingBuilder].
*
* @param policy algorithm policy, which is consulted to determine suitable algorithms
* @param referenceTime reference time for key generation
* @param preferences set of preferred algorithms and enabled features
*/
open class OpenPgpKeyBuilder( open class OpenPgpKeyBuilder(
protected val policy: Policy, protected val policy: Policy,
protected val referenceTime: Date = Date(), protected val referenceTime: Date = Date(),
protected val keyGenerationPolicy: AlgorithmSuite = policy.keyGenerationAlgorithmSuite protected val preferences: AlgorithmSuite = policy.keyGenerationAlgorithmSuite
) { ) {
/**
* Build an OpenPGP v4 key. The result will be a key compliant to RFC4880, RFC6637.
*
* @param keyType type of the primary key
* @param flags key flags for the primary key. Defaults to [KeyFlag.CERTIFY_OTHER].
* @return [V4OpenPgpKeyBuilder] which can be further modified, e.g. add subkeys, user-ids etc.
*/
fun buildV4Key( fun buildV4Key(
keyType: KeyType, keyType: KeyType,
): V4OpenPgpKeyBuilder = flags: List<KeyFlag>? = listOf(KeyFlag.CERTIFY_OTHER)
V4OpenPgpKeyBuilder(keyType, policy, referenceTime, keyGenerationPolicy) ): V4OpenPgpKeyBuilder = V4OpenPgpKeyBuilder(keyType, flags, policy, referenceTime, preferences)
class V4OpenPgpKeyBuilder( /**
keyType: KeyType, * Builder for version 4 OpenPGP keys.
*
* @param primaryKeyType type of the primary key
* @param primaryFlags list of key-flags for the primary key. Can be `null`.
* @param policy algorithm policy
* @param referenceTime reference time for key generation
* @param preferences set of algorithm preferences and enabled features for the key
*/
class V4OpenPgpKeyBuilder
internal constructor(
primaryKeyType: KeyType,
primaryFlags: List<KeyFlag>?,
policy: Policy, policy: Policy,
referenceTime: Date, referenceTime: Date,
keyGenerationPolicy: AlgorithmSuite preferences: AlgorithmSuite
) : OpenPgpKeyBuilder(policy, referenceTime, keyGenerationPolicy) { ) : OpenPgpKeyBuilder(policy, referenceTime, preferences) {
private val primaryKey = private val primaryKey =
BaseOpenPgpKeyBuilder.BaseV4PrimaryKeyBuilder(keyType, referenceTime, policy) BaseOpenPgpKeyBuilder.BaseV4PrimaryKeyBuilder(primaryKeyType, referenceTime, policy)
private val subkeys = mutableListOf<BaseOpenPgpKeyBuilder.BaseV4SubkeyBuilder>() private val subkeys = mutableListOf<BaseOpenPgpKeyBuilder.BaseV4SubkeyBuilder>()
private val preferencesCallback =
SelfSignatureSubpackets.applyHashed {
setPreferredSymmetricKeyAlgorithms(preferences.symmetricKeyAlgorithms)
setPreferredHashAlgorithms(preferences.hashAlgorithms)
setPreferredCompressionAlgorithms(preferences.compressionAlgorithms)
setFeatures(*preferences.features.toTypedArray())
primaryFlags?.let { setKeyFlags(*it.toTypedArray()) }
}
/**
* Add a user-id to the key. The subpackets of the binding signature are prepopulated,
* setting algorithm preferences and features. However, the subpackets can still be modified
* using the provided [subpacketsCallback].
*
* @param userId user-id to add
* @param subpacketsCallback callback to modify the user-id binding signatures subpackets.
* @return this
*/
fun addUserId( fun addUserId(
userId: CharSequence, userId: CharSequence,
algorithmSuite: AlgorithmSuite = keyGenerationPolicy,
subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop() subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop()
) = apply { ) = apply {
primaryKey.userId(userId, algorithmSuite, subpacketsCallback = subpacketsCallback) primaryKey.userId(
userId, subpacketsCallback = preferencesCallback.then(subpacketsCallback))
} }
/**
* Add a user-attribute to the key. The subpackets of the binding signature are
* prepopulated, setting algorithm preferences and features. However, the subpackets can
* still be modified using the provided [subpacketsCallback].
*
* @param attribute user-attribute to add
* @param subpacketsCallback callback to modify the user-attribute binding signatures
* subpackets.
* @return this
*/
fun addUserAttribute( fun addUserAttribute(
attribute: PGPUserAttributeSubpacketVector, attribute: PGPUserAttributeSubpacketVector,
algorithmSuite: AlgorithmSuite = keyGenerationPolicy,
subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop() subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop()
) = apply { ) = apply {
primaryKey.userAttribute( primaryKey.userAttribute(
attribute, algorithmSuite, subpacketsCallback = subpacketsCallback) attribute, subpacketsCallback = preferencesCallback.then(subpacketsCallback))
} }
/**
* Add a subkey to the key. The subpackets of the binding signature will be populated with
* issuer information, the passed in [bindingTime] as signature creation time and given
* key-flags (if non-null). You can further manipulate the subpackets by passing in an
* appropriate [subpacketsCallback].
*
* @param keyType type of the key
* @param creationTime creation time of the key. Defaults to [referenceTime]
* @param bindingTime creation time of the binding signature. Defaults to [creationTime]
* @param keyFlags list of key-flags for the subkey.
* @param subpacketsCallback callback to modify the subpackets of the binding signature
*/
fun addSubkey( fun addSubkey(
keyType: KeyType, keyType: KeyType,
creationTime: Date = referenceTime, creationTime: Date = referenceTime,
bindingTime: Date = creationTime, bindingTime: Date = creationTime,
keyFlags: List<KeyFlag>? keyFlags: List<KeyFlag>? = null,
subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop()
) = ) =
addSubkey( addSubkey(
BaseOpenPgpKeyBuilder.BaseV4SubkeyBuilder( BaseOpenPgpKeyBuilder.BaseV4SubkeyBuilder(
keyType, creationTime, policy, primaryKey), keyType, creationTime, policy, primaryKey),
bindingTime, SelfSignatureSubpackets.applyHashed {
keyFlags) setSignatureCreationTime(bindingTime)
keyFlags?.let { setKeyFlags(it) }
}
.then(subpacketsCallback))
fun addSubkey( fun addSubkey(
subkeyBuilder: BaseOpenPgpKeyBuilder.BaseV4SubkeyBuilder, subkeyBuilder: BaseOpenPgpKeyBuilder.BaseV4SubkeyBuilder,
bindingTime: Date = subkeyBuilder.creationTime, subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop()
keyFlags: List<KeyFlag>?
) = apply { ) = apply {
subkeys.add( subkeys.add(subkeyBuilder.bindingSignature(subpacketsCallback = subpacketsCallback))
subkeyBuilder.also {
it.bindingSignature(
bindingTime,
subpacketsCallback =
object : SelfSignatureSubpackets.Callback {
override fun modifyHashedSubpackets(
hashedSubpackets: SelfSignatureSubpackets
) {
hashedSubpackets.setSignatureCreationTime(bindingTime)
keyFlags?.let { flagList ->
hashedSubpackets.setKeyFlags(flagList)
}
}
})
})
} }
fun addEncryptionSubkey( fun addEncryptionSubkey(
@ -113,44 +164,18 @@ open class OpenPgpKeyBuilder(
protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys() protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys()
): PGPSecretKeyRing { ): PGPSecretKeyRing {
// Add DK sig in case of no user-id // add a direct key sig with preferences
if (primaryKey.isWithoutUserIds()) { primaryKey.directKeySignature(subpacketsCallback = preferencesCallback)
primaryKey.directKeySignature(
subpacketsCallback = defaultPrimarySubpacketsCallback())
}
return PGPSecretKeyRing( return PGPSecretKeyRing(
mutableListOf( mutableListOf(
PGPSecretKey( toSecretKey(primaryKey, true, protector.getEncryptor(primaryKey.key.keyID)))
primaryKey.key.privateKey,
primaryKey.key.publicKey,
ImplementationFactory.getInstance().v4FingerprintCalculator,
true,
protector.getEncryptor(primaryKey.key.keyID)))
.plus( .plus(
subkeys.map { subkeys.map {
PGPSecretKey( toSecretKey(it, false, protector.getEncryptor(it.key.keyID))
it.key.privateKey,
it.key.publicKey,
ImplementationFactory.getInstance().v4FingerprintCalculator,
false,
protector.getEncryptor(it.key.keyID))
})) }))
} }
private fun defaultPrimarySubpacketsCallback(): SelfSignatureSubpackets.Callback =
object : SelfSignatureSubpackets.Callback {
override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) {
hashedSubpackets.apply {
setPreferredHashAlgorithms(keyGenerationPolicy.hashAlgorithms)
setPreferredSymmetricKeyAlgorithms(
keyGenerationPolicy.symmetricKeyAlgorithms)
setPreferredCompressionAlgorithms(keyGenerationPolicy.compressionAlgorithms)
setKeyFlags(KeyFlag.CERTIFY_OTHER)
}
}
}
private fun toSecretKey( private fun toSecretKey(
key: BaseOpenPgpKeyBuilder.BaseV4KeyBuilder<*>, key: BaseOpenPgpKeyBuilder.BaseV4KeyBuilder<*>,
isPrimaryKey: Boolean, isPrimaryKey: Boolean,

View file

@ -4,6 +4,7 @@ import org.bouncycastle.bcpg.attr.ImageAttribute
import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVectorGenerator import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVectorGenerator
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.pgpainless.PGPainless import org.pgpainless.PGPainless
import org.pgpainless.algorithm.KeyFlag
import org.pgpainless.key.generation.type.KeyType import org.pgpainless.key.generation.type.KeyType
import org.pgpainless.key.generation.type.eddsa.EdDSACurve import org.pgpainless.key.generation.type.eddsa.EdDSACurve
import org.pgpainless.key.generation.type.xdh.XDHSpec import org.pgpainless.key.generation.type.xdh.XDHSpec
@ -18,7 +19,7 @@ class OpenPgpKeyBuilderTest {
val date = DateUtil.parseUTCDate("2020-04-01 10:00:00 UTC") val date = DateUtil.parseUTCDate("2020-04-01 10:00:00 UTC")
val key = val key =
OpenPgpKeyBuilder(Policy.getInstance(), date) OpenPgpKeyBuilder(Policy.getInstance(), date)
.buildV4Key(KeyType.EDDSA(EdDSACurve._Ed25519)) .buildV4Key(KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.CERTIFY_OTHER))
.addUserId("Alice") .addUserId("Alice")
.addUserAttribute( .addUserAttribute(
PGPUserAttributeSubpacketVectorGenerator() PGPUserAttributeSubpacketVectorGenerator()
@ -34,7 +35,7 @@ class OpenPgpKeyBuilderTest {
fun minimal() { fun minimal() {
val key = val key =
OpenPgpKeyBuilder(Policy.getInstance()) OpenPgpKeyBuilder(Policy.getInstance())
.buildV4Key(KeyType.EDDSA(EdDSACurve._Ed25519)) .buildV4Key(KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.CERTIFY_OTHER))
.build() .build()
println(PGPainless.asciiArmor(key)) println(PGPainless.asciiArmor(key))
} }
@ -43,7 +44,7 @@ class OpenPgpKeyBuilderTest {
fun minimalWithUserId() { fun minimalWithUserId() {
val key = val key =
OpenPgpKeyBuilder(Policy.getInstance()) OpenPgpKeyBuilder(Policy.getInstance())
.buildV4Key(KeyType.EDDSA(EdDSACurve._Ed25519)) .buildV4Key(KeyType.EDDSA(EdDSACurve._Ed25519), listOf(KeyFlag.CERTIFY_OTHER))
.addUserId("Alice <alice@pgpainless.org>") .addUserId("Alice <alice@pgpainless.org>")
.build() .build()
println(PGPainless.asciiArmor(key)) println(PGPainless.asciiArmor(key))