Per-key protection and default binding signatures

This commit is contained in:
Paul Schaub 2024-02-29 16:16:16 +01:00
parent b4240ac9f7
commit f1aa910431
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
3 changed files with 242 additions and 25 deletions

View File

@ -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) {

View File

@ -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)

View File

@ -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" }
})
}
}
} }