diff --git a/src/commonMain/kotlin/net/sergeych/crypto2/Multikey.kt b/src/commonMain/kotlin/net/sergeych/crypto2/Multikey.kt index 7e1aca1..65906c1 100644 --- a/src/commonMain/kotlin/net/sergeych/crypto2/Multikey.kt +++ b/src/commonMain/kotlin/net/sergeych/crypto2/Multikey.kt @@ -3,6 +3,7 @@ package net.sergeych.crypto2 import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.sergeych.bipack.Unsigned +import net.sergeych.crypto2.Multikey.AnyKey import net.sergeych.crypto2.Multikey.Companion.allOf import net.sergeych.crypto2.Multikey.Companion.allOfMultikeys import net.sergeych.crypto2.Multikey.Companion.anyOf @@ -36,6 +37,7 @@ import net.sergeych.crypto2.Multikey.Companion.someOfMultikeys * - [someOfMultikeys], [someOf] family for `n of M` logic * - [anyOfMultikeys], [anyOf], [allOf], and [allOfMultikeys] * - [invoke] for a single-key multikey + * - [AnyKey] when you need effectively match any key, useful when you need a `var` `Multikey`. * * __Important__. When serializing, always serialize as root [Multikey] instance to keep * it compatible with any combination. @@ -53,17 +55,17 @@ sealed class Multikey { */ fun check(vararg verifyingKeys: VerifyingPublicKey): Boolean = check(verifyingKeys.asIterable()) - infix fun or(mk: Multikey): Multikey = SomeOf(1, listOf(this,mk)) + infix fun or(mk: Multikey): Multikey = SomeOf(1, listOf(this, mk)) - infix fun or(k: VerifyingPublicKey) = SomeOf( 1, listOf(this, Multikey(k))) + infix fun or(k: VerifyingPublicKey) = SomeOf(1, listOf(this, Multikey(k))) - infix fun or(k: SigningSecretKey) = SomeOf( 1, listOf(this, Multikey(k.verifyingKey))) + infix fun or(k: SigningSecretKey) = SomeOf(1, listOf(this, Multikey(k.verifyingKey))) - infix fun and(mk: Multikey): Multikey = SomeOf(2, listOf(this,mk)) + infix fun and(mk: Multikey): Multikey = SomeOf(2, listOf(this, mk)) - infix fun and(k: VerifyingPublicKey) = SomeOf( 2, listOf(this, Multikey(k))) + infix fun and(k: VerifyingPublicKey) = SomeOf(2, listOf(this, Multikey(k))) - infix fun and(k: SigningSecretKey) = SomeOf( 2, listOf(this, Multikey(k.verifyingKey))) + infix fun and(k: SigningSecretKey) = SomeOf(2, listOf(this, Multikey(k.verifyingKey))) /** * Multikey instance implementing `m of N` logic against [VerifyingPublicKey] set. Do not use @@ -74,13 +76,13 @@ sealed class Multikey { class Keys internal constructor( @Unsigned val requiredMinimum: Int, - val validKeys: Set + val validKeys: Set, ) : Multikey() { override fun check(keys: Iterable): Boolean { var matches = 0 - for( signer in keys ) { - if( signer in validKeys) { - if( ++matches >= requiredMinimum ) return true + for (signer in keys) { + if (signer in validKeys) { + if (++matches >= requiredMinimum) return true } } return false @@ -96,24 +98,34 @@ sealed class Multikey { class SomeOf internal constructor( @Unsigned val requiredMinimum: Int, - val validKeys: List + val validKeys: List, ) : Multikey() { override fun check(keys: Iterable): Boolean { var matches = 0 - for( k in validKeys ) { - if( k.check(keys) ) { - if( ++matches >= requiredMinimum ) return true + for (k in validKeys) { + if (k.check(keys)) { + if (++matches >= requiredMinimum) return true } } return false } } + /** + * Special `AnyKey`: no restrictions, any key will satisfy this. In the rare case to mark + * publicly available operations, etc. Please note it is an object, not a class, and can't + * be instantiated. + */ + @Serializable + object AnyKey : Multikey() { + override fun check(keys: Iterable): Boolean = true + } + companion object { - operator fun invoke(k: SigningSecretKey): Multikey = Keys(1, setOf( k.verifyingKey)) - operator fun invoke(k: VerifyingPublicKey): Multikey = Keys(1, setOf( k)) + operator fun invoke(k: SigningSecretKey): Multikey = Keys(1, setOf(k.verifyingKey)) + operator fun invoke(k: VerifyingPublicKey): Multikey = Keys(1, setOf(k)) /** * Create a multikey instance that requires some keys from a list @@ -142,7 +154,7 @@ sealed class Multikey { /** * Create a multikey instance that requires any key from a list */ - fun anyOf(vararg keys: VerifyingPublicKey): Multikey = someOf(1, *keys) + fun anyOf(vararg keys: VerifyingPublicKey): Multikey = someOf(1, *keys) /** * Create a multikey instance that requires any key from a list @@ -163,7 +175,7 @@ sealed class Multikey { /** * Create a multikey instance that requires all keys from a list */ - fun allOf(vararg keys: VerifyingPublicKey): Multikey = someOf(keys.size, *keys) + fun allOf(vararg keys: VerifyingPublicKey): Multikey = someOf(keys.size, *keys) /** * Create a multikey instance that requires all keys from a list @@ -181,7 +193,5 @@ sealed class Multikey { fun allOf(keys: List): Multikey = someOf(keys.size, keys) - - } } \ No newline at end of file diff --git a/src/commonTest/kotlin/KeysTest.kt b/src/commonTest/kotlin/KeysTest.kt index c2ffdfd..bb93edf 100644 --- a/src/commonTest/kotlin/KeysTest.kt +++ b/src/commonTest/kotlin/KeysTest.kt @@ -15,9 +15,9 @@ class KeysTest { @Test fun testSigningCreationAndMap() = runTest { initCrypto() - val (stk,pbk) = SigningSecretKey.generatePair() + val (stk, pbk) = SigningSecretKey.generatePair() - val x = mapOf( stk to "STK!", pbk to "PBK!") + val x = mapOf(stk to "STK!", pbk to "PBK!") assertEquals("STK!", x[stk]) val s1 = SigningSecretKey(stk.keyBytes) assertEquals(stk, s1) @@ -52,7 +52,7 @@ class KeysTest { fun testNonDeterministicSeals() = runTest { initCrypto() val data = "Welcome to the crazy new world!".encodeToUByteArray() - val (sk,_) = SigningSecretKey.generatePair() + val (sk, _) = SigningSecretKey.generatePair() val t = now() val s1 = Seal.create(sk, data, createdAt = t) val s2 = Seal.create(sk, data, createdAt = t) @@ -60,11 +60,11 @@ class KeysTest { val s3 = Seal.create(sk, data, createdAt = t, nonDeterministic = true) val s4 = Seal.create(sk, data, createdAt = t, nonDeterministic = true) - for( seal in listOf(s1,s2,s3,s4)) { + for (seal in listOf(s1, s2, s3, s4)) { assertTrue { seal.isValid(data) } assertTrue { Seal.unpack(seal.packed).isValid(data) } } - assertFalse { s2bad.isValid(data)} + assertFalse { s2bad.isValid(data) } assertContentEquals(s1.packed, s2.packed) assertFalse { s1.packed contentEquals s3.packed } assertFalse { s4.packed contentEquals s3.packed } @@ -97,7 +97,7 @@ class KeysTest { assertContentEquals(src, k1.decryptWithNonce(k1.encryptWithNonce(src, nonce), nonce)) assertThrows { val n2 = nonce.copyOf() - n2[4] = n2[4].inv() + n2[4] = n2[4].inv() k1.decryptWithNonce(k1.encryptWithNonce(src, nonce), n2) } @@ -173,7 +173,7 @@ class KeysTest { val (sk0, pk0) = Asymmetric.generateKeys() // println(sk0.publicKey) - val j = Json { prettyPrint = true} + val j = Json { prettyPrint = true } val sk1 = j.decodeFromString(j.encodeToString(sk0)) assertEquals(sk0, sk1) @@ -274,11 +274,11 @@ class KeysTest { assertContentEquals(k.verifyingKey.keyBytes, dk2.id.binaryTag.take(32).toUByteArray()) assertContentEquals(k.verifyingKey.keyBytes, dk1.id.binaryTag.take(32).toUByteArray()) // and restored from id should be the same: - assertEquals( k.verifyingKey, dk2.id.id.asVerifyingKey) + assertEquals(k.verifyingKey, dk2.id.id.asVerifyingKey) } @Test - fun multiKeyTestSom() = runTest { + fun multiKeyTestSome() = runTest { initCrypto() val k1 = SigningSecretKey.new() val k2 = SigningSecretKey.new() @@ -301,7 +301,7 @@ class KeysTest { println(pack(mk23).toDump()) println(pack(mk23).size) - val smk23: Multikey = Multikey.someOf(2, k1.verifyingKey, k2.verifyingKey, k3.verifyingKey) + val smk23: Multikey = Multikey.someOf(2, k1.verifyingKey, k2.verifyingKey, k3.verifyingKey) // val smk13: Multikey = Multikey.Keys(1, setOf(k1.verifyingKey, k2.verifyingKey, k3.verifyingKey)) assertTrue { smk23.check(k1.verifyingKey, k2.verifyingKey, k4.verifyingKey) } @@ -342,6 +342,22 @@ class KeysTest { assertFalse { s3.check(k1.verifyingKey) } assertFalse { s3.check(k2.verifyingKey) } assertFalse { s3.check(k1.verifyingKey, k4.verifyingKey) } - } -} \ No newline at end of file + + @Test + fun multiKeyTestAny() = runTest { + initCrypto() + val k1 = SigningSecretKey.new() + val k2 = SigningSecretKey.new() + val k3 = SigningSecretKey.new() + val k4 = SigningSecretKey.new() + val k5 = SigningSecretKey.new() +// val k6 = SigningSecretKey.new() + val mk: Multikey = Multikey.AnyKey + assertTrue { mk.check(k1.verifyingKey) } + assertTrue { mk.check(k2.verifyingKey) } + assertTrue { mk.check(k3.verifyingKey) } + assertTrue { mk.check(k4.verifyingKey) } + assertTrue { mk.check(k5.verifyingKey) } + } +}