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

Parameter sanitization and tests

This commit is contained in:
Paul Schaub 2024-02-21 13:08:17 +01:00
parent 902d5f2973
commit f21f257c2c
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
2 changed files with 296 additions and 5 deletions

View file

@ -6,6 +6,7 @@ package org.pgpainless.key.generation
import java.io.InputStream import java.io.InputStream
import java.util.Date import java.util.Date
import openpgp.formatUTC
import org.bouncycastle.bcpg.attr.ImageAttribute import org.bouncycastle.bcpg.attr.ImageAttribute
import org.bouncycastle.extensions.plusCertification import org.bouncycastle.extensions.plusCertification
import org.bouncycastle.openpgp.PGPKeyPair import org.bouncycastle.openpgp.PGPKeyPair
@ -138,10 +139,8 @@ internal constructor(val policy: Policy, val creationTime: Date, val preferences
): PGPKeyPair ): PGPKeyPair
/** /**
* Define the primary key for the OpenPGP key. * Define the primary key for the OpenPGP key. The [applyToPrimaryKey] function block can be
* The [applyToPrimaryKey] function block can be used to add UserIDs and preferences to * used to add UserIDs and preferences to the key. Example:
* the key.
* Example:
* ``` * ```
* setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) { * setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
* addDirectKeySignature(...) * addDirectKeySignature(...)
@ -162,6 +161,25 @@ internal constructor(val policy: Policy, val creationTime: Date, val preferences
creationTime: Date = this.creationTime, creationTime: Date = this.creationTime,
applyToPrimaryKey: (ApplyToPrimaryKey.() -> PGPKeyPair)? = null applyToPrimaryKey: (ApplyToPrimaryKey.() -> PGPKeyPair)? = null
): O ): O
/**
* Sanitize the [HashAlgorithm] used for creating a signature by comparing it to the [Policy].
*
* @param algorithm hash algorithm
*/
internal open fun sanitizeHashAlgorithm(algorithm: HashAlgorithm) {
// Do nothing
}
/**
* Sanitize the creation time of a self-certification signature.
*
* @param bindingTime signature creation time
* @param primaryKey primary key
*/
internal open fun sanitizeBindingTime(bindingTime: Date, primaryKey: PGPKeyPair) {
// Do nothing
}
} }
/** Implementation of [DefinePrimaryKey] build for version 4 OpenPGP keys. */ /** Implementation of [DefinePrimaryKey] build for version 4 OpenPGP keys. */
@ -218,6 +236,8 @@ internal constructor(
applyToSubkey: (ApplyToSubkey.() -> PGPKeyPair)? = null applyToSubkey: (ApplyToSubkey.() -> PGPKeyPair)? = null
): B = ): B =
apply { apply {
sanitizeSubkeyCreationTime(creationTime, primaryKey)
val subkey = generateSubkey(type, creationTime) val subkey = generateSubkey(type, creationTime)
subkeys.add(applyToSubkey(subkey, applyToSubkey)) subkeys.add(applyToSubkey(subkey, applyToSubkey))
} }
@ -264,6 +284,35 @@ internal constructor(
* @return finished [PGPSecretKeyRing] * @return finished [PGPSecretKeyRing]
*/ */
fun build(passphrase: Passphrase) = build(SecretKeyRingProtector.unlockAnyKeyWith(passphrase)) fun build(passphrase: Passphrase) = build(SecretKeyRingProtector.unlockAnyKeyWith(passphrase))
/**
* Sanitize the [HashAlgorithm] used for creating a signature by comparing it to the [Policy].
*
* @param algorithm hash algorithm
*/
internal open fun sanitizeHashAlgorithm(algorithm: HashAlgorithm) {
// Do nothing
}
/**
* Sanitize the signature creation time of a subkey binding signature.
*
* @param bindingTime creation time of the binding signature
* @param subkey subkey
*/
internal open fun sanitizeBindingTime(bindingTime: Date, subkey: PGPKeyPair) {
// Do nothing
}
/**
* Sanitize the creation time of the subkey.
*
* @param subkeyCreationTime creation time of the subkey
* @param primaryKey primary key
*/
internal open fun sanitizeSubkeyCreationTime(subkeyCreationTime: Date, primaryKey: PGPKeyPair) {
// Do nothing
}
} }
/** /**
@ -411,6 +460,21 @@ internal constructor(policy: Policy, creationTime: Date, preferences: AlgorithmS
return OpinionatedDefineSubkeysV4(key, policy, creationTime) return OpinionatedDefineSubkeysV4(key, policy, creationTime)
} }
override fun sanitizeHashAlgorithm(algorithm: HashAlgorithm) {
require(policy.certificationSignatureHashAlgorithmPolicy.isAcceptable(algorithm)) {
"Unacceptable Hash Algorithm. $algorithm cannot be used to generate self-certifications" +
" as per the current hash algorithm policy."
}
}
override fun sanitizeBindingTime(bindingTime: Date, primaryKey: PGPKeyPair) {
require(!bindingTime.before(primaryKey.publicKey.creationTime)) {
"Signature creation time predates primary key creation time. " +
"Signature was created at ${bindingTime.formatUTC()}, " +
"key was created at ${primaryKey.publicKey.creationTime.formatUTC()}."
}
}
} }
/** /**
@ -513,6 +577,29 @@ internal constructor(primaryKey: PGPKeyPair, policy: Policy, creationTime: Date)
}) })
} }
) = addSubkey(type, creationTime, applyToSubkey) ) = addSubkey(type, creationTime, applyToSubkey)
override fun sanitizeHashAlgorithm(algorithm: HashAlgorithm) {
require(policy.certificationSignatureHashAlgorithmPolicy.isAcceptable(algorithm)) {
"Unacceptable Hash Algorithm. $algorithm cannot be used to generate self-certifications" +
" as per the current hash algorithm policy."
}
}
override fun sanitizeBindingTime(bindingTime: Date, subkey: PGPKeyPair) {
require(!bindingTime.before(subkey.publicKey.creationTime)) {
"Creation time of binding signature predates subkey creation time. " +
"Signature was created at ${bindingTime.formatUTC()}, " +
"Subkey was created at ${subkey.publicKey.creationTime.formatUTC()}."
}
}
override fun sanitizeSubkeyCreationTime(subkeyCreationTime: Date, primaryKey: PGPKeyPair) {
require(!subkeyCreationTime.before(primaryKey.publicKey.creationTime)) {
"Subkey creation time predates primary key creation time. " +
"Subkey was created at ${subkeyCreationTime.formatUTC()}, " +
"Primary key was created at ${primaryKey.publicKey.creationTime.formatUTC()}."
}
}
} }
/** /**
@ -586,6 +673,9 @@ internal constructor(var keyPair: PGPKeyPair, val builder: DefinePrimaryKey<*>)
builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm, builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm,
bindingTime: Date = builder.creationTime bindingTime: Date = builder.creationTime
): PGPKeyPair { ): PGPKeyPair {
builder.sanitizeHashAlgorithm(hashAlgorithm)
builder.sanitizeBindingTime(bindingTime, keyPair)
val callback = builder.userIdSubpackets(keyPair).then(subpacketsCallback) val callback = builder.userIdSubpackets(keyPair).then(subpacketsCallback)
return doAddUserId(userId, callback, certificationType, hashAlgorithm, bindingTime) return doAddUserId(userId, callback, certificationType, hashAlgorithm, bindingTime)
} }
@ -628,8 +718,10 @@ internal constructor(var keyPair: PGPKeyPair, val builder: DefinePrimaryKey<*>)
builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm, builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm,
bindingTime: Date = builder.creationTime bindingTime: Date = builder.creationTime
): PGPKeyPair { ): PGPKeyPair {
val callback = builder.userAttributeSubpackets(keyPair).then(subpacketsCallback) builder.sanitizeHashAlgorithm(hashAlgorithm)
builder.sanitizeBindingTime(bindingTime, keyPair)
val callback = builder.userAttributeSubpackets(keyPair).then(subpacketsCallback)
return doAddUserAttribute( return doAddUserAttribute(
userAttribute, callback, certificationType, hashAlgorithm, bindingTime) userAttribute, callback, certificationType, hashAlgorithm, bindingTime)
} }
@ -697,6 +789,9 @@ internal constructor(var keyPair: PGPKeyPair, val builder: DefinePrimaryKey<*>)
builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm, builder.policy.certificationSignatureHashAlgorithmPolicy.defaultHashAlgorithm,
bindingTime: Date = builder.creationTime bindingTime: Date = builder.creationTime
): PGPKeyPair { ): PGPKeyPair {
builder.sanitizeHashAlgorithm(hashAlgorithm)
builder.sanitizeBindingTime(bindingTime, keyPair)
skipDefaultSignature() skipDefaultSignature()
val callback = builder.directKeySignatureSubpackets().then(subpacketsCallback) val callback = builder.directKeySignatureSubpackets().then(subpacketsCallback)
return doAddDirectKeySignature(callback, hashAlgorithm, bindingTime) return doAddDirectKeySignature(callback, hashAlgorithm, bindingTime)
@ -855,6 +950,9 @@ internal constructor(primaryKey: PGPKeyPair, subkey: PGPKeyPair, builder: Define
hashAlgorithm: HashAlgorithm, hashAlgorithm: HashAlgorithm,
bindingTime: Date bindingTime: Date
): PGPKeyPair { ): PGPKeyPair {
builder.sanitizeHashAlgorithm(hashAlgorithm)
builder.sanitizeBindingTime(bindingTime, subkey)
val sig = val sig =
buildBindingSignature( buildBindingSignature(
primaryKey, subkey, hashAlgorithm, bindingTime, subpacketsCallback) primaryKey, subkey, hashAlgorithm, bindingTime, subpacketsCallback)

View file

@ -6,11 +6,13 @@ package org.pgpainless.key.generation
import org.bouncycastle.bcpg.sig.PrimaryUserID import org.bouncycastle.bcpg.sig.PrimaryUserID
import org.bouncycastle.extensions.toAsciiArmor import org.bouncycastle.extensions.toAsciiArmor
import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVectorGenerator
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import org.pgpainless.PGPainless import org.pgpainless.PGPainless
import org.pgpainless.algorithm.HashAlgorithm
import org.pgpainless.algorithm.KeyFlag import org.pgpainless.algorithm.KeyFlag
import org.pgpainless.algorithm.PublicKeyAlgorithm import org.pgpainless.algorithm.PublicKeyAlgorithm
import org.pgpainless.key.generation.type.KeyType import org.pgpainless.key.generation.type.KeyType
@ -194,4 +196,195 @@ class OpenPgpKeyGeneratorTest {
fun testModernKeyGeneration() { fun testModernKeyGeneration() {
println(KeyRingTemplates().modernKeyRing("null").toAsciiArmor()) println(KeyRingTemplates().modernKeyRing("null").toAsciiArmor())
} }
@Test
fun `opinionated add UserID with weak hash algorithm fails`() {
val policy = Policy()
assertThrows<IllegalArgumentException> {
OpenPgpKeyGenerator.buildV4(policy).setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
addUserId("Alice <alice@example.org>", hashAlgorithm = HashAlgorithm.SHA1)
}
}
}
@Test
fun `unopinionated add UserID with weak hash algorithm is okay`() {
val policy = Policy()
OpenPgpKeyGenerator.buildV4(policy).unopinionated().setPrimaryKey(
KeyType.EDDSA(EdDSACurve._Ed25519)) {
addUserId("Alice <alice@example.org>", hashAlgorithm = HashAlgorithm.SHA1)
}
}
@Test
fun `opinionated add UserAttribute with weak hash algorithm fails`() {
val policy = Policy()
assertThrows<IllegalArgumentException> {
OpenPgpKeyGenerator.buildV4(policy).setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
addUserAttribute(
PGPUserAttributeSubpacketVectorGenerator().generate(),
hashAlgorithm = HashAlgorithm.SHA1)
}
}
}
@Test
fun `unopinionated add UserAttribute with weak hash algorithm is okay`() {
val policy = Policy()
OpenPgpKeyGenerator.buildV4(policy).unopinionated().setPrimaryKey(
KeyType.EDDSA(EdDSACurve._Ed25519)) {
addUserAttribute(
PGPUserAttributeSubpacketVectorGenerator().generate(),
hashAlgorithm = HashAlgorithm.SHA1)
}
}
@Test
fun `opinionated add DK sig with weak hash algorithm fails`() {
val policy = Policy()
assertThrows<IllegalArgumentException> {
OpenPgpKeyGenerator.buildV4(policy).setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
addDirectKeySignature(hashAlgorithm = HashAlgorithm.SHA1)
}
}
}
@Test
fun `unopinionated add DK sig with weak hash algorithm is okay`() {
val policy = Policy()
OpenPgpKeyGenerator.buildV4(policy).unopinionated().setPrimaryKey(
KeyType.EDDSA(EdDSACurve._Ed25519)) {
addDirectKeySignature(hashAlgorithm = HashAlgorithm.SHA1)
}
}
@Test
fun `opinionated add UserID with predating binding time fails`() {
val policy = Policy()
val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC")
val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC")
assertThrows<IllegalArgumentException> {
OpenPgpKeyGenerator.buildV4(policy, t1).setPrimaryKey(
KeyType.EDDSA(EdDSACurve._Ed25519)) {
addUserId("Alice <alice@example.org>", bindingTime = t0)
}
}
}
@Test
fun `unopinionated add UserID with predating binding time is okay`() {
val policy = Policy()
val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC")
val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC")
OpenPgpKeyGenerator.buildV4(policy, t1).unopinionated().setPrimaryKey(
KeyType.EDDSA(EdDSACurve._Ed25519)) {
addUserId("Alice <alice@example.org>", bindingTime = t0)
}
}
@Test
fun `opinionated add UserAttribute with predating binding time fails`() {
val policy = Policy()
val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC")
val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC")
assertThrows<IllegalArgumentException> {
OpenPgpKeyGenerator.buildV4(policy, t1).setPrimaryKey(
KeyType.EDDSA(EdDSACurve._Ed25519)) {
addUserAttribute(
PGPUserAttributeSubpacketVectorGenerator().generate(), bindingTime = t0)
}
}
}
@Test
fun `unopinionated add UserAttribute with predating binding time is okay`() {
val policy = Policy()
val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC")
val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC")
OpenPgpKeyGenerator.buildV4(policy, t1).unopinionated().setPrimaryKey(
KeyType.EDDSA(EdDSACurve._Ed25519)) {
addUserAttribute(
PGPUserAttributeSubpacketVectorGenerator().generate(), bindingTime = t0)
}
}
@Test
fun `opinionated add DK sig with predating binding time fails`() {
val policy = Policy()
val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC")
val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC")
assertThrows<IllegalArgumentException> {
OpenPgpKeyGenerator.buildV4(policy, t1).setPrimaryKey(
KeyType.EDDSA(EdDSACurve._Ed25519)) {
addDirectKeySignature(bindingTime = t0)
}
}
}
@Test
fun `unopinionated add DK sig with predating binding time is okay`() {
val policy = Policy()
val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC")
val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC")
OpenPgpKeyGenerator.buildV4(policy, t1).unopinionated().setPrimaryKey(
KeyType.EDDSA(EdDSACurve._Ed25519)) {
addDirectKeySignature(bindingTime = t0)
}
}
@Test
fun `opinionated add subkey with predating creation time fails`() {
val policy = Policy()
val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC")
val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC")
assertThrows<IllegalArgumentException> {
OpenPgpKeyGenerator.buildV4(policy, t1)
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519))
.addSubkey(KeyType.XDH(XDHSpec._X25519), t0)
}
}
@Test
fun `unopinionated add subkey with predating creation time is okay`() {
val policy = Policy()
val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC")
val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC")
OpenPgpKeyGenerator.buildV4(policy, t1)
.unopinionated()
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519))
.addSubkey(KeyType.XDH(XDHSpec._X25519), t0)
}
@Test
fun `opinionated add subkey with predating binding time fails`() {
val policy = Policy()
val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC")
val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC")
assertThrows<IllegalArgumentException> {
OpenPgpKeyGenerator.buildV4(policy, t1)
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519))
.addSubkey(KeyType.XDH(XDHSpec._X25519)) { addBindingSignature(bindingTime = t0) }
}
}
@Test
fun `unopinionated add subkey with predating binding time is okay`() {
val policy = Policy()
val t0 = DateUtil.parseUTCDate("2024-01-01 00:00:00 UTC")
val t1 = DateUtil.parseUTCDate("2024-02-01 00:00:00 UTC")
OpenPgpKeyGenerator.buildV4(policy, t1)
.unopinionated()
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519))
.addSubkey(KeyType.XDH(XDHSpec._X25519)) { addBindingSignature(bindingTime = t0) }
}
} }