Per-key protection and default binding signatures
This commit is contained in:
parent
b4240ac9f7
commit
f1aa910431
|
@ -5,6 +5,7 @@
|
|||
package org.pgpainless.key
|
||||
|
||||
import java.nio.charset.Charset
|
||||
import org.bouncycastle.openpgp.PGPKeyPair
|
||||
import org.bouncycastle.openpgp.PGPKeyRing
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
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: PGPKeyPair): OpenPgpFingerprint = of(key.publicKey)
|
||||
|
||||
@JvmStatic
|
||||
fun of(keyVersion: Int, binaryFingerprint: ByteArray): OpenPgpFingerprint =
|
||||
when (keyVersion) {
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.pgpainless.algorithm.KeyFlag
|
|||
import org.pgpainless.algorithm.PublicKeyAlgorithm
|
||||
import org.pgpainless.bouncycastle.extensions.plusCertification
|
||||
import org.pgpainless.implementation.ImplementationFactory
|
||||
import org.pgpainless.key.OpenPgpFingerprint
|
||||
import org.pgpainless.key.generation.DefinePrimaryKey.PrimaryKeyBuilder
|
||||
import org.pgpainless.key.generation.DefineSubkeys.SubkeyBuilder
|
||||
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.
|
||||
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
|
||||
* 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() {
|
||||
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>>
|
||||
internal constructor(
|
||||
internal var primaryKey: PGPKeyPair,
|
||||
internal val primaryKey: PGPKeyPair,
|
||||
internal val primaryKeyProtector: SecretKeyRingProtector?,
|
||||
internal val policy: Policy,
|
||||
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 creationTime creation time of the subkey
|
||||
|
@ -439,14 +467,23 @@ internal constructor(
|
|||
sanitizeSubkeyCreationTime(creationTime, primaryKey)
|
||||
|
||||
var subkey = generateSubkey(type, creationTime)
|
||||
val subkeyBlock =
|
||||
block
|
||||
?: {
|
||||
addBindingSignature(
|
||||
SelfSignatureSubpackets.applyHashed { flags?.let { setKeyFlags(it) } },
|
||||
bindingTime = creationTime)
|
||||
}
|
||||
|
||||
// Default function block will only set appropriate key flags
|
||||
val defaultBlock: SubkeyBlock = {
|
||||
addBindingSignature(
|
||||
SelfSignatureSubpackets.applyHashed { flags?.let { setKeyFlags(it) } },
|
||||
bindingTime = creationTime)
|
||||
}
|
||||
// if no custom function block is given, simply set key flags
|
||||
val subkeyBlock = block ?: defaultBlock
|
||||
|
||||
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)
|
||||
}
|
||||
as B
|
||||
|
@ -472,7 +509,9 @@ internal constructor(
|
|||
): 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
|
||||
* @return finished [PGPSecretKeyRing]
|
||||
|
@ -481,7 +520,8 @@ internal constructor(
|
|||
|
||||
/**
|
||||
* 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
|
||||
* @return finished [PGPSecretKeyRing]
|
||||
|
@ -543,6 +583,10 @@ internal constructor(
|
|||
/**
|
||||
* 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 hashAlgorithm hash algorithm to be used during signature calculation
|
||||
* @param bindingTime creation time of the binding signature
|
||||
|
@ -559,6 +603,16 @@ internal constructor(
|
|||
builder.sanitizeBindingTime(bindingTime, subkey)
|
||||
|
||||
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(
|
||||
|
@ -566,6 +620,13 @@ internal constructor(
|
|||
hashAlgorithm: HashAlgorithm,
|
||||
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>>(
|
||||
primaryKey: PGPKeyPair,
|
||||
primaryKeyProtector: SecretKeyRingProtector?,
|
||||
policy: Policy,
|
||||
creationTime: Date,
|
||||
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 {
|
||||
return OpenPgpKeyPairGenerator.V4().generateSubkey(type, creationTime)
|
||||
|
@ -674,7 +738,7 @@ internal constructor(primaryKey: PGPKeyPair, subkey: PGPKeyPair, builder: Define
|
|||
primaryKey.publicKey,
|
||||
ImplementationFactory.getInstance().v4FingerprintCalculator,
|
||||
true,
|
||||
protector.getEncryptor(primaryKey.keyID)))
|
||||
(primaryKeyProtector ?: protector).getEncryptor(primaryKey.keyID)))
|
||||
|
||||
// Subkeys
|
||||
subkeys.forEach {
|
||||
|
@ -684,7 +748,9 @@ internal constructor(primaryKey: PGPKeyPair, subkey: PGPKeyPair, builder: Define
|
|||
it.publicKey,
|
||||
ImplementationFactory.getInstance().v4FingerprintCalculator,
|
||||
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()) }
|
||||
}
|
||||
|
||||
return OpinionatedDefineSubkeysV4(primaryKey, policy, creationTime)
|
||||
return OpinionatedDefineSubkeysV4(primaryKey, primaryKeyProtector, policy, creationTime)
|
||||
}
|
||||
|
||||
override fun sanitizeHashAlgorithm(algorithm: HashAlgorithm) {
|
||||
|
@ -866,7 +932,7 @@ internal constructor(policy: Policy, creationTime: Date, preferences: AlgorithmS
|
|||
primaryKey = invokeOnPrimaryKey(primaryKey, block)
|
||||
|
||||
// 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
|
||||
*/
|
||||
class OpinionatedDefineSubkeysV4
|
||||
internal constructor(primaryKey: PGPKeyPair, policy: Policy, creationTime: Date) :
|
||||
internal constructor(
|
||||
primaryKey: PGPKeyPair,
|
||||
primaryKeyProtector: SecretKeyRingProtector?,
|
||||
policy: Policy,
|
||||
creationTime: Date
|
||||
) :
|
||||
SubkeyBuilderV4.DefineSubkeysV4<OpinionatedDefineSubkeysV4>(
|
||||
primaryKey, policy, creationTime, listOf()) {
|
||||
primaryKey, primaryKeyProtector, policy, creationTime, listOf()) {
|
||||
|
||||
/**
|
||||
* Return an unopinionated implementation of this builder.
|
||||
|
@ -890,7 +961,9 @@ internal constructor(primaryKey: PGPKeyPair, policy: Policy, creationTime: Date)
|
|||
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 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 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)
|
||||
|
||||
/**
|
||||
* 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 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 block function block to add binding signatures to the subkey
|
||||
|
@ -996,12 +1075,13 @@ internal constructor(primaryKey: PGPKeyPair, policy: Policy, creationTime: Date)
|
|||
class UnopinionatedDefineSubkeysV4
|
||||
internal constructor(
|
||||
primaryKey: PGPKeyPair,
|
||||
primaryKeyProtector: SecretKeyRingProtector?,
|
||||
policy: Policy,
|
||||
creationTime: Date,
|
||||
subkeys: List<PGPKeyPair> = mutableListOf()
|
||||
) :
|
||||
SubkeyBuilderV4.DefineSubkeysV4<UnopinionatedDefineSubkeysV4>(
|
||||
primaryKey, policy, creationTime, subkeys) {
|
||||
primaryKey, primaryKeyProtector, policy, creationTime, subkeys) {
|
||||
|
||||
/**
|
||||
* Constructor to build an unopinionated variant of the given [OpinionatedDefineSubkeysV4].
|
||||
|
@ -1011,7 +1091,11 @@ internal constructor(
|
|||
internal constructor(
|
||||
opinionated: OpinionatedDefineSubkeysV4
|
||||
) : 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 {
|
||||
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.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.junit.jupiter.api.fail
|
||||
import org.pgpainless.PGPainless
|
||||
import org.pgpainless.algorithm.HashAlgorithm
|
||||
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.rsa.RsaLength
|
||||
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.signature.subpackets.SelfSignatureSubpackets
|
||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil
|
||||
import org.pgpainless.util.DateUtil
|
||||
import org.pgpainless.util.Passphrase
|
||||
|
||||
class OpenPgpKeyGeneratorTest {
|
||||
|
||||
|
@ -664,4 +668,130 @@ class OpenPgpKeyGeneratorTest {
|
|||
.build()
|
||||
.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