Skip to content

Commit

Permalink
Add more tests for keyceremony, tally.
Browse files Browse the repository at this point in the history
Add mockK for testing.
  • Loading branch information
JohnLCaron committed Apr 13, 2024
1 parent 1d2d065 commit 566ce22
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 41 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![License](https://img.shields.io/github/license/JohnLCaron/egk-ec)](https://github.com/JohnLCaron/egk-ec/blob/main/LICENSE.txt)
![GitHub branch checks state](https://img.shields.io/github/actions/workflow/status/JohnLCaron/egk-ec/unit-tests.yml)
![Coverage](https://img.shields.io/badge/coverage-90.3%25%20LOC%20(6905/7650)-blue)
![Coverage](https://img.shields.io/badge/coverage-90.5%25%20LOC%20(6924/7647)-blue)

# ElectionGuard-Kotlin Elliptic Curve

Expand Down
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ dependencies {
implementation(libs.bundles.eglib)
implementation(libs.bundles.logging)
testImplementation(libs.bundles.egtest)
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation(libs.kotlin.test)
testImplementation(libs.mockk)
}

tasks.test {
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", vers
kotest-property = { module = "io.kotest:kotest-property", version.ref = "kotest-version" }
kotest-datatest = { module = "io.kotest:kotest-framework-datatest", version.ref = "kotest-version" }
kotest-runner = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest-version" }
mockk = { module = "io.mockk:mockk", version = "1.13.7" }
mockk = { module = "io.mockk:mockk", version = "1.13.10" }

[bundles]
eglib = ["bull-result", "kotlinx-cli", "kotlinx-coroutines-core", "kotlinx-datetime", "kotlinx-serialization-json", "oshai-logging"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,6 @@ open class KeyCeremonyTrustee(
}

// If the MAC verifies, Gℓ decrypts b(Pi(ℓ), 32) = Ci,ℓ,1 ⊕ k1 .
// TODO prove always 32 bytes
return ByteArray(32) { c1[it] xor k1[it] }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class RunEncryptBallotTest {
val manifest = record.manifest()
val publisher = makePublisher(outputDeviceDir, true)
val consumerOut = makeConsumer(outputDir, consumerIn.group)
createDirectories("$outputDeviceDir")
createDirectories(outputDeviceDir)

val ballotProvider = RandomBallotProvider(manifest)
repeat(nballots) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package org.cryptobiotic.eg.tally
package org.cryptobiotic.eg.decrypt

import org.cryptobiotic.eg.core.productionGroup
import org.cryptobiotic.eg.decrypt.computeLagrangeCoefficient
import kotlin.test.Test
import kotlin.test.assertEquals

private val group = productionGroup()

class LagrangeCoefficientsTest {
private val group = productionGroup()

@Test
fun testLagrangeCoefficientAreIntegral() {
Expand All @@ -32,39 +30,39 @@ class LagrangeCoefficientsTest {
val coeff: Int = computeLagrangeCoefficientInt(coord, others)
val numer: Int = computeLagrangeNumerator(others)
val denom: Int = computeLagrangeDenominator(coord, others)
val coeffQ = group.computeLagrangeCoefficient(coord, others.map { it})
val coeffQ = group.computeLagrangeCoefficient(coord, others.map { it })
println("($coord) $coeff == ${numer} / ${denom} rem ${numer % denom} == $coeffQ")
if (exact) {
assertEquals(0, numer % denom)
}
}
println()
}
}

fun computeLagrangeCoefficientInt(coordinate: Int, others: List<Int>): Int {
if (others.isEmpty()) {
return 1
}
val numerator: Int = others.reduce { a, b -> a * b }
fun computeLagrangeCoefficientInt(coordinate: Int, others: List<Int>): Int {
if (others.isEmpty()) {
return 1
}
val numerator: Int = others.reduce { a, b -> a * b }

val diff: List<Int> = others.map { degree: Int -> degree - coordinate }
val denominator = diff.reduce { a, b -> a * b }
val diff: List<Int> = others.map { degree: Int -> degree - coordinate }
val denominator = diff.reduce { a, b -> a * b }

return numerator / denominator
}
return numerator / denominator
}

fun computeLagrangeNumerator(others: List<Int>): Int {
if (others.isEmpty()) {
return 1
fun computeLagrangeNumerator(others: List<Int>): Int {
if (others.isEmpty()) {
return 1
}
return others.reduce { a, b -> a * b }
}
return others.reduce { a, b -> a * b }
}

fun computeLagrangeDenominator(coordinate: Int, others: List<Int>): Int {
if (others.isEmpty()) {
return 1
fun computeLagrangeDenominator(coordinate: Int, others: List<Int>): Int {
if (others.isEmpty()) {
return 1
}
val diff: List<Int> = others.map { degree: Int -> degree - coordinate }
return diff.reduce { a, b -> a * b }
}
val diff: List<Int> = others.map { degree: Int -> degree - coordinate }
return diff.reduce { a, b -> a * b }
}
133 changes: 133 additions & 0 deletions src/test/kotlin/org/cryptobiotic/eg/keyceremony/KeyCeremonyMockTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package org.cryptobiotic.eg.keyceremony

import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.unwrap
import io.mockk.every
import io.mockk.spyk
import org.cryptobiotic.eg.core.generateHashedCiphertext
import org.cryptobiotic.eg.core.productionGroup
import kotlin.test.Test

import kotlin.test.assertTrue

class KeyCeremonyMockTest {
val group = productionGroup("P-256")

@Test
fun testKeyCeremonyOk() {
val trustee1 = KeyCeremonyTrustee(group, "id1", 1, 3, 3)
val trustee2 = KeyCeremonyTrustee(group, "id2", 3, 3, 3)
val trustee3 = KeyCeremonyTrustee(group, "id3", 2, 3, 3)
val spy3 = spyk(trustee3)
val trustees = listOf(trustee1, trustee2, spy3)

val result = keyCeremonyExchange(trustees)
assertTrue(result is Ok)
}

@Test
fun testKeyCeremonyMockOk() {
val group = productionGroup()
val trustee1 = KeyCeremonyTrustee(group, "id1", 1, 3, 3)
val trustee2 = KeyCeremonyTrustee(group, "id2", 3, 3, 3)
val trustee3 = KeyCeremonyTrustee(group, "id3", 2, 3, 3)
val spy3 = spyk(trustee3)
every { spy3.encryptedKeyShareFor(trustee1.id()) } answers {
val result1 = trustee3.encryptedKeyShareFor(trustee1.id())
assertTrue(result1 is Ok)
val ss21 = result1.unwrap()
Ok(EncryptedKeyShare(spy3.xCoordinate(), spy3.id(), trustee1.id(), ss21.encryptedCoordinate))
}
val trustees = listOf(trustee1, trustee2, spy3)

val result = keyCeremonyExchange(trustees)
assertTrue(result is Ok)
}

@Test
fun testAllowBadEncryptedShare() {
val group = productionGroup()
val trustee1 = KeyCeremonyTrustee(group, "id1", 1, 3, 3)
val trustee2 = KeyCeremonyTrustee(group, "id2", 3, 3, 3)
val trustee3 = KeyCeremonyTrustee(group, "id3", 2, 3, 3)
val spy3 = spyk(trustee3)
every { spy3.encryptedKeyShareFor(trustee1.id()) } answers {
trustee3.encryptedKeyShareFor(trustee1.id()) // trustee needs to cache
// bad EncryptedShare
Ok(EncryptedKeyShare(spy3.xCoordinate(), spy3.id(), trustee1.id(), generateHashedCiphertext(group)))
}
val trustees = listOf(trustee1, trustee2, spy3)
val result = keyCeremonyExchange(trustees, true)
println("result = $result")
assertTrue(result is Ok)
}

@Test
fun testBadEncryptedShare() {
val group = productionGroup()
val trustee1 = KeyCeremonyTrustee(group, "id1", 1, 3, 3)
val trustee2 = KeyCeremonyTrustee(group, "id2", 3, 3, 3)
val trustee3 = KeyCeremonyTrustee(group, "id3", 2, 3, 3)
val spy3 = spyk(trustee3)
every { spy3.encryptedKeyShareFor(trustee1.id()) } answers {
trustee3.encryptedKeyShareFor(trustee1.id()) // trustee needs to cache
// bad EncryptedShare
Ok(EncryptedKeyShare(spy3.xCoordinate(), spy3.id(), trustee1.id(), generateHashedCiphertext(group)))
}
val trustees = listOf(trustee1, trustee2, spy3)
val result = keyCeremonyExchange(trustees, false)
println("result = $result")
assertTrue(result is Err)
assertTrue(result.error.contains("Trustee 'id1' couldnt decrypt EncryptedKeyShare for missingGuardianId 'id3'"))
}

@Test
fun testBadKeySharesAllowFalse() {
val group = productionGroup()
val trustee1 = KeyCeremonyTrustee(group, "id1", 1, 3, 3)
val trustee2 = KeyCeremonyTrustee(group, "id2", 3, 3, 3)
val trustee3 = KeyCeremonyTrustee(group, "id3", 2, 3, 3)
val spy3 = spyk(trustee3)
every { spy3.encryptedKeyShareFor(trustee1.id()) } answers {
trustee3.encryptedKeyShareFor(trustee1.id()) // trustee needs to cache
// bad EncryptedShare
Ok(EncryptedKeyShare(spy3.xCoordinate(), spy3.id(), trustee1.id(), generateHashedCiphertext(group)))
}
every { spy3.keyShareFor(trustee1.id()) } answers {
// bad KeyShare
Ok(KeyShare(spy3.xCoordinate(), spy3.id(), trustee1.id(), group.TWO_MOD_Q))
}
val trustees = listOf(trustee1, trustee2, spy3)
val result = keyCeremonyExchange(trustees, false)
println("result = $result")
assertTrue(result is Err)
println(result)
assertTrue(result.error.contains("keyCeremonyExchange not complete"))
}

@Test
fun testBadKeySharesAllowTrue() {
val group = productionGroup()
val trustee1 = KeyCeremonyTrustee(group, "id1", 1, 3, 3)
val trustee2 = KeyCeremonyTrustee(group, "id2", 3, 3, 3)
val trustee3 = KeyCeremonyTrustee(group, "id3", 2, 3, 3)
val spy3 = spyk(trustee3)
every { spy3.encryptedKeyShareFor(trustee1.id()) } answers {
trustee3.encryptedKeyShareFor(trustee1.id()) // trustee needs to cache
// bad EncryptedShare
Ok(EncryptedKeyShare(spy3.xCoordinate(), spy3.id(), trustee1.id(), generateHashedCiphertext(group)))
}
every { spy3.keyShareFor(trustee1.id()) } answers {
// bad KeyShare
Ok(KeyShare(spy3.xCoordinate(), spy3.id(), trustee1.id(), group.TWO_MOD_Q))
}
val trustees = listOf(trustee1, trustee2, spy3)
val result = keyCeremonyExchange(trustees, true)
println("result = $result")
assertTrue(result is Err)
println(result)
assertTrue(result.error.contains("keyCeremonyExchange not complete"))
}

}
Loading

0 comments on commit 566ce22

Please sign in to comment.