Skip to content

Commit

Permalink
Merge pull request #84 from JohnLCaron/fixStuff
Browse files Browse the repository at this point in the history
Fix problems from Arash review
  • Loading branch information
JohnLCaron authored May 22, 2024
2 parents 81df478 + 28992ab commit 0fd3d64
Show file tree
Hide file tree
Showing 490 changed files with 72,137 additions and 74,224 deletions.
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

0 comments on commit 0fd3d64

Please sign in to comment.