Per-key protection and default binding signatures
This commit is contained in:
parent
b4240ac9f7
commit
f1aa910431
|
@ -5,6 +5,7 @@
|
||||||
package org.pgpainless.key
|
package org.pgpainless.key
|
||||||
|
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
import org.bouncycastle.openpgp.PGPKeyPair
|
||||||
import org.bouncycastle.openpgp.PGPKeyRing
|
import org.bouncycastle.openpgp.PGPKeyRing
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey
|
import org.bouncycastle.openpgp.PGPPublicKey
|
||||||
import org.bouncycastle.openpgp.PGPSecretKey
|
import org.bouncycastle.openpgp.PGPSecretKey
|
||||||
|
@ -108,6 +109,8 @@ abstract class OpenPgpFingerprint : CharSequence, Comparable<OpenPgpFingerprint>
|
||||||
*/
|
*/
|
||||||
@JvmStatic fun of(key: PGPPublicKey): OpenPgpFingerprint = of(key.version, key.fingerprint)
|
@JvmStatic fun of(key: PGPPublicKey): OpenPgpFingerprint = of(key.version, key.fingerprint)
|
||||||
|
|
||||||
|
@JvmStatic fun of(key: PGPKeyPair): OpenPgpFingerprint = of(key.publicKey)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun of(keyVersion: Int, binaryFingerprint: ByteArray): OpenPgpFingerprint =
|
fun of(keyVersion: Int, binaryFingerprint: ByteArray): OpenPgpFingerprint =
|
||||||
when (keyVersion) {
|
when (keyVersion) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.pgpainless.algorithm.KeyFlag
|
||||||
import org.pgpainless.algorithm.PublicKeyAlgorithm
|
import org.pgpainless.algorithm.PublicKeyAlgorithm
|
||||||
import org.pgpainless.bouncycastle.extensions.plusCertification
|
import org.pgpainless.bouncycastle.extensions.plusCertification
|
||||||
import org.pgpainless.implementation.ImplementationFactory
|
import org.pgpainless.implementation.ImplementationFactory
|
||||||
|
import org.pgpainless.key.OpenPgpFingerprint
|
||||||
import org.pgpainless.key.generation.DefinePrimaryKey.PrimaryKeyBuilder
|
import org.pgpainless.key.generation.DefinePrimaryKey.PrimaryKeyBuilder
|
||||||
import org.pgpainless.key.generation.DefineSubkeys.SubkeyBuilder
|
import org.pgpainless.key.generation.DefineSubkeys.SubkeyBuilder
|
||||||
import org.pgpainless.key.generation.OpenPgpKeyTemplates.Companion.v4
|
import org.pgpainless.key.generation.OpenPgpKeyTemplates.Companion.v4
|
||||||
|
@ -99,6 +100,9 @@ internal constructor(val policy: Policy, val creationTime: Date, val preferences
|
||||||
// signatures with the intended key flags.
|
// signatures with the intended key flags.
|
||||||
protected var keyFlags: List<KeyFlag>? = null
|
protected var keyFlags: List<KeyFlag>? = null
|
||||||
|
|
||||||
|
// Dedicated protector for the primary key.
|
||||||
|
protected var primaryKeyProtector: SecretKeyRingProtector? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback to set preferences on the key and user-ids, such as algorithm preferences, features
|
* Callback to set preferences on the key and user-ids, such as algorithm preferences, features
|
||||||
* etc. This callback will be used to modify direct-key signatures and bindings for user-ids.
|
* etc. This callback will be used to modify direct-key signatures and bindings for user-ids.
|
||||||
|
@ -398,6 +402,25 @@ internal constructor(val policy: Policy, val creationTime: Date, val preferences
|
||||||
fun skipDefaultSignature() {
|
fun skipDefaultSignature() {
|
||||||
builder.skipDefaultDirectKeySignature = true
|
builder.skipDefaultDirectKeySignature = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a dedicated [Passphrase] for the primary key. This is useful, if each (sub-) key of
|
||||||
|
* this OpenPGP key is intended to use a different passphrase.
|
||||||
|
*
|
||||||
|
* @param passphrase passphrase to protect the primary key with
|
||||||
|
*/
|
||||||
|
fun setPrimaryKeyPassphrase(passphrase: Passphrase) =
|
||||||
|
setPrimaryKeyProtector(SecretKeyRingProtector.unlockAnyKeyWith(passphrase))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a dedicated [SecretKeyRingProtector] for the primary key. This is useful, if each
|
||||||
|
* (sub-) key of this OpenPGP key is intended to use a different passphrase.
|
||||||
|
*
|
||||||
|
* @param protector protector to protect the primary key with
|
||||||
|
*/
|
||||||
|
fun setPrimaryKeyProtector(protector: SecretKeyRingProtector) {
|
||||||
|
builder.primaryKeyProtector = protector
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,14 +435,19 @@ internal constructor(val policy: Policy, val creationTime: Date, val preferences
|
||||||
*/
|
*/
|
||||||
abstract class DefineSubkeys<B : DefineSubkeys<B>>
|
abstract class DefineSubkeys<B : DefineSubkeys<B>>
|
||||||
internal constructor(
|
internal constructor(
|
||||||
internal var primaryKey: PGPKeyPair,
|
internal val primaryKey: PGPKeyPair,
|
||||||
|
internal val primaryKeyProtector: SecretKeyRingProtector?,
|
||||||
internal val policy: Policy,
|
internal val policy: Policy,
|
||||||
internal val creationTime: Date,
|
internal val creationTime: Date,
|
||||||
internal val subkeys: MutableList<PGPKeyPair> = mutableListOf()
|
internal val subkeys: MutableList<PGPKeyPair> = mutableListOf(),
|
||||||
|
internal val subkeyProtectors: MutableMap<OpenPgpFingerprint, SecretKeyRingProtector> =
|
||||||
|
mutableMapOf(),
|
||||||
|
internal val skipDefaultBindingSignatureFor: MutableList<OpenPgpFingerprint> = mutableListOf()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a subkey to the OpenPGP key.
|
* Add a subkey to the OpenPGP key. If no explicit binding signature is set inside [block], the
|
||||||
|
* key will be bound using a default binding signature containing the given [flags].
|
||||||
*
|
*
|
||||||
* @param type subkey type
|
* @param type subkey type
|
||||||
* @param creationTime creation time of the subkey
|
* @param creationTime creation time of the subkey
|
||||||
|
@ -439,14 +467,23 @@ internal constructor(
|
||||||
sanitizeSubkeyCreationTime(creationTime, primaryKey)
|
sanitizeSubkeyCreationTime(creationTime, primaryKey)
|
||||||
|
|
||||||
var subkey = generateSubkey(type, creationTime)
|
var subkey = generateSubkey(type, creationTime)
|
||||||
val subkeyBlock =
|
|
||||||
block
|
// Default function block will only set appropriate key flags
|
||||||
?: {
|
val defaultBlock: SubkeyBlock = {
|
||||||
addBindingSignature(
|
addBindingSignature(
|
||||||
SelfSignatureSubpackets.applyHashed { flags?.let { setKeyFlags(it) } },
|
SelfSignatureSubpackets.applyHashed { flags?.let { setKeyFlags(it) } },
|
||||||
bindingTime = creationTime)
|
bindingTime = creationTime)
|
||||||
}
|
}
|
||||||
|
// if no custom function block is given, simply set key flags
|
||||||
|
val subkeyBlock = block ?: defaultBlock
|
||||||
|
|
||||||
subkey = invokeOnSubkey(subkey, subkeyBlock)
|
subkey = invokeOnSubkey(subkey, subkeyBlock)
|
||||||
|
|
||||||
|
// If no binding signature was added yet, add a default binding sig using the default
|
||||||
|
// block
|
||||||
|
if (!skipDefaultBindingSignatureFor.contains(OpenPgpFingerprint.of(subkey))) {
|
||||||
|
subkey = invokeOnSubkey(subkey, defaultBlock)
|
||||||
|
}
|
||||||
subkeys.add(subkey)
|
subkeys.add(subkey)
|
||||||
}
|
}
|
||||||
as B
|
as B
|
||||||
|
@ -472,7 +509,9 @@ internal constructor(
|
||||||
): PGPKeyPair
|
): PGPKeyPair
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finish the key generation and return the OpenPGP [PGPSecretKeyRing].
|
* Finish the key generation and return the OpenPGP [PGPSecretKeyRing]. The [protector] is used
|
||||||
|
* as a catch-all to protect any keys where the user did not specify protection explicitly
|
||||||
|
* otherwise.
|
||||||
*
|
*
|
||||||
* @param protector protector to protect the OpenPGP key's secret components with
|
* @param protector protector to protect the OpenPGP key's secret components with
|
||||||
* @return finished [PGPSecretKeyRing]
|
* @return finished [PGPSecretKeyRing]
|
||||||
|
@ -481,7 +520,8 @@ internal constructor(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finish the key generation and return the OpenPGP [PGPSecretKeyRing] protected with the given
|
* Finish the key generation and return the OpenPGP [PGPSecretKeyRing] protected with the given
|
||||||
* [passphrase].
|
* [passphrase]. The [passphrase] is used as a catch-all to protect any keys where the user did
|
||||||
|
* not specify protection explicitly otherwise.
|
||||||
*
|
*
|
||||||
* @param passphrase passphrase to protect the OpenPGP key's secret components with
|
* @param passphrase passphrase to protect the OpenPGP key's secret components with
|
||||||
* @return finished [PGPSecretKeyRing]
|
* @return finished [PGPSecretKeyRing]
|
||||||
|
@ -543,6 +583,10 @@ internal constructor(
|
||||||
/**
|
/**
|
||||||
* Add a binding signature to the subkey.
|
* Add a binding signature to the subkey.
|
||||||
*
|
*
|
||||||
|
* If this method is not explicitly called at least once while adding a subkey, the subkey
|
||||||
|
* will be bound using a default binding signature. To prevent adding this default
|
||||||
|
* signature, call [skipDefaultBindingSignatureFor].
|
||||||
|
*
|
||||||
* @param subpacketsCallback callback to modify the binding signatures subpackets
|
* @param subpacketsCallback callback to modify the binding signatures subpackets
|
||||||
* @param hashAlgorithm hash algorithm to be used during signature calculation
|
* @param hashAlgorithm hash algorithm to be used during signature calculation
|
||||||
* @param bindingTime creation time of the binding signature
|
* @param bindingTime creation time of the binding signature
|
||||||
|
@ -559,6 +603,16 @@ internal constructor(
|
||||||
builder.sanitizeBindingTime(bindingTime, subkey)
|
builder.sanitizeBindingTime(bindingTime, subkey)
|
||||||
|
|
||||||
doAddBindingSignature(subpacketsCallback, hashAlgorithm, bindingTime)
|
doAddBindingSignature(subpacketsCallback, hashAlgorithm, bindingTime)
|
||||||
|
skipDefaultBindingSignature()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not bind the key using a default binding signature, even if the user did not add an
|
||||||
|
* explicit binding signature. This method is useful mostly for testing to generate keys
|
||||||
|
* with unbound subkeys.
|
||||||
|
*/
|
||||||
|
fun skipDefaultBindingSignature() {
|
||||||
|
builder.skipDefaultBindingSignatureFor.add(OpenPgpFingerprint.of(subkey))
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun doAddBindingSignature(
|
abstract fun doAddBindingSignature(
|
||||||
|
@ -566,6 +620,13 @@ internal constructor(
|
||||||
hashAlgorithm: HashAlgorithm,
|
hashAlgorithm: HashAlgorithm,
|
||||||
bindingTime: Date
|
bindingTime: Date
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun setSubkeyPassphrase(passphrase: Passphrase) =
|
||||||
|
setSubkeyProtector(SecretKeyRingProtector.unlockAnyKeyWith(passphrase))
|
||||||
|
|
||||||
|
fun setSubkeyProtector(protector: SecretKeyRingProtector) {
|
||||||
|
builder.subkeyProtectors[OpenPgpFingerprint.of(subkey)] = protector
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,10 +707,13 @@ internal constructor(primaryKey: PGPKeyPair, subkey: PGPKeyPair, builder: Define
|
||||||
*/
|
*/
|
||||||
abstract class DefineSubkeysV4<O : DefineSubkeys<O>>(
|
abstract class DefineSubkeysV4<O : DefineSubkeys<O>>(
|
||||||
primaryKey: PGPKeyPair,
|
primaryKey: PGPKeyPair,
|
||||||
|
primaryKeyProtector: SecretKeyRingProtector?,
|
||||||
policy: Policy,
|
policy: Policy,
|
||||||
creationTime: Date,
|
creationTime: Date,
|
||||||
subkeys: List<PGPKeyPair>
|
subkeys: List<PGPKeyPair>
|
||||||
) : DefineSubkeys<O>(primaryKey, policy, creationTime, subkeys.toMutableList()) {
|
) :
|
||||||
|
DefineSubkeys<O>(
|
||||||
|
primaryKey, primaryKeyProtector, policy, creationTime, subkeys.toMutableList()) {
|
||||||
|
|
||||||
override fun generateSubkey(type: KeyType, creationTime: Date): PGPKeyPair {
|
override fun generateSubkey(type: KeyType, creationTime: Date): PGPKeyPair {
|
||||||
return OpenPgpKeyPairGenerator.V4().generateSubkey(type, creationTime)
|
return OpenPgpKeyPairGenerator.V4().generateSubkey(type, creationTime)
|
||||||
|
@ -674,7 +738,7 @@ internal constructor(primaryKey: PGPKeyPair, subkey: PGPKeyPair, builder: Define
|
||||||
primaryKey.publicKey,
|
primaryKey.publicKey,
|
||||||
ImplementationFactory.getInstance().v4FingerprintCalculator,
|
ImplementationFactory.getInstance().v4FingerprintCalculator,
|
||||||
true,
|
true,
|
||||||
protector.getEncryptor(primaryKey.keyID)))
|
(primaryKeyProtector ?: protector).getEncryptor(primaryKey.keyID)))
|
||||||
|
|
||||||
// Subkeys
|
// Subkeys
|
||||||
subkeys.forEach {
|
subkeys.forEach {
|
||||||
|
@ -684,7 +748,9 @@ internal constructor(primaryKey: PGPKeyPair, subkey: PGPKeyPair, builder: Define
|
||||||
it.publicKey,
|
it.publicKey,
|
||||||
ImplementationFactory.getInstance().v4FingerprintCalculator,
|
ImplementationFactory.getInstance().v4FingerprintCalculator,
|
||||||
false,
|
false,
|
||||||
protector.getEncryptor(it.keyID)))
|
subkeyProtectors
|
||||||
|
.getOrDefault(OpenPgpFingerprint.of(it), protector)
|
||||||
|
.getEncryptor(it.keyID)))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -782,7 +848,7 @@ internal constructor(policy: Policy, creationTime: Date, preferences: AlgorithmS
|
||||||
invokeOnPrimaryKey(primaryKey) { addDirectKeySignature(preferencesSubpackets()) }
|
invokeOnPrimaryKey(primaryKey) { addDirectKeySignature(preferencesSubpackets()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return OpinionatedDefineSubkeysV4(primaryKey, policy, creationTime)
|
return OpinionatedDefineSubkeysV4(primaryKey, primaryKeyProtector, policy, creationTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sanitizeHashAlgorithm(algorithm: HashAlgorithm) {
|
override fun sanitizeHashAlgorithm(algorithm: HashAlgorithm) {
|
||||||
|
@ -866,7 +932,7 @@ internal constructor(policy: Policy, creationTime: Date, preferences: AlgorithmS
|
||||||
primaryKey = invokeOnPrimaryKey(primaryKey, block)
|
primaryKey = invokeOnPrimaryKey(primaryKey, block)
|
||||||
|
|
||||||
// return builder for adding subkeys
|
// return builder for adding subkeys
|
||||||
return UnopinionatedDefineSubkeysV4(primaryKey, policy, creationTime)
|
return UnopinionatedDefineSubkeysV4(primaryKey, primaryKeyProtector, policy, creationTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -878,9 +944,14 @@ internal constructor(policy: Policy, creationTime: Date, preferences: AlgorithmS
|
||||||
* @param creationTime creation time of the OpenPGP key
|
* @param creationTime creation time of the OpenPGP key
|
||||||
*/
|
*/
|
||||||
class OpinionatedDefineSubkeysV4
|
class OpinionatedDefineSubkeysV4
|
||||||
internal constructor(primaryKey: PGPKeyPair, policy: Policy, creationTime: Date) :
|
internal constructor(
|
||||||
|
primaryKey: PGPKeyPair,
|
||||||
|
primaryKeyProtector: SecretKeyRingProtector?,
|
||||||
|
policy: Policy,
|
||||||
|
creationTime: Date
|
||||||
|
) :
|
||||||
SubkeyBuilderV4.DefineSubkeysV4<OpinionatedDefineSubkeysV4>(
|
SubkeyBuilderV4.DefineSubkeysV4<OpinionatedDefineSubkeysV4>(
|
||||||
primaryKey, policy, creationTime, listOf()) {
|
primaryKey, primaryKeyProtector, policy, creationTime, listOf()) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an unopinionated implementation of this builder.
|
* Return an unopinionated implementation of this builder.
|
||||||
|
@ -890,7 +961,9 @@ internal constructor(primaryKey: PGPKeyPair, policy: Policy, creationTime: Date)
|
||||||
fun unopinionated() = UnopinionatedDefineSubkeysV4(this)
|
fun unopinionated() = UnopinionatedDefineSubkeysV4(this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a subkey for signing messages to the OpenPGP key.
|
* Add a subkey for signing messages to the OpenPGP key. If no explicit binding signature is set
|
||||||
|
* inside [block], the key will be bound using a default binding signature marking the key as
|
||||||
|
* signing capable.
|
||||||
*
|
*
|
||||||
* @param type signing key type
|
* @param type signing key type
|
||||||
* @param creationTime creation time of the signing subkey
|
* @param creationTime creation time of the signing subkey
|
||||||
|
@ -906,7 +979,9 @@ internal constructor(primaryKey: PGPKeyPair, policy: Policy, creationTime: Date)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a subkey for signing messages to the OpenPGP key.
|
* Add a subkey for signing messages to the OpenPGP key. If no explicit binding signature is set
|
||||||
|
* inside [block], the key will be bound using a default binding signature marking the key as
|
||||||
|
* signing capable.
|
||||||
*
|
*
|
||||||
* @param type signing key type
|
* @param type signing key type
|
||||||
* @param block function block to add binding signatures to the subkey
|
* @param block function block to add binding signatures to the subkey
|
||||||
|
@ -915,7 +990,9 @@ internal constructor(primaryKey: PGPKeyPair, policy: Policy, creationTime: Date)
|
||||||
addSigningSubkey(type, this.creationTime, block)
|
addSigningSubkey(type, this.creationTime, block)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a subkey for message encryption to the OpenPGP key.
|
* Add a subkey for message encryption to the OpenPGP key. If no explicit binding signature is
|
||||||
|
* set inside [block], the key will be bound using a default binding signature marking the key
|
||||||
|
* as encryption capable.
|
||||||
*
|
*
|
||||||
* @param type encryption key type
|
* @param type encryption key type
|
||||||
* @param creationTime creation time of the encryption key
|
* @param creationTime creation time of the encryption key
|
||||||
|
@ -932,7 +1009,9 @@ internal constructor(primaryKey: PGPKeyPair, policy: Policy, creationTime: Date)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a subkey for message encryption to the OpenPGP key.
|
* Add a subkey for message encryption to the OpenPGP key. If no explicit binding signature is
|
||||||
|
* set inside [block], the key will be bound using a default binding signature marking the key
|
||||||
|
* as encryption capable.
|
||||||
*
|
*
|
||||||
* @param type encryption key type
|
* @param type encryption key type
|
||||||
* @param block function block to add binding signatures to the subkey
|
* @param block function block to add binding signatures to the subkey
|
||||||
|
@ -996,12 +1075,13 @@ internal constructor(primaryKey: PGPKeyPair, policy: Policy, creationTime: Date)
|
||||||
class UnopinionatedDefineSubkeysV4
|
class UnopinionatedDefineSubkeysV4
|
||||||
internal constructor(
|
internal constructor(
|
||||||
primaryKey: PGPKeyPair,
|
primaryKey: PGPKeyPair,
|
||||||
|
primaryKeyProtector: SecretKeyRingProtector?,
|
||||||
policy: Policy,
|
policy: Policy,
|
||||||
creationTime: Date,
|
creationTime: Date,
|
||||||
subkeys: List<PGPKeyPair> = mutableListOf()
|
subkeys: List<PGPKeyPair> = mutableListOf()
|
||||||
) :
|
) :
|
||||||
SubkeyBuilderV4.DefineSubkeysV4<UnopinionatedDefineSubkeysV4>(
|
SubkeyBuilderV4.DefineSubkeysV4<UnopinionatedDefineSubkeysV4>(
|
||||||
primaryKey, policy, creationTime, subkeys) {
|
primaryKey, primaryKeyProtector, policy, creationTime, subkeys) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor to build an unopinionated variant of the given [OpinionatedDefineSubkeysV4].
|
* Constructor to build an unopinionated variant of the given [OpinionatedDefineSubkeysV4].
|
||||||
|
@ -1011,7 +1091,11 @@ internal constructor(
|
||||||
internal constructor(
|
internal constructor(
|
||||||
opinionated: OpinionatedDefineSubkeysV4
|
opinionated: OpinionatedDefineSubkeysV4
|
||||||
) : this(
|
) : this(
|
||||||
opinionated.primaryKey, opinionated.policy, opinionated.creationTime, opinionated.subkeys)
|
opinionated.primaryKey,
|
||||||
|
opinionated.primaryKeyProtector,
|
||||||
|
opinionated.policy,
|
||||||
|
opinionated.creationTime,
|
||||||
|
opinionated.subkeys)
|
||||||
|
|
||||||
override fun generateSubkey(type: KeyType, creationTime: Date): PGPKeyPair {
|
override fun generateSubkey(type: KeyType, creationTime: Date): PGPKeyPair {
|
||||||
return OpenPgpKeyPairGenerator.V4().generateSubkey(type, creationTime)
|
return OpenPgpKeyPairGenerator.V4().generateSubkey(type, creationTime)
|
||||||
|
|
|
@ -17,6 +17,7 @@ import org.junit.jupiter.api.Assertions.assertNull
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
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.junit.jupiter.api.fail
|
||||||
import org.pgpainless.PGPainless
|
import org.pgpainless.PGPainless
|
||||||
import org.pgpainless.algorithm.HashAlgorithm
|
import org.pgpainless.algorithm.HashAlgorithm
|
||||||
import org.pgpainless.algorithm.KeyFlag
|
import org.pgpainless.algorithm.KeyFlag
|
||||||
|
@ -28,10 +29,13 @@ 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.rsa.RsaLength
|
import org.pgpainless.key.generation.type.rsa.RsaLength
|
||||||
import org.pgpainless.key.generation.type.xdh.XDHSpec
|
import org.pgpainless.key.generation.type.xdh.XDHSpec
|
||||||
|
import org.pgpainless.key.protection.SecretKeyRingProtector
|
||||||
|
import org.pgpainless.key.protection.UnlockSecretKey
|
||||||
import org.pgpainless.policy.Policy
|
import org.pgpainless.policy.Policy
|
||||||
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets
|
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets
|
||||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil
|
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil
|
||||||
import org.pgpainless.util.DateUtil
|
import org.pgpainless.util.DateUtil
|
||||||
|
import org.pgpainless.util.Passphrase
|
||||||
|
|
||||||
class OpenPgpKeyGeneratorTest {
|
class OpenPgpKeyGeneratorTest {
|
||||||
|
|
||||||
|
@ -664,4 +668,130 @@ class OpenPgpKeyGeneratorTest {
|
||||||
.build()
|
.build()
|
||||||
.let { println(it.toAsciiArmor()) }
|
.let { println(it.toAsciiArmor()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate key with unbound subkey`() {
|
||||||
|
val policy = Policy()
|
||||||
|
|
||||||
|
val key =
|
||||||
|
PGPainless.generateOpenPgpKey(policy)
|
||||||
|
.buildV4Key()
|
||||||
|
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||||
|
.addSigningSubkey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
|
||||||
|
skipDefaultBindingSignature()
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val subkey = key.secretKeys.asSequence().last()
|
||||||
|
assertFalse(PGPainless.inspectKeyRing(key).isKeyValidlyBound(subkey.keyID))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate key with a single passphrase for all subkeys`() {
|
||||||
|
val policy = Policy()
|
||||||
|
|
||||||
|
val key =
|
||||||
|
PGPainless.generateOpenPgpKey(policy)
|
||||||
|
.buildV4Key()
|
||||||
|
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||||
|
.addSigningSubkey(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||||
|
.addEncryptionSubkey(KeyType.XDH(XDHSpec._X25519))
|
||||||
|
.build(Passphrase.fromPassword("sw0rdf1sh"))
|
||||||
|
|
||||||
|
assertTrue(PGPainless.inspectKeyRing(key).isFullyEncrypted)
|
||||||
|
|
||||||
|
for (subkey in key.secretKeys) {
|
||||||
|
UnlockSecretKey.unlockSecretKey(subkey, Passphrase.fromPassword("sw0rdf1sh"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate key with dedicated passphrases for all subkeys`() {
|
||||||
|
val policy = Policy()
|
||||||
|
|
||||||
|
val key =
|
||||||
|
PGPainless.generateOpenPgpKey(policy)
|
||||||
|
.buildV4Key()
|
||||||
|
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
|
||||||
|
setPrimaryKeyPassphrase(Passphrase.fromPassword("MyColorIsRed"))
|
||||||
|
}
|
||||||
|
.addSigningSubkey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
|
||||||
|
setSubkeyPassphrase(Passphrase.fromPassword("MyColorIsGreen"))
|
||||||
|
}
|
||||||
|
.addEncryptionSubkey(KeyType.XDH(XDHSpec._X25519)) {
|
||||||
|
setSubkeyPassphrase(Passphrase.fromPassword("MyColorIsBlue"))
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
|
assertTrue(PGPainless.inspectKeyRing(key).isFullyEncrypted)
|
||||||
|
|
||||||
|
key.secretKeys.asSequence().forEachIndexed { index, subkey ->
|
||||||
|
UnlockSecretKey.unlockSecretKey(
|
||||||
|
subkey,
|
||||||
|
when (index) {
|
||||||
|
0 -> Passphrase.fromPassword("MyColorIsRed")
|
||||||
|
1 -> Passphrase.fromPassword("MyColorIsGreen")
|
||||||
|
2 -> Passphrase.fromPassword("MyColorIsBlue")
|
||||||
|
else -> fail { "Unexpected secret key at index $index" }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate key with dedicated subkey passphrase and catch-all primary key protection`() {
|
||||||
|
val policy = Policy()
|
||||||
|
|
||||||
|
val key =
|
||||||
|
PGPainless.generateOpenPgpKey(policy)
|
||||||
|
.buildV4Key()
|
||||||
|
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||||
|
.addSigningSubkey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
|
||||||
|
setSubkeyPassphrase(Passphrase.fromPassword("Yin"))
|
||||||
|
}
|
||||||
|
.build(Passphrase.fromPassword("Yang"))
|
||||||
|
|
||||||
|
assertTrue(PGPainless.inspectKeyRing(key).isFullyEncrypted)
|
||||||
|
|
||||||
|
key.secretKeys.asSequence().forEachIndexed { index, subkey ->
|
||||||
|
UnlockSecretKey.unlockSecretKey(
|
||||||
|
subkey,
|
||||||
|
when (index) {
|
||||||
|
0 -> Passphrase.fromPassword("Yang")
|
||||||
|
1 -> Passphrase.fromPassword("Yin")
|
||||||
|
else -> fail { "Unexpected secret key at index $index" }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate key with unprotected subkey and other keys protected through catch-all`() {
|
||||||
|
val policy = Policy()
|
||||||
|
|
||||||
|
val key =
|
||||||
|
PGPainless.generateOpenPgpKey(policy)
|
||||||
|
.buildV4Key()
|
||||||
|
.setPrimaryKey(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||||
|
.addSigningSubkey(KeyType.EDDSA(EdDSACurve._Ed25519)) {
|
||||||
|
// Only the signing subkey is unprotected
|
||||||
|
setSubkeyProtector(SecretKeyRingProtector.unprotectedKeys())
|
||||||
|
}
|
||||||
|
.addEncryptionSubkey(KeyType.XDH(XDHSpec._X25519))
|
||||||
|
// All other keys are protected
|
||||||
|
.build(Passphrase.fromPassword("sw0rdf1sh"))
|
||||||
|
|
||||||
|
val info = PGPainless.inspectKeyRing(key)
|
||||||
|
assertFalse(info.isFullyEncrypted)
|
||||||
|
assertFalse(info.isFullyDecrypted)
|
||||||
|
|
||||||
|
key.secretKeys.asSequence().forEachIndexed { index, subkey ->
|
||||||
|
UnlockSecretKey.unlockSecretKey(
|
||||||
|
subkey,
|
||||||
|
when (index) {
|
||||||
|
0,
|
||||||
|
2 -> Passphrase.fromPassword("sw0rdf1sh")
|
||||||
|
1 -> Passphrase.emptyPassphrase()
|
||||||
|
else -> fail { "Unexpected subkey at index $index" }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue