Prepare 0.9.6-SNAPSHOT with DH input validation and distinct signer checks
This commit is contained in:
1 parent
66254f8fa6
commit
70d7a13fc3
7 files changed
+175
-7
No files matched your search
@@ -0,0 +1,57 @@
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.sergeych.bipack.BipackDecoder
|
||||
import net.sergeych.crypto2.*
|
||||
import kotlin.test.*
|
||||
|
||||
class DuplicateSignerTest {
|
||||
@Test fun thresholdsCountDistinctAuthorizedKeys() = runTest {
|
||||
initCrypto()
|
||||
val keys = List(4) { SigningSecretKey.new().verifyingKey }
|
||||
val outsider = SigningSecretKey.new().verifyingKey
|
||||
// Exhaust all subsets of authorized keys, thresholds, and repeated inputs.
|
||||
for (threshold in 1..keys.size) {
|
||||
val policy = Multikey.someOf(threshold, keys)
|
||||
for (mask in 0 until (1 shl keys.size)) {
|
||||
val selected = keys.filterIndexed { index, _ -> mask and (1 shl index) != 0 }
|
||||
for (copies in 1..5) {
|
||||
val signers = selected.flatMap { key -> List(copies) { key } } + List(5) { outsider }
|
||||
assertEquals(selected.size >= threshold, policy.check(signers))
|
||||
assertEquals(selected.size >= threshold, policy.check(signers.reversed()))
|
||||
}
|
||||
}
|
||||
}
|
||||
assertFalse(Multikey.allOf(keys).check(List(10) { keys.first() }))
|
||||
assertTrue(Multikey.allOf(keys).check(keys + keys))
|
||||
}
|
||||
|
||||
@Test fun nestedPoliciesKeepBooleanSemantics() = runTest {
|
||||
initCrypto()
|
||||
val a = SigningSecretKey.new().verifyingKey
|
||||
val b = SigningSecretKey.new().verifyingKey
|
||||
val c = SigningSecretKey.new().verifyingKey
|
||||
val d = SigningSecretKey.new().verifyingKey
|
||||
val threshold = Multikey.someOf(2, a, b)
|
||||
val policy = (threshold and c) or d
|
||||
assertFalse(policy.check(a, a, c))
|
||||
assertTrue(policy.check(a, a, b, c))
|
||||
assertTrue(policy.check(d))
|
||||
// Overlapping branches remain logical expressions, not globally consumed signatures.
|
||||
assertTrue(((a or b) and (a or c)).check(a))
|
||||
}
|
||||
|
||||
@Test fun repeatedValidSealCannotSatisfyThresholdAfterDecoding() = runTest {
|
||||
initCrypto()
|
||||
val a = SigningSecretKey.new()
|
||||
val b = SigningSecretKey.new()
|
||||
val policy = Multikey.someOf(2, a.verifyingKey, b.verifyingKey)
|
||||
val original = SealedBox.create("threshold regression".encodeToByteArray().toUByteArray(), a)
|
||||
val repeated = SealedBox(original.message, List(3) { original.seals.single() })
|
||||
val decoded = BipackDecoder.decode<SealedBox>(repeated.encoded.asByteArray())
|
||||
assertEquals(3, decoded.seals.size)
|
||||
assertEquals(1, decoded.signedByKeys.toSet().size)
|
||||
assertFalse(decoded.isSealedBy(policy))
|
||||
assertTrue(decoded.isSealedBy(Multikey(a)))
|
||||
val authorized = BipackDecoder.decode<SealedBox>((original + b).encoded.asByteArray())
|
||||
assertTrue(authorized.isSealedBy(policy))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.sergeych.crypto2.SafeKeyExchange
|
||||
import net.sergeych.crypto2.initCrypto
|
||||
import kotlin.test.*
|
||||
|
||||
class KeyExchangeValidationTest {
|
||||
@Test
|
||||
fun rejectsLowOrderPeerKeysOnBothSides() = runTest {
|
||||
initCrypto()
|
||||
// u = 0, 1, p-1, p, p+1, including aliases with the ignored high bit set.
|
||||
val points = listOf(
|
||||
UByteArray(32),
|
||||
UByteArray(32).also { it[0] = 1u },
|
||||
fieldEncoding(0xecu),
|
||||
fieldEncoding(0xedu),
|
||||
fieldEncoding(0xeeu),
|
||||
)
|
||||
for ((index, point) in points.withIndex()) {
|
||||
for (highBit in listOf(false, true)) {
|
||||
val bytes = point.copyOf()
|
||||
if (highBit) bytes[31] = (bytes[31].toInt() or 0x80).toUByte()
|
||||
val peer = SafeKeyExchange.PublicKey(bytes)
|
||||
assertFails("client accepted low-order point $index, highBit=$highBit") {
|
||||
SafeKeyExchange().clientSessionKey(peer)
|
||||
}
|
||||
assertFails("server accepted low-order point $index, highBit=$highBit") {
|
||||
SafeKeyExchange().serverSessionKey(peer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun rejectsWrongLengthBeforeCallingNativeCode() = runTest {
|
||||
initCrypto()
|
||||
val valid = SafeKeyExchange().publicKey.keyBytes
|
||||
for (size in listOf(0, 1, 31, 33, 64)) {
|
||||
val peer = SafeKeyExchange.PublicKey(valid.copyOf(size))
|
||||
assertFailsWith<IllegalArgumentException> { SafeKeyExchange().clientSessionKey(peer) }
|
||||
assertFailsWith<IllegalArgumentException> { SafeKeyExchange().serverSessionKey(peer) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun validPeersStillAgreeInBothDirections() = runTest {
|
||||
initCrypto()
|
||||
val client = SafeKeyExchange()
|
||||
val server = SafeKeyExchange()
|
||||
val c = client.clientSessionKey(server.publicKey)
|
||||
val s = server.serverSessionKey(client.publicKey)
|
||||
assertContentEquals(c.sessionTag, s.sessionTag)
|
||||
assertEquals("client", s.decryptString(c.encrypt("client")))
|
||||
assertEquals("server", c.decryptString(s.encrypt("server")))
|
||||
}
|
||||
|
||||
private fun fieldEncoding(first: UByte) = UByteArray(32) { 0xffu }.also {
|
||||
it[0] = first
|
||||
it[31] = 0x7fu
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user