Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix problems from Arash review #84

Merged
merged 1 commit into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[![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.6%25%20LOC%20(7055/7787)-blue)
![Coverage](https://img.shields.io/badge/coverage-90.5%25%20LOC%20(7064/7809)-blue)

# ElectionGuard-Kotlin Elliptic Curve

_last update 05/09/2024_
_last update 05/22/2024_

EGK Elliptic Curve (egk-ec) is an experimental implementation of [ElectionGuard](https://github.com/microsoft/electionguard),
[version 2.0](https://github.com/microsoft/electionguard/releases/download/v2.0/EG_Spec_2_0.pdf),
Expand Down
18 changes: 12 additions & 6 deletions src/main/kotlin/org/cryptobiotic/eg/cli/RunVerifier.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package org.cryptobiotic.eg.cli

import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.cli.ArgParser
import kotlinx.cli.ArgType
import kotlinx.cli.default
import kotlinx.cli.required
import org.cryptobiotic.eg.cli.RunTrustedTallyDecryption.Companion
import org.cryptobiotic.eg.publish.Consumer
import org.cryptobiotic.eg.publish.readElectionRecord
import org.cryptobiotic.eg.verifier.Verifier
import org.cryptobiotic.util.ErrorMessages
import org.cryptobiotic.util.Stats
import org.cryptobiotic.util.Stopwatch
import kotlin.system.exitProcess
Expand Down Expand Up @@ -72,7 +74,7 @@ class RunVerifier {
return if (allOk) 0 else 1
}

fun verifyEncryptedBallots(inputDir: String, nthreads: Int) {
fun verifyEncryptedBallots(inputDir: String, nthreads: Int): Result<Boolean, ErrorMessages> {
val stopwatch = Stopwatch() // start timing here

val electionRecord = readElectionRecord(inputDir)
Expand All @@ -84,9 +86,10 @@ class RunVerifier {
stats.show(logger)

logger.info { "VerifyEncryptedBallots ${stopwatch.took()}" }
return if (errs.hasErrors()) Err(errs) else Ok(true)
}

fun verifyDecryptedTally(inputDir: String) {
fun verifyDecryptedTally(inputDir: String): Result<Boolean, ErrorMessages> {
val stopwatch = Stopwatch() // start timing here

val electionRecord = readElectionRecord(inputDir)
Expand All @@ -99,9 +102,10 @@ class RunVerifier {
stats.show(logger)

logger.info { "verifyDecryptedTally ${stopwatch.took()} " }
return if (errs.hasErrors()) Err(errs) else Ok(true)
}

fun verifyChallengedBallots(inputDir: String) {
fun verifyChallengedBallots(inputDir: String): Result<Boolean, ErrorMessages> {
val stopwatch = Stopwatch() // start timing here

val electionRecord = readElectionRecord(inputDir)
Expand All @@ -113,15 +117,17 @@ class RunVerifier {
stats.show(logger)

logger.info { "verifyRecoveredShares ${stopwatch.took()}" }
return if (errs.hasErrors()) Err(errs) else Ok(true)
}

fun verifyTallyBallotIds(inputDir: String) {
fun verifyTallyBallotIds(inputDir: String): Result<Boolean, ErrorMessages> {
val electionRecord = readElectionRecord(inputDir)
// println("$inputDir stage=${electionRecord.stage()} ncast_ballots=${electionRecord.encryptedTally()!!.castBallotIds.size}")

val verifier = Verifier(electionRecord, 1)
val errs = verifier.verifyTallyBallotIds()
logger.info { errs }
return if (errs.hasErrors()) Err(errs) else Ok(true)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class RFC9380(val group: EcGroupContext, val DST: ByteArray, kBytes: Int) {
// 1. ell = ceil(len_in_bytes / b_in_bytes)
val ell = (len_in_bytes + b_in_bytes - 1)/ b_in_bytes
// 2. ABORT if ell > 255 or len_in_bytes > 65535 or len(DST) > 255
require (ell < 255 && len_in_bytes < 65535 && DST.size < 255)
require (ell < 256 && len_in_bytes < 65536 && DST.size < 256)

// 3. DST_prime = DST || I2OSP(len(DST), 1)
val DST_prime = DST + I2OSP(DST.size, 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ data class DecryptedTallyOrBallot(
data class Contest(
val contestId: String, // matches ContestDescription.contestId
val selections: List<Selection>,
val ballot_count: Int = 0, // number of ballots voting on this contest, 1 for ballots
val ballot_count: Int? = null, // number of ballots voting on this contest, 1 for ballots
val decryptedContestData: DecryptedContestData? = null, // only for ballots, but still optional
) {
init {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ data class EncryptedTally(
val contestId: String, // matches ContestDescription.contestId
val sequenceOrder: Int, // matches ContestDescription.sequenceOrder
val selections: List<Selection>,
val ballot_count: Int = 0, // number of ballots voting on this contest
val ballot_count: Int? = null, // number of ballots voting on this contest
val contestData: HashedElGamalCiphertext? = null, // only used when decrypting ballots, not tallies
) {
init {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ data class DecryptedTallyOrBallotJson(
data class DecryptedContestJson(
val contest_id: String,
val selections: List<DecryptedSelectionJson>,
val ballot_count: Int = 0, // number of ballots voting on this contest
val ballot_count: Int? = null, // number of ballots voting on this contest
val decrypted_contest_data: DecryptedContestDataJson?, // ballot decryption only
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ data class EncryptedTallyContestJson(
val contest_id: String,
val sequence_order: Int,
val selections: List<EncryptedTallySelectionJson>,
val ballot_count: Int = 0, // number of ballots voting on this contest
val ballot_count: Int? = null, // number of ballots voting on this contest
)

@Serializable
Expand Down
78 changes: 53 additions & 25 deletions src/main/kotlin/org/cryptobiotic/eg/verifier/Verifier.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ package org.cryptobiotic.eg.verifier
import com.github.michaelbull.result.*

import org.cryptobiotic.eg.core.*
import org.cryptobiotic.eg.core.intgroup.IntGroupConstants
import org.cryptobiotic.eg.core.ecgroup.VecGroups
import org.cryptobiotic.eg.core.intgroup.Primes4096
import org.cryptobiotic.eg.core.intgroup.IntGroupContext
import org.cryptobiotic.eg.election.*
import org.cryptobiotic.eg.publish.ElectionRecord
import org.cryptobiotic.util.ErrorMessages
Expand Down Expand Up @@ -41,7 +40,7 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) {
if (record.stage() < ElectionRecord.Stage.INIT) {
println("election record stage = ${record.stage()}, stopping verification now\n")
if (showTime) println(" verify ${stopwatch.took()}")
return true
return (parametersOk is Ok)
}

val guardiansOk = verifyGuardianPublicKey()
Expand All @@ -53,10 +52,11 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) {
val baseHashOk = verifyExtendedBaseHash()
println(" 4. verifyExtendedBaseHash= $baseHashOk")

val initOk = (parametersOk is Ok) && (guardiansOk is Ok) && (publicKeyOk is Ok) && (baseHashOk is Ok)
if (record.stage() < ElectionRecord.Stage.ENCRYPTED) {
println("election record stage = ${record.stage()}, stopping verification now\n")
if (showTime) println(" verify 2,3,4 ${stopwatch.took()}")
return true
return initOk
}

// encryption and vote limits
Expand All @@ -81,7 +81,7 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) {

if (record.stage() < ElectionRecord.Stage.TALLIED) {
println("election record stage = ${record.stage()}, stopping verification now\n")
return true
return initOk && ballotsOk && chainOk
}

// tally accumulation
Expand All @@ -95,7 +95,7 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) {

if (record.stage() < ElectionRecord.Stage.DECRYPTED) {
println("election record stage = ${record.stage()}, stopping verification now\n")
return true
return initOk && ballotsOk && chainOk && tallyOk
}

// tally decryption
Expand All @@ -115,8 +115,7 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) {
println(challengedErrs)
}

val allOk = (parametersOk is Ok) && (guardiansOk is Ok) && (publicKeyOk is Ok) && (baseHashOk is Ok) &&
ballotsOk && chainOk && tallyOk && tdOk && challengedOk
val allOk = initOk && ballotsOk && chainOk && tallyOk && tdOk && challengedOk

println("verify allOK = $allOk\n")
return allOk
Expand All @@ -125,26 +124,57 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) {
// Verification Box 1
private fun verifyParameters(config : ElectionConfig, manifestBytes: ByteArray): Result<Boolean, String> {
val check: MutableList<Result<Boolean, String>> = mutableListOf()
val configConstants = config.constants

if (config.constants.protocolVersion != "v2.0.0" && config.constants.protocolVersion != "v2.1.0") {
check.add(Err(" 1.A The election record protocolVersion is unknown: '${config.constants.protocolVersion}'"))
if (configConstants.protocolVersion != "v2.0.0" && configConstants.protocolVersion != "v2.1.0") {
check.add(Err(" 1.A The election record protocolVersion is unknown: '${configConstants.protocolVersion}'"))
}

if (group.constants.type == GroupType.IntegerGroup) {
val constants: IntGroupConstants = (group as IntGroupContext).groupConstants

if (!constants.largePrime.toByteArray().normalize(512).contentEquals(Primes4096.largePrimeBytes)) {
val largePrime = configConstants.constants["largePrime"]
if (largePrime == null || !largePrime.toByteArray().normalize(512).contentEquals(Primes4096.largePrimeBytes)) {
check.add(Err(" 1.B The large prime is not equal to p defined in Section 3.1.1"))
}
if (!constants.smallPrime.toByteArray().normalize(32).contentEquals(Primes4096.smallPrimeBytes)) {
val smallPrime = configConstants.constants["smallPrime"]
if (smallPrime == null || !smallPrime.toByteArray().normalize(32).contentEquals(Primes4096.smallPrimeBytes)) {
check.add(Err(" 1.C The small prime is not equal to q defined in Section 3.1.1"))
}
if (!constants.cofactor.toByteArray().normalize(512).contentEquals(Primes4096.residualBytes)) {
val cofactor = configConstants.constants["cofactor"]
if (cofactor == null || !cofactor.toByteArray().normalize(512).contentEquals(Primes4096.residualBytes)) {
check.add(Err(" 1.D The cofactor is not equal to r defined in Section 3.1.1"))
}
if (!constants.generator.toByteArray().normalize(512).contentEquals(Primes4096.generatorBytes)) {
val generator = configConstants.constants["generator"]
if (generator == null || !generator.toByteArray().normalize(512).contentEquals(Primes4096.generatorBytes)) {
check.add(Err(" 1.E The generator is not equal to g defined in Section 3.1.1"))
}
} else {
val vecGroup: VecGroups = VecGroups.NAMED_PARAMS.get("P-256")!!

val primeModulus = configConstants.constants["primeModulus"]!!

if (primeModulus == null || !primeModulus.toByteArray().contentEquals(vecGroup.p.toByteArray())) {
check.add(Err(" 1.B The primeModulus is not equal to p defined in P-256"))
}
val order = configConstants.constants["order"]
if (order == null || !order.toByteArray().contentEquals(vecGroup.n.toByteArray())) {
check.add(Err(" 1.C The order is not equal to n defined in P-256"))
}
val a = configConstants.constants["a"]
if (a == null || !a.toByteArray().contentEquals(vecGroup.a.toByteArray())) {
check.add(Err(" 1.D The factor a is not equal to a defined in P-256"))
}
val b = configConstants.constants["b"]
if (b == null || !b.toByteArray().contentEquals(vecGroup.b.toByteArray())) {
check.add(Err(" 1.D The factor b is not equal to b defined in P-256"))
}
val gx = configConstants.constants["g.x"]
if (gx == null || !gx.toByteArray().contentEquals(vecGroup.gx.toByteArray())) {
check.add(Err(" 1.E The generatr g.x is not equal to g.x defined in P-256"))
}
val gy = configConstants.constants["g.y"]
if (gy == null || !gy.toByteArray().contentEquals(vecGroup.gy.toByteArray())) {
check.add(Err(" 1.E The generatr g.y is not equal to g.y defined in P-256"))
}
}

val Hp = parameterBaseHash(config.constants)
Expand All @@ -155,8 +185,8 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) {
if (Hm != config.manifestHash) {
check.add(Err(" 1.G The manifest hash does not match eq 5"))
}
val Hd = electionBaseHash(Hp, Hm, config.numberOfGuardians, config.quorum)
if (Hd != config.electionBaseHash) {
val Hb = electionBaseHash(Hp, Hm, config.numberOfGuardians, config.quorum)
if (Hb != config.electionBaseHash) {
check.add(Err(" 1.H The election base hash does not match eq 6"))
}

Expand Down Expand Up @@ -247,23 +277,21 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) {
return errs
}

fun verifyTallyBallotIds(): Boolean {
var allOk = true
fun verifyTallyBallotIds(): ErrorMessages {
val errs = ErrorMessages("verifyTallyBallotIds")
val encryptedBallotIds = record.encryptedAllBallots{ it.state == EncryptedBallot.BallotState.CAST }.map { it.ballotId }.toSet()
val tallyBallotIds = record.encryptedTally()!!.castBallotIds.toSet()
encryptedBallotIds.forEach {
if (!tallyBallotIds.contains(it)) {
println(" tallyBallotIds doesnt contain $it")
allOk = false
errs.add(" tallyBallotIds doesnt contain $it")
}
}
tallyBallotIds.forEach {
if (!encryptedBallotIds.contains(it)) {
println(" encryptedBallotIds doesnt contain $it")
allOk = false
errs.add(" encryptedBallotIds doesnt contain $it")
}
}
return allOk
return errs
}


Expand Down
Loading
Loading