From 33d69b48cac6c949b8bdc9a024da5e20cfa267b4 Mon Sep 17 00:00:00 2001 From: JohnLCaron Date: Thu, 9 Nov 2023 09:14:25 -0700 Subject: [PATCH] Verifier uses ErrorMessages. --- .../kotlin/electionguard/ballot/Manifest.kt | 20 ++- .../kotlin/electionguard/ballot/ManifestIF.kt | 2 + .../kotlin/electionguard/verifier/Verifier.kt | 84 +++++++---- .../verifier/VerifyDecryption.kt | 113 ++++++++------- .../verifier/VerifyEncryptedBallots.kt | 133 +++++++++--------- .../verifier/VerifyPreEncryptedBallots.kt | 42 +++--- .../electionguard/verifier/VerifyTally.kt | 16 +-- .../decryptBallot/EncryptDecryptBallotTest.kt | 15 +- .../encrypt/AddEncryptedBallotTest.kt | 23 ++- .../preencrypt/PreEncryptorTest.kt | 9 +- .../verifier/AttackEncryptedBallotTest.kt | 4 +- .../verifier/DuplicateTrackingCodesTest.kt | 6 +- .../electionguard/cli/RunBatchEncryption.kt | 8 +- .../electionguard/cli/RunExampleEncryption.kt | 10 +- .../kotlin/electionguard/cli/RunVerifier.kt | 19 +-- .../testvectors/EncryptedBallotJsonV.kt | 4 + .../testvectors/PlaintextBallotJsonV.kt | 4 + 17 files changed, 267 insertions(+), 245 deletions(-) diff --git a/egklib/src/commonMain/kotlin/electionguard/ballot/Manifest.kt b/egklib/src/commonMain/kotlin/electionguard/ballot/Manifest.kt index b536b75d..631dd318 100644 --- a/egklib/src/commonMain/kotlin/electionguard/ballot/Manifest.kt +++ b/egklib/src/commonMain/kotlin/electionguard/ballot/Manifest.kt @@ -38,24 +38,22 @@ data class Manifest( return styleToContestsMap[ballotStyle] } - override fun contestLimit(contestId : String) : Int { - return contestIdToContestLimit[contestId]?: 1 + override fun findContest(contestId: String): ManifestIF.Contest? { + return contestMap[contestId] } - override fun optionLimit(contestId : String) : Int { - return contestIdToOptionLimit[contestId]?: 1 + override fun contestLimit(contestId : String) : Int { + return contestMap[contestId]?.votesAllowed ?: 1 } - /** Map of contestId to contest selection limit. */ - val contestIdToContestLimit : Map by - lazy { - contests.associate { it.contestId to it.votesAllowed } + override fun optionLimit(contestId : String) : Int { + return contestMap[contestId]?.optionSelectionLimit ?: 1 } - /** Map of contestId to contest selection limit. */ - val contestIdToOptionLimit : Map by + /** Map of contestId to contests. */ + val contestMap : Map by lazy { - contests.associate { it.contestId to it.optionSelectionLimit } + contests.associateBy { it.contestId } } /** Map "$contestId/$selectionId" to candidateId. */ diff --git a/egklib/src/commonMain/kotlin/electionguard/ballot/ManifestIF.kt b/egklib/src/commonMain/kotlin/electionguard/ballot/ManifestIF.kt index c9acc386..57e8f732 100644 --- a/egklib/src/commonMain/kotlin/electionguard/ballot/ManifestIF.kt +++ b/egklib/src/commonMain/kotlin/electionguard/ballot/ManifestIF.kt @@ -18,6 +18,8 @@ interface ManifestIF { /** get the list of valid contests for the given ballotStyle */ fun contestsForBallotStyle(ballotStyle : String): List? + fun findContest(contestId: String): Contest? + /** get the contest selection limit (aka votesAllowed) for the given contest id */ fun contestLimit(contestId : String): Int diff --git a/egklib/src/commonMain/kotlin/electionguard/verifier/Verifier.kt b/egklib/src/commonMain/kotlin/electionguard/verifier/Verifier.kt index 2e5f858b..20a66615 100644 --- a/egklib/src/commonMain/kotlin/electionguard/verifier/Verifier.kt +++ b/egklib/src/commonMain/kotlin/electionguard/verifier/Verifier.kt @@ -4,6 +4,7 @@ import com.github.michaelbull.result.* import electionguard.ballot.* import electionguard.core.* import electionguard.publish.ElectionRecord +import electionguard.util.ErrorMessages import electionguard.util.Stats class Verifier(val record: ElectionRecord, val nthreads: Int = 11) { @@ -56,16 +57,24 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) { } // encryption and vote limits - val verifyEncryptions = VerifyEncryptedBallots(group, manifest, jointPublicKey, He, config, nthreads) - // Note we are validating all ballots, not just CAST,including preencrypted - val ballotResult = verifyEncryptions.verifyBallots(record.encryptedAllBallots { true }, stats, showTime) - println(" 5,6,15,16,17,18. verifyEncryptedBallots $ballotResult") + val encryptionVerifier = VerifyEncryptedBallots(group, manifest, jointPublicKey, He, config, nthreads) + // Note we are validating all ballots, not just CAST, and including preencrypted + val eerrs = ErrorMessages("") + val ballotsOk = encryptionVerifier.verifyBallots(record.encryptedAllBallots { true }, eerrs, stats, showTime) + println(" 5,6,15,16,17,18. verifyEncryptedBallots $ballotsOk") + if (!ballotsOk) { + println(eerrs) + } - val chainResults = if (config.chainConfirmationCodes) { - val chainResult = verifyEncryptions.verifyConfirmationChain(record) - println(" 7. verifyConfirmationChain $chainResult") - chainResult - } else Ok(true) + val chainOk = if (!config.chainConfirmationCodes) true else { + val chainErrs = ErrorMessages("") + val ok = encryptionVerifier.verifyConfirmationChain(record, chainErrs) + println(" 7. verifyConfirmationChain $ok") + if (!ok) { + println(chainErrs) + } + ok + } if (record.stage() < ElectionRecord.Stage.TALLIED) { println("election record stage = ${record.stage()}, stopping verification now\n") @@ -73,9 +82,13 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) { } // tally accumulation - val verifyTally = VerifyTally(group, verifyEncryptions.aggregator) - val aggResult = verifyTally.verify(record.encryptedTally()!!, showTime) - println(" 8. verifyBallotAggregation $aggResult") + val tallyVerifier = VerifyTally(group, encryptionVerifier.aggregator) + val tallyErrs = ErrorMessages("") + val tallyOk = tallyVerifier.verify(record.encryptedTally()!!, tallyErrs, showTime) + println(" 8. verifyBallotAggregation $tallyOk") + if (!tallyOk) { + println(tallyErrs) + } if (record.stage() < ElectionRecord.Stage.DECRYPTED) { println("election record stage = ${record.stage()}, stopping verification now\n") @@ -83,18 +96,25 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) { } // tally decryption - val verifyDecryption = VerifyDecryption(group, manifest, jointPublicKey, He) - val tallyResult = verifyDecryption.verify(record.decryptedTally()!!, isBallot = false, stats) - println(" 9,10,11. verifyTallyDecryption $tallyResult") + val decryptionVerifier = VerifyDecryption(group, manifest, jointPublicKey, He) + val tdErrs = ErrorMessages("") + val tdOk = decryptionVerifier.verify(record.decryptedTally()!!, isBallot = false, tdErrs, stats) + println(" 9,10,11. verifyTallyDecryption $tdOk") + if (!tdOk) { + println(tdErrs) + } // 12, 13, 14 spoiled ballots - val spoiledResult = - verifyDecryption.verifySpoiledBallotTallies(record.decryptedBallots(), nthreads, stats, showTime) - println(" 12,13,14. verifySpoiledBallotTallies $spoiledResult") + val spoiledErrs = ErrorMessages("") + val spoiledOk = + decryptionVerifier.verifySpoiledBallotTallies(record.decryptedBallots(), nthreads, spoiledErrs, stats, showTime) + println(" 12,13,14. verifySpoiledBallotTallies $spoiledOk") + if (!spoiledOk) { + println(spoiledErrs) + } val allOk = (parametersOk is Ok) && (guardiansOk is Ok) && (publicKeyOk is Ok) && (baseHashOk is Ok) && - (ballotResult is Ok) && - (chainResults is Ok) && (aggResult is Ok) && (tallyResult is Ok) && (spoiledResult is Ok) + ballotsOk && chainOk && tallyOk && tdOk && spoiledOk println("verify allOK = $allOk\n") return allOk } @@ -192,24 +212,32 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) { return errors.merge() } - fun verifyEncryptedBallots(stats : Stats): Result { + fun verifyEncryptedBallots(stats : Stats): ErrorMessages { + val errs = ErrorMessages("verifyDecryptedTally") val verifyBallots = VerifyEncryptedBallots(group, manifest, jointPublicKey, He, record.config(), nthreads) - return verifyBallots.verifyBallots(record.encryptedAllBallots { true }, stats) + verifyBallots.verifyBallots(record.encryptedAllBallots { true }, errs, stats) + return errs } - fun verifyEncryptedBallots(ballots: Iterable, stats : Stats): Result { + fun verifyEncryptedBallots(ballots: Iterable, stats : Stats): ErrorMessages { + val errs = ErrorMessages("verifyDecryptedTally") val verifyBallots = VerifyEncryptedBallots(group, manifest, jointPublicKey, He, record.config(), nthreads) - return verifyBallots.verifyBallots(ballots, stats) + verifyBallots.verifyBallots(ballots, errs, stats) + return errs } - fun verifyDecryptedTally(tally: DecryptedTallyOrBallot, stats: Stats): Result { + fun verifyDecryptedTally(tally: DecryptedTallyOrBallot, stats: Stats): ErrorMessages { + val errs = ErrorMessages("verifyDecryptedTally") val verifyTally = VerifyDecryption(group, manifest, jointPublicKey, He) - return verifyTally.verify(tally, false, stats) + verifyTally.verify(tally, false, errs, stats) + return errs } - fun verifySpoiledBallotTallies(stats: Stats): Result { + fun verifySpoiledBallotTallies(stats: Stats): ErrorMessages { + val errs = ErrorMessages("verifyDecryptedTally") val verifyTally = VerifyDecryption(group, manifest, jointPublicKey, He) - return verifyTally.verifySpoiledBallotTallies(record.decryptedBallots(), nthreads, stats, true) + verifyTally.verifySpoiledBallotTallies(record.decryptedBallots(), nthreads, errs, stats, true) + return errs } fun verifyTallyBallotIds(): Boolean { diff --git a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyDecryption.kt b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyDecryption.kt index e419a2c2..20187d27 100644 --- a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyDecryption.kt +++ b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyDecryption.kt @@ -1,9 +1,9 @@ package electionguard.verifier -import com.github.michaelbull.result.* import electionguard.ballot.DecryptedTallyOrBallot import electionguard.ballot.ManifestIF import electionguard.core.* +import electionguard.util.ErrorMessages import electionguard.util.Stats import kotlin.collections.mutableSetOf import kotlinx.coroutines.CoroutineScope @@ -16,7 +16,6 @@ import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.yield import kotlin.math.roundToInt @@ -32,88 +31,96 @@ class VerifyDecryption( val extendedBaseHash: UInt256, ) { - /** Set of "contestId/selectionId" string to detect existence. */ - val contestAndSelectionSet : Set = manifest.contests.map { contest -> contest.selections.map { "${contest.contestId}/${it.selectionId}" } }.flatten().toSet() + /** All manifest "contestId/selectionId" strings. */ + val contestAndSelectionSet : Set = manifest.contests.map { contest -> contest.selections.map { + "${contest.contestId}/${it.selectionId}" } }.flatten().toSet() - fun verify(decrypted: DecryptedTallyOrBallot, isBallot: Boolean, stats: Stats): Result { + fun verify(decrypted: DecryptedTallyOrBallot, isBallot: Boolean, errs: ErrorMessages, stats: Stats): Boolean { val starting = getSystemTimeInMillis() if (decrypted.electionId != extendedBaseHash) { - return Err("DecryptedTallyOrBallot has wrong electionId = ${decrypted.electionId}") + errs.add("DecryptedTallyOrBallot has wrong electionId = ${decrypted.electionId}") + return false } var nselections = 0 - val results = mutableListOf>() - val ballotSelectionSet = mutableSetOf() for (contest in decrypted.contests) { - val where = "${decrypted.id}/${contest.contestId}" - // (10.B) The contest text label occurs as a contest label in the list of contests in the election manifest. - if (manifest.contestLimit(contest.contestId) == null) { // TODO - results.add(Err(" 10.B,13.D Ballot contains contest not in manifest: '$where' ")) + val cerrs = errs.nested("Contest ${contest.contestId}") + + // (10.B, 13.D) The contest text label occurs as a contest label in the contests in the election manifest. + if (manifest.findContest(contest.contestId) == null) { + val what = if (isBallot) "13.D" else "10.B" + cerrs.add(" $what contest '${contest.contestId}' not in manifest") continue } + val optionLimit = manifest.optionLimit(contest.contestId) val contestLimit = manifest.contestLimit(contest.contestId) if (contest.decryptedContestData != null) { - verifyContestData(where, contest.decryptedContestData) + verifyContestData(contest.decryptedContestData, errs) } + val contestSelectionSet = mutableSetOf() var contestVotes = 0 for (selection in contest.selections) { + val serrs = cerrs.nested("Selection ${selection.selectionId}") + nselections++ val here = "${contest.contestId}/${selection.selectionId}" - val where2 = "$${decrypted.id}/$here" - ballotSelectionSet.add(here) + contestSelectionSet.add(selection.selectionId) - // (10.C) For each option in the contest, the option text label occurs as an option label for the contest - // in the election manifest. + // (10.C, 13.E) the option text label occurs as an option label for the contest in the election manifest. if (!contestAndSelectionSet.contains(here)) { - results.add(Err(" 10.C,13.E Ballot contains selection not in manifest: '$where2' ")) + val what = if (isBallot) "13.E" else "10.C" + serrs.add(" $what contest/selection '$here' not in manifest") continue } if (!selection.proof.r.inBounds()) { - results.add(Err(" 9.A,12.A response out of bounds: '$where2' ")) + val what = if (isBallot) "12.A" else "9.A" + serrs.add(" $what response out of bounds") } // 9.B, 11.B if (!selection.verifySelection()) { - results.add(Err(" 9.B,12.B Challenge does not match: '$where2' ")) + val what = if (isBallot) "12.B" else "9.B" + serrs.add(" $what challenge does not match") } // T = K^t mod p. val tallyQ = selection.tally.toElementModQ(group) if (selection.bOverM != publicKey powP tallyQ) { - results.add(Err(" 10.A,13.A Tally Decryption M = K^t mod p error: '$where2'")) + if (isBallot) serrs.add(" 10.A incorrect Decryption T = K^t mod p") + else serrs.add(" 13.A incorrect Decryption S = K^sigma mod p") } if (isBallot && (selection.tally !in (0..optionLimit))) { - results.add(Err(" 13.B ballot vote ${selection.tally} must be between 0..$optionLimit : '$where2'")) + serrs.add(" 13.B ballot vote ${selection.tally} must be between 0..$optionLimit") } contestVotes += selection.tally } - if (isBallot) { - if (contestVotes !in (0..contestLimit)) { - results.add(Err(" 13.C sum of votes ${contestVotes} in contest must be between 0..$contestLimit : '$where'")) + + // (10.D) For each option in the election manifest, the option occurs in the decrypted tally contest. + // (13.F) For each option in the election manifest, the option occurs in the decrypted ballot contest. + val mcontest = manifest.findContest(contest.contestId)!! // already checked existence above + mcontest.selections.forEach { + if (!contestSelectionSet.contains(it.selectionId)) { + if (isBallot) errs.add(" 13.F Manifest contains selection '$it' not on decrypted ballot") + else errs.add(" 10.D Manifest contains selection '$it' not on decrypted tally ") } } - // println(" verify $nselections on ${if (isBallot) "ballot" else "tally"} ${decrypted.id}") - } - // (10.D) For each option text label listed for this contest in the election manifest, the option label - // occurs for a option in the decrypted tally contest. - // (13.F) For each option text label listed for this contest in the election manifest, the option label - //occurs for a option in the decrypted spoiled ballot. - contestAndSelectionSet.forEach { - if (!ballotSelectionSet.contains(it)) { - results.add(Err(" 10.D,13.F Manifest contains selection not in ballot: '$it' ")) + if (isBallot) { + if (contestVotes !in (0..contestLimit)) { + cerrs.add(" 13.C sum of votes ${contestVotes} in contest must be between 0..$contestLimit") + } } } stats.of("verifyDecryption", "selections").accum(getSystemTimeInMillis() - starting, nselections) - return results.merge() + return !errs.hasErrors() } // Verification 9 (Correctness of tally decryptions) @@ -132,19 +139,14 @@ class VerifyDecryption( // An election verifier must then confirm the following. // (11.A) The given value v is in the set Zq . // (11.B) The challenge value c satisfies c = H(HE ; 0x31, K, C0 , C1 , C2 , a, b, β). - private fun verifyContestData(where: String, decryptedContestData: DecryptedTallyOrBallot.DecryptedContestData): Result { - val results = mutableListOf>() - + private fun verifyContestData(decryptedContestData: DecryptedTallyOrBallot.DecryptedContestData, errs: ErrorMessages){ // (11.A,14.A) The given value v is in the set Zq. if (!decryptedContestData.proof.r.inBounds()) { - results.add(Err(" (11.A,14.A) The value v is not in the set Zq.: '$where'")) + errs.add(" (11.A,14.A) The value v is not in the set Zq.") } - - val challengeOk = decryptedContestData.proof.verifyContestDataDecryption(publicKey.key, extendedBaseHash, decryptedContestData.beta, decryptedContestData.encryptedContestData) - if (challengeOk) { - results.add(Err(" (11.B,14.B) The challenge value is wrong: '$where'")) + if (!decryptedContestData.proof.verifyContestDataDecryption(publicKey.key, extendedBaseHash, decryptedContestData.beta, decryptedContestData.encryptedContestData)) { + errs.add(" (11.B,14.B) The challenge value is wrong") } - return results.merge() } //////////////////////////////////////////////////////////////////////////////// @@ -153,18 +155,21 @@ class VerifyDecryption( fun verifySpoiledBallotTallies( ballots: Iterable, nthreads: Int, + errs: ErrorMessages, stats: Stats, showTime: Boolean, - ): Result { + ): Boolean { val starting = getSystemTimeInMillis() runBlocking { val verifierJobs = mutableListOf() val ballotProducer = produceTallies(ballots) repeat(nthreads) { - verifierJobs.add(launchVerifier(it, ballotProducer) { ballot -> verify(ballot, isBallot = true, stats) }) + // decrypted: DecryptedTallyOrBallot, isBallot: Boolean, errs: ErrorMessages, stats: Stats) + verifierJobs.add(launchVerifier(ballotProducer) { dballot -> + verify(dballot, isBallot = true, errs.nested("Ballot ${dballot.id}"), stats) + }) } - // wait for all encryptions to be done, then close everything joinAll(*verifierJobs.toTypedArray()) } @@ -172,10 +177,9 @@ class VerifyDecryption( val took = getSystemTimeInMillis() - starting val perBallot = if (count == 0) 0 else (took.toDouble() / count).roundToInt() if (showTime) println(" verifySpoiledBallotTallies took $took millisecs for $count ballots = $perBallot msecs/ballot wallclock") - return globalResults.merge() + return !errs.hasErrors() } - private val globalResults = mutableListOf>() private var count = 0 private fun CoroutineScope.produceTallies(producer: Iterable): ReceiveChannel = produce { @@ -190,16 +194,11 @@ class VerifyDecryption( private val mutex = Mutex() private fun CoroutineScope.launchVerifier( - id: Int, input: ReceiveChannel, - verify: (DecryptedTallyOrBallot) -> Result, + verify: (DecryptedTallyOrBallot) -> Boolean ) = launch(Dispatchers.Default) { - for (tally in input) { - if (debug) println("$id channel working on ${tally.id}") - val stat = verify(tally) - mutex.withLock { - globalResults.add(stat) - } + for (dballot in input) { + verify(dballot) yield() } } diff --git a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyEncryptedBallots.kt b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyEncryptedBallots.kt index ec6aacd2..32b47640 100644 --- a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyEncryptedBallots.kt +++ b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyEncryptedBallots.kt @@ -1,8 +1,6 @@ package electionguard.verifier import com.github.michaelbull.result.Err -import com.github.michaelbull.result.Ok -import com.github.michaelbull.result.Result import com.github.michaelbull.result.unwrap import electionguard.ballot.ElectionConfig import electionguard.ballot.EncryptedBallot @@ -10,6 +8,7 @@ import electionguard.ballot.EncryptedBallotChain import electionguard.ballot.ManifestIF import electionguard.core.* import electionguard.publish.ElectionRecord +import electionguard.util.ErrorMessages import electionguard.util.Stats import electionguard.util.sigfig import kotlinx.coroutines.CoroutineScope @@ -41,9 +40,10 @@ class VerifyEncryptedBallots( fun verifyBallots( ballots: Iterable, + errs: ErrorMessages, stats: Stats = Stats(), showTime: Boolean = false - ): Result { + ): Boolean { val starting = getSystemTimeInMillis() runBlocking { @@ -55,7 +55,7 @@ class VerifyEncryptedBallots( it, ballotProducer, aggregator - ) { ballot -> verifyEncryptedBallot(ballot, stats) }) + ) { ballot -> verifyEncryptedBallot(ballot, errs.nested("ballot ${ballot.ballotId}"), stats) }) } // wait for all verifications to be done @@ -67,7 +67,7 @@ class VerifyEncryptedBallots( val checkDuplicates = mutableMapOf() confirmationCodes.forEach { if (checkDuplicates[it.code] != null) { - allResults.add(Err(" 7.C, 17.D. Duplicate confirmation code for ballot ${it.ballotId} and ${checkDuplicates[it.code]}")) + errs.add(" 7.C, 17.D. Duplicate confirmation code ${it.code} for ballot ids=${it.ballotId},${checkDuplicates[it.code]}") } checkDuplicates[it.code] = it.ballotId } @@ -77,56 +77,27 @@ class VerifyEncryptedBallots( val perBallot = if (count == 0) 0 else (took.toDouble() / count).sigfig() println(" VerifyEncryptedBallots with $nthreads threads took $took millisecs wallclock for $count ballots = $perBallot msecs/ballot") } - return allResults.merge() + return !errs.hasErrors() } - fun verifyEncryptedBallot(ballot: EncryptedBallot, stats: Stats): Result { + fun verifyEncryptedBallot( + ballot: EncryptedBallot, + errs: ErrorMessages, + stats: Stats + ) : Boolean { val starting = getSystemTimeInMillis() - val results = mutableListOf>() if (ballot.electionId != extendedBaseHash) { - return Err("Encrypted Ballot ${ballot.ballotId} has wrong electionId = ${ballot.electionId}; skipping") + errs.add("Encrypted Ballot ${ballot.ballotId} has wrong electionId = ${ballot.electionId}; skipping") + return false } var ncontests = 0 var nselections = 0 for (contest in ballot.contests) { - val where = "${ballot.ballotId}/${contest.contestId}" ncontests++ nselections += contest.selections.size - - contest.selections.forEach { - results.add(verifySelection(where, it, manifest.optionLimit(contest.contestId))) - } - - // Verification 6 (Adherence to vote limits) - val texts: List = contest.selections.map { it.encryptedVote } - val ciphertextAccumulation: ElGamalCiphertext = texts.encryptedSum()?: 0.encrypt(jointPublicKey) - val cvalid = contest.proof.verify( - ciphertextAccumulation, - this.jointPublicKey, - this.extendedBaseHash, - manifest.contestLimit(contest.contestId) - ) - if (cvalid is Err) { - results.add(Err(" 6. ChaumPedersenProof validation error for $where = ${cvalid.error} ")) - } - - // χl = H(HE ; 0x23, l, K, α1 , β1 , α2 , β2 . . . , αm , βm ) 7.A - val ciphers = mutableListOf() - texts.forEach { - ciphers.add(it.pad) - ciphers.add(it.data) - } - val contestHash = - hashFunction(extendedBaseHash.bytes, 0x23.toByte(), contest.sequenceOrder, jointPublicKey.key, ciphers) - if (contestHash != contest.contestHash) { - results.add(Err(" 7.A. Incorrect contest hash for contest ${contest.contestId} ")) - } - - if (ballot.isPreencrypt) { - results.add(verifyPreencryptionShortCodes(ballot.ballotId, contest)) - } + verifyEncryptedContest(contest, ballot.isPreencrypt, errs.nested("Selection ${contest.contestId}")) } if (!ballot.isPreencrypt) { @@ -134,27 +105,63 @@ class VerifyEncryptedBallots( val contestHashes = ballot.contests.map { it.contestHash } val confirmationCode = hashFunction(extendedBaseHash.bytes, 0x24.toByte(), contestHashes, ballot.codeBaux) if (confirmationCode != ballot.confirmationCode) { - results.add(Err(" 7.B. Incorrect ballot confirmation code for ballot ${ballot.ballotId} ")) + errs.add(" 7.B. Incorrect ballot confirmation code for ballot ${ballot.ballotId} ") } } else { - results.add(verifyPreencryptedCode(ballot)) + verifyPreencryptedCode(ballot, errs) } - // TODO ballot chaining 7.D-G stats.of("verifyEncryptions", "selection").accum(getSystemTimeInMillis() - starting, nselections) if (debugBallots) println(" Ballot '${ballot.ballotId}' ncontests = $ncontests nselections = $nselections") - return results.merge() + + return !errs.hasErrors() + } + + fun verifyEncryptedContest( + contest: EncryptedBallot.Contest, + isPreencrypt: Boolean, + errs: ErrorMessages + ) { + contest.selections.forEach { + verifySelection( it, manifest.optionLimit(contest.contestId), errs.nested("Selection ${it.selectionId}")) + } + + // Verification 6 (Adherence to vote limits) + val texts: List = contest.selections.map { it.encryptedVote } + val ciphertextAccumulation: ElGamalCiphertext = texts.encryptedSum()?: 0.encrypt(jointPublicKey) + val cvalid = contest.proof.verify( + ciphertextAccumulation, + this.jointPublicKey, + this.extendedBaseHash, + manifest.contestLimit(contest.contestId) + ) + if (cvalid is Err) { + errs.add(" 6. ChaumPedersenProof validation error = ${cvalid.error} ") + } + + // χl = H(HE ; 0x23, l, K, α1 , β1 , α2 , β2 . . . , αm , βm ) 7.A + val ciphers = mutableListOf() + texts.forEach { + ciphers.add(it.pad) + ciphers.add(it.data) + } + val contestHash = + hashFunction(extendedBaseHash.bytes, 0x23.toByte(), contest.sequenceOrder, jointPublicKey.key, ciphers) + if (contestHash != contest.contestHash) { + errs.add(" 7.A. Incorrect contest hash") + } + + if (isPreencrypt) { + verifyPreencryptionShortCodes(contest, errs) + } } // Verification 5 (Well-formedness of selection encryptions) private fun verifySelection( - where: String, selection: EncryptedBallot.Selection, - optionLimit: Int - ): Result { - val errors = mutableListOf>() - val here = "${where}/${selection.selectionId}" - + optionLimit: Int, + errs: ErrorMessages + ) { val svalid = selection.proof.verify( selection.encryptedVote, this.jointPublicKey, @@ -162,22 +169,20 @@ class VerifyEncryptedBallots( optionLimit, ) if (svalid is Err) { - errors.add(Err(" 5. ChaumPedersenProof validation error for ${here}} = ${svalid.error} ")) + errs.add(" 5. ChaumPedersenProof validation error = ${svalid.error} ") } - return errors.merge() } ////////////////////////////////////////////////////////////////////////////// // ballot chaining, section 7 - fun verifyConfirmationChain(consumer: ElectionRecord): Result { - val results = mutableListOf>() + fun verifyConfirmationChain(consumer: ElectionRecord, errs: ErrorMessages): Boolean { consumer.encryptingDevices().forEach { device -> // println("verifyConfirmationChain device=$device") val ballotChainResult = consumer.readEncryptedBallotChain(device) if (ballotChainResult is Err) { - results.add(Err(ballotChainResult.toString())) + errs.add(ballotChainResult.toString()) } else { val ballotChain: EncryptedBallotChain = ballotChainResult.unwrap() val ballots = consumer.encryptedBallots(device) { true } @@ -194,7 +199,7 @@ class VerifyEncryptedBallots( val expectedBaux = if (first) H0 else prevCC + config.configBaux0 // eq 7.D and 7.E first = false if (!expectedBaux.contentEquals(ballot.codeBaux)) { - results.add(Err(" 7.E. additional input byte array Baux != H(Bj−1 ) ∥ Baux,0 for ballot=${ballot.ballotId}")) + errs.add(" 7.E. additional input byte array Baux != H(Bj−1 ) ∥ Baux,0 for ballot=${ballot.ballotId}") } prevCC = ballot.confirmationCode.bytes } @@ -205,16 +210,15 @@ class VerifyEncryptedBallots( // 7.G The closing hash is correctly computed as H = H(HE ; 0x24, Baux ) val expectedClosingHash = hashFunction(extendedBaseHash.bytes, 0x24.toByte(), bauxFinal) if (expectedClosingHash != ballotChain.closingHash) { - results.add(Err(" 7.G. The closing hash is not equal to H = H(HE ; 24, bauxFinal ) for encrypting device=$device")) + errs.add(" 7.G. The closing hash is not equal to H = H(HE ; 24, bauxFinal ) for encrypting device=$device") } } } - return results.merge() + return !errs.hasErrors() } ////////////////////////////////////////////////////////////// // coroutines - private val allResults = mutableListOf>() private var count = 0 private fun CoroutineScope.produceBallots(producer: Iterable): ReceiveChannel = produce { @@ -233,17 +237,16 @@ class VerifyEncryptedBallots( id: Int, input: ReceiveChannel, aggregator: SelectionAggregator, - verify: (EncryptedBallot) -> Result, + verify: (EncryptedBallot) -> Boolean, ) = launch(Dispatchers.Default) { for (ballot in input) { if (debugBallots) println("$id channel working on ${ballot.ballotId}") val result = verify(ballot) mutex.withLock { - if (result is Ok) { + if (result) { aggregator.add(ballot) // this slows down the ballot parallelism: nselections * (2 (modP multiplication)) confirmationCodes.add(ConfirmationCode(ballot.ballotId, ballot.confirmationCode)) } - allResults.add(result) } yield() } diff --git a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyPreEncryptedBallots.kt b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyPreEncryptedBallots.kt index d09b2874..54214e8e 100644 --- a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyPreEncryptedBallots.kt +++ b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyPreEncryptedBallots.kt @@ -1,10 +1,8 @@ package electionguard.verifier -import com.github.michaelbull.result.Err -import com.github.michaelbull.result.Ok -import com.github.michaelbull.result.Result import electionguard.ballot.EncryptedBallot import electionguard.core.* +import electionguard.util.ErrorMessages ////////////////////////////////////////////////////////////////////////////// // pre-encryption @@ -48,18 +46,16 @@ record as uncast. // Specifically, for cast ballots, this includes all short codes that are published in the election record // whose associated selection hashes correspond to selection vectors that are accumulated to form // tallies. For spoiled ballots, this includes all selection vectors on the ballot. -// (18.B) An election verifier must also confirm that for contests with selection limit greater than 1, the se- -// lection vectors published in the election record match the product of the pre-encryptions associated +// (18.B) An election verifier must also confirm that for contests with selection limit greater than 1, +// the selection vectors published in the election record match the product of the pre-encryptions associated // with the short codes listed as selected. fun VerifyEncryptedBallots.verifyPreencryptionShortCodes( - ballotId: String, - contest: EncryptedBallot.Contest -): Result { - val results = mutableListOf>() - + contest: EncryptedBallot.Contest, + errs: ErrorMessages, +) { if (contest.preEncryption == null) { - results.add(Err(" 18. Contest ${contest.contestId} for preencrypted '${ballotId}' has no preEncryption")) - return results.merge() + errs.add(" 18. Contest has no preEncryption") + return } val cv = contest.preEncryption val contestLimit = manifest.contestLimit(contest.contestId) @@ -71,7 +67,7 @@ fun VerifyEncryptedBallots.verifyPreencryptionShortCodes( // All short codes on the ballot are correctly computed from the pre-encrypted selections associated with each short code cv.selectedVectors.forEach { sv -> if (sv.shortCode != sigma(sv.selectionHash)) { - results.add(Err(" 18.A Contest ${contest.contestId} shortCode '${sv.shortCode}' has no match")) + errs.add(" 18.A Contest ${contest.contestId} shortCode '${sv.shortCode}' has no match") } } @@ -87,11 +83,9 @@ fun VerifyEncryptedBallots.verifyPreencryptionShortCodes( val compList = cv.selectedVectors.map { it.encryptions[idx] } val sum = compList.encryptedSum()?: 0.encrypt(jointPublicKey) if (sum != selectionVector[idx]) { - results.add(Err(" 18.B Contest ${contest.contestId} (contestLimit=$contestLimit) selectionVector $idx does not match product")) + errs.add(" 18.B Contest ${contest.contestId} (contestLimit=$contestLimit) selectionVector $idx does not match product") } } - - return results.merge() } // Verification 17 (Validation of confirmation codes in pre-encrypted ballots) @@ -108,13 +102,13 @@ fun VerifyEncryptedBallots.verifyPreencryptionShortCodes( // array Baux as H(B) = H(HE ; 0x42, χ1 , χ2 , . . . , χmB , Baux ). // (17.D) There are no duplicate confirmation codes, i.e. among the set of submitted (cast and chal- // lenged) ballots, no two have the same confirmation code. -fun VerifyEncryptedBallots.verifyPreencryptedCode(ballot: EncryptedBallot): Result { - val errors = mutableListOf() - +fun VerifyEncryptedBallots.verifyPreencryptedCode(ballot: EncryptedBallot, + errs: ErrorMessages, +): Boolean { val contestHashes = mutableListOf() for (contest in ballot.contests) { if (contest.preEncryption == null) { - errors.add(" 17. Contest ${contest.contestId} for preencrypted '${ballot.ballotId}' has no preEncryption") + errs.add(" 17. Contest ${contest.contestId} for preencrypted '${ballot.ballotId}' has no preEncryption") continue } val cv = contest.preEncryption @@ -122,7 +116,7 @@ fun VerifyEncryptedBallots.verifyPreencryptedCode(ballot: EncryptedBallot): Resu val hashVector: List = sv.encryptions.map { listOf(it.pad, it.data) }.flatten() val selectionHash = hashFunction(extendedBaseHash.bytes, 0x40.toByte(), jointPublicKey.key, hashVector) if (selectionHash != sv.selectionHash) { - errors.add(" 17.A. Incorrect selectionHash for selection shortCode=${sv.shortCode} contest=${contest.contestId} ballot='${ballot.ballotId}' ") + errs.add(" 17.A. Incorrect selectionHash for selection shortCode=${sv.shortCode} contest=${contest.contestId} ballot='${ballot.ballotId}' ") } } @@ -135,15 +129,15 @@ fun VerifyEncryptedBallots.verifyPreencryptedCode(ballot: EncryptedBallot): Resu cv.allSelectionHashes ) if (preencryptionHash != cv.preencryptionHash) { - errors.add(" 17.B. Incorrect contestHash for ${contest.contestId} ballot='${ballot.ballotId}' ") + errs.add(" 17.B. Incorrect contestHash for ${contest.contestId} ballot='${ballot.ballotId}' ") } contestHashes.add(preencryptionHash) } val confirmationCode = hashFunction(extendedBaseHash.bytes, 0x42.toByte(), contestHashes, ballot.codeBaux) if (confirmationCode != ballot.confirmationCode) { - errors.add(" 17.C. Incorrect confirmationCode ballot='${ballot.ballotId}' ") + errs.add(" 17.C. Incorrect confirmationCode ballot='${ballot.ballotId}' ") } - return if (errors.isEmpty()) Ok(true) else Err(errors.joinToString("\n")) + return !errs.hasErrors() } \ No newline at end of file diff --git a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyTally.kt b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyTally.kt index 4083c270..377666b5 100644 --- a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyTally.kt +++ b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyTally.kt @@ -1,10 +1,9 @@ package electionguard.verifier -import com.github.michaelbull.result.Err -import com.github.michaelbull.result.Result import electionguard.ballot.EncryptedBallot import electionguard.ballot.EncryptedTally import electionguard.core.* +import electionguard.util.ErrorMessages /** * Verification 8 (Correctness of ballot aggregation) @@ -22,10 +21,9 @@ class VerifyTally( val aggregator: SelectionAggregator, ) { - fun verify(encryptedTally: EncryptedTally, showTime: Boolean = false): Result { + fun verify(encryptedTally: EncryptedTally, errs: ErrorMessages, showTime: Boolean = false): Boolean { val starting = getSystemTimeInMillis() - val errors = mutableListOf>() var nselections = 0 for (contest in encryptedTally.contests) { for (selection in contest.selections) { @@ -36,15 +34,15 @@ class VerifyTally( val accum = aggregator.getAggregateFor(key) if (accum != null) { if (selection.encryptedVote.pad != accum.pad) { - errors.add(Err(" 8.A Ballot Aggregation does not match: $key")) + errs.add(" 8.A Ballot Aggregation does not match: $key") } if (selection.encryptedVote.data != accum.data) { - errors.add(Err(" 8.B Ballot Aggregation does not match: $key")) + errs.add(" 8.B Ballot Aggregation does not match: $key") } } else { // TODO what is it? is it needed? left over from placeholders ?? if (selection.encryptedVote.pad != group.ZERO_MOD_P || selection.encryptedVote.data != group.ZERO_MOD_P) { - errors.add(Err(" Ballot Aggregation empty does not match $key")) + errs.add(" Ballot Aggregation empty does not match $key") } } } @@ -54,14 +52,14 @@ class VerifyTally( // label occurs in the list of contests in the corresponding tally.. aggregator.contestIdSet.forEach { contestId -> if (null == encryptedTally.contests.find { it.contestId == contestId}) { - errors.add(Err(" 10.E Contest '$contestId' found in cast ballots not found in tally")) + errs.add(" 10.E Contest '$contestId' found in cast ballots not found in tally") } } val took = getSystemTimeInMillis() - starting if (showTime) println(" VerifyAggregation took $took millisecs") - return errors.merge() + return !errs.hasErrors() } } diff --git a/egklib/src/commonTest/kotlin/electionguard/decryptBallot/EncryptDecryptBallotTest.kt b/egklib/src/commonTest/kotlin/electionguard/decryptBallot/EncryptDecryptBallotTest.kt index e6efba1d..61607ae7 100644 --- a/egklib/src/commonTest/kotlin/electionguard/decryptBallot/EncryptDecryptBallotTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/decryptBallot/EncryptDecryptBallotTest.kt @@ -1,7 +1,5 @@ package electionguard.decryptBallot -import com.github.michaelbull.result.Err -import com.github.michaelbull.result.Ok import com.github.michaelbull.result.unwrap import electionguard.ballot.* import electionguard.core.* @@ -18,10 +16,7 @@ import electionguard.util.ErrorMessages import electionguard.util.Stats import electionguard.verifier.VerifyDecryption import kotlin.math.roundToInt -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertTrue +import kotlin.test.* /** Test KeyCeremony Trustee generation and recovered decryption. */ class EncryptDecryptBallotTest { @@ -176,11 +171,9 @@ fun testEncryptDecryptVerify( } } - val result = verifier.verify(decryptedBallot, true, Stats()) - if (result is Err) { - println(result) - } - assertTrue( result is Ok) + verifier.verify(decryptedBallot, true, errs.nested("verify"), Stats()) + println(errs) + assertFalse(errs.hasErrors()) } } diff --git a/egklib/src/commonTest/kotlin/electionguard/encrypt/AddEncryptedBallotTest.kt b/egklib/src/commonTest/kotlin/electionguard/encrypt/AddEncryptedBallotTest.kt index 91130e24..32786fb2 100644 --- a/egklib/src/commonTest/kotlin/electionguard/encrypt/AddEncryptedBallotTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/encrypt/AddEncryptedBallotTest.kt @@ -1,7 +1,6 @@ package electionguard.encrypt import com.github.michaelbull.result.Err -import com.github.michaelbull.result.Ok import com.github.michaelbull.result.unwrap import electionguard.ballot.EncryptedBallot import electionguard.core.* @@ -12,10 +11,7 @@ import electionguard.publish.readElectionRecord import electionguard.util.ErrorMessages import electionguard.util.Stats import electionguard.verifier.VerifyEncryptedBallots -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertTrue +import kotlin.test.* class AddEncryptedBallotTest { val group = productionGroup() @@ -342,17 +338,16 @@ fun checkOutput(group : GroupContext, outputDir: String, expectedCount: Int, cha record.config(), 1) val stats = Stats() - val ballotResult = verifyEncryptions.verifyBallots(record.encryptedAllBallots { true }, stats) - println("verifyBallots = $ballotResult") - assertTrue( ballotResult is Ok) + val errs = ErrorMessages("verifyBallots") + verifyEncryptions.verifyBallots(record.encryptedAllBallots { true }, errs, stats) + println(errs) + assertFalse( errs.hasErrors()) assertEquals( expectedCount, stats.count()) if (chained) { - val result = verifyEncryptions.verifyConfirmationChain(record) - if (result is Err) { - println("FAIL $result") - } - println("verifyConfirmationChain = $ballotResult") - assertTrue(result is Ok) + val chainErrs = ErrorMessages("verifyConfirmationChain") + verifyEncryptions.verifyConfirmationChain(record, chainErrs) + println(chainErrs) + assertFalse( chainErrs.hasErrors()) } } \ No newline at end of file diff --git a/egklib/src/commonTest/kotlin/electionguard/preencrypt/PreEncryptorTest.kt b/egklib/src/commonTest/kotlin/electionguard/preencrypt/PreEncryptorTest.kt index 945ba5a6..09697a82 100644 --- a/egklib/src/commonTest/kotlin/electionguard/preencrypt/PreEncryptorTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/preencrypt/PreEncryptorTest.kt @@ -246,14 +246,13 @@ internal fun runComplete( } // verify + val verifyErrs = ErrorMessages("verifyEncryptedBallot") val stats = Stats() val fakeConfig = makeFakeConfig() val verifier = VerifyEncryptedBallots(group, manifest, ElGamalPublicKey(publicKey), qbar, fakeConfig, 1) - val results = verifier.verifyEncryptedBallot(fullEncryptedBallot, stats) - if (show || results !is Ok) { - println("VerifyEncryptedBallots $results\n") - } + verifier.verifyEncryptedBallot(fullEncryptedBallot, verifyErrs, stats) + println(errs) // decrypt with nonce val decryptionWithPrimaryNonce = DecryptPreencryptWithNonce(group, manifest, ElGamalPublicKey(publicKey), qbar, ::sigma) @@ -297,7 +296,7 @@ internal fun runComplete( } } - assertTrue(results is Ok) + assertFalse(errs.hasErrors()) } fun sigma(hash: UInt256): String = hash.toHex().substring(0, 5) diff --git a/egklib/src/commonTest/kotlin/electionguard/verifier/AttackEncryptedBallotTest.kt b/egklib/src/commonTest/kotlin/electionguard/verifier/AttackEncryptedBallotTest.kt index f62c1388..3bfae9cc 100644 --- a/egklib/src/commonTest/kotlin/electionguard/verifier/AttackEncryptedBallotTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/verifier/AttackEncryptedBallotTest.kt @@ -15,8 +15,8 @@ import electionguard.publish.readElectionRecord import electionguard.tally.AccumulateTally import electionguard.util.ErrorMessages import electionguard.util.Stats +import kotlin.test.assertFalse import kotlin.test.assertNotEquals -import kotlin.test.assertTrue /** An attack that modifies the EncryptedBallots before the tally phase. @@ -81,7 +81,7 @@ class AttackEncryptedBallotTest { val results = verifier.verifyEncryptedBallots(mungedBallots, stats) println("verify = ${results}") stats.show() - assertTrue(results is Ok) + assertFalse(results.hasErrors()) } private fun mungeBallot(ballot: EncryptedBallot, publicKey: ElGamalPublicKey): EncryptedBallot { diff --git a/egklib/src/commonTest/kotlin/electionguard/verifier/DuplicateTrackingCodesTest.kt b/egklib/src/commonTest/kotlin/electionguard/verifier/DuplicateTrackingCodesTest.kt index 92adbaf6..c4658880 100644 --- a/egklib/src/commonTest/kotlin/electionguard/verifier/DuplicateTrackingCodesTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/verifier/DuplicateTrackingCodesTest.kt @@ -1,12 +1,11 @@ package electionguard.verifier -import com.github.michaelbull.result.Err import electionguard.ballot.EncryptedBallot import electionguard.util.Stats import electionguard.core.productionGroup import electionguard.publish.readElectionRecord +import org.junit.jupiter.api.Assertions.assertTrue import kotlin.test.Test -import kotlin.test.assertTrue class DuplicateTrackingCodesTest { private val inputDir = "src/commonTest/data/workflow/allAvailableProto" @@ -33,6 +32,7 @@ class DuplicateTrackingCodesTest { // verify duplicate ballots fail val verifier = Verifier(electionRecord) val results = verifier.verifyEncryptedBallots(mungedBallots, Stats()) - assertTrue(results is Err) + println(results) + assertTrue(results.hasErrors()) } } \ No newline at end of file diff --git a/egklib/src/jvmMain/kotlin/electionguard/cli/RunBatchEncryption.kt b/egklib/src/jvmMain/kotlin/electionguard/cli/RunBatchEncryption.kt index f75df9cb..26e140f1 100644 --- a/egklib/src/jvmMain/kotlin/electionguard/cli/RunBatchEncryption.kt +++ b/egklib/src/jvmMain/kotlin/electionguard/cli/RunBatchEncryption.kt @@ -289,8 +289,8 @@ class RunBatchEncryption { val ciphertextBallot = ciphertextBallotMaybe!! // experiments in testing the encryption + val errs2 = ErrorMessages("Ballot ${ballot.ballotId}") if (check == CheckType.EncryptTwice) { - val errs2 = ErrorMessages("Ballot ${ballot.ballotId}") val encrypted2 = encryptor.encrypt(ballot, config.configBaux0, errs2, ciphertextBallot.ballotNonce)!! if (encrypted2.confirmationCode != ciphertextBallot.confirmationCode) { logger.warn { "CheckType.EncryptTwice: encrypted.confirmationCode doesnt match" } @@ -301,9 +301,9 @@ class RunBatchEncryption { } else if (check == CheckType.Verify && verifier != null) { // VerifyEncryptedBallots may be doing more work than actually needed val submitted = ciphertextBallot.submit(EncryptedBallot.BallotState.CAST) - val verifyResults = verifier.verifyEncryptedBallot(submitted, Stats()) - if (verifyResults is Err) { - logger.warn { "CheckType.Verify: encrypted doesnt verify = ${verifyResults}" } + val verifyOk = verifier.verifyEncryptedBallot(submitted, errs2, Stats()) + if (!verifyOk) { + logger.warn { "CheckType.Verify: encrypted doesnt verify = ${errs2}" } } } else if (check == CheckType.DecryptNonce) { // Decrypt with Nonce to ensure encryption worked diff --git a/egklib/src/jvmMain/kotlin/electionguard/cli/RunExampleEncryption.kt b/egklib/src/jvmMain/kotlin/electionguard/cli/RunExampleEncryption.kt index d068d1e1..d42310d7 100644 --- a/egklib/src/jvmMain/kotlin/electionguard/cli/RunExampleEncryption.kt +++ b/egklib/src/jvmMain/kotlin/electionguard/cli/RunExampleEncryption.kt @@ -101,12 +101,14 @@ class RunExampleEncryption { ) // Note we are verifying all ballots, not just CAST - val verifierResult = verifier.verifyBallots(record.encryptedAllBallots { true }) - println("verifyEncryptedBallots: $verifierResult") + val errs = ErrorMessages("verifyBallots") + verifier.verifyBallots(record.encryptedAllBallots { true }, errs) + println("verifyEncryptedBallots: $errs") if (chained) { - val chainResult = verifier.verifyConfirmationChain(record) - println(" verifyChain: $chainResult") + val chainErrs = ErrorMessages("verifyConfirmationChain") + verifier.verifyConfirmationChain(record, chainErrs) + println(" verifyChain: $chainErrs") } } } diff --git a/egklib/src/jvmMain/kotlin/electionguard/cli/RunVerifier.kt b/egklib/src/jvmMain/kotlin/electionguard/cli/RunVerifier.kt index 7da5827c..e8fabffd 100644 --- a/egklib/src/jvmMain/kotlin/electionguard/cli/RunVerifier.kt +++ b/egklib/src/jvmMain/kotlin/electionguard/cli/RunVerifier.kt @@ -66,11 +66,12 @@ class RunVerifier { val verifier = Verifier(electionRecord, nthreads) val stats = Stats() - val allOk = verifier.verifyEncryptedBallots(stats) + val errs = verifier.verifyEncryptedBallots(stats) + println(errs) stats.show() val took = ((getSystemTimeInMillis() - starting) / 1000.0).sigfig() - println("VerifyEncryptedBallots $allOk took $took seconds") + println("VerifyEncryptedBallots took $took seconds") } fun verifyDecryptedTally(group: GroupContext, inputDir: String) { @@ -81,11 +82,12 @@ class RunVerifier { val decryptedTally = electionRecord.decryptedTally() ?: throw IllegalStateException("no decryptedTally ") val stats = Stats() - val allOk = verifier.verifyDecryptedTally(decryptedTally, stats) + val errs = verifier.verifyDecryptedTally(decryptedTally, stats) + println(errs) stats.show() val took = ((getSystemTimeInMillis() - starting) / 1000.0).roundToInt() - println("verifyDecryptedTally $allOk took $took seconds wallclock") + println("verifyDecryptedTally took $took seconds wallclock") } fun verifyChallengedBallots(group: GroupContext, inputDir: String) { @@ -95,11 +97,12 @@ class RunVerifier { val verifier = Verifier(electionRecord, 1) val stats = Stats() - val allOk = verifier.verifySpoiledBallotTallies(stats) + val errs = verifier.verifySpoiledBallotTallies(stats) stats.show() + println(errs) val took = ((getSystemTimeInMillis() - starting) / 1000.0).roundToInt() - println("verifyRecoveredShares $allOk took $took seconds wallclock") + println("verifyRecoveredShares took $took seconds wallclock") } fun verifyTallyBallotIds(group: GroupContext, inputDir: String) { @@ -107,8 +110,8 @@ class RunVerifier { println("$inputDir stage=${electionRecord.stage()} ncast_ballots=${electionRecord.encryptedTally()!!.castBallotIds.size}") val verifier = Verifier(electionRecord, 1) - val allOk = verifier.verifyTallyBallotIds() - println(" verifyTallyBallotIds= $allOk") + val errs = verifier.verifyTallyBallotIds() + println(errs) } } } diff --git a/egklib/src/jvmTest/kotlin/electionguard/testvectors/EncryptedBallotJsonV.kt b/egklib/src/jvmTest/kotlin/electionguard/testvectors/EncryptedBallotJsonV.kt index 56b237b2..4b738378 100644 --- a/egklib/src/jvmTest/kotlin/electionguard/testvectors/EncryptedBallotJsonV.kt +++ b/egklib/src/jvmTest/kotlin/electionguard/testvectors/EncryptedBallotJsonV.kt @@ -91,6 +91,10 @@ class EncryptedBallotJsonManifestFacade(ballot : EncryptedBallotJsonV) : Manifes } override fun contestsForBallotStyle(ballotStyle : String) = contests + override fun findContest(contestId: String): ManifestIF.Contest? { + return contests.find{ it.contestId == contestId } + } + override fun contestLimit(contestId: String): Int { return contests.find{ it.contestId == contestId }?.votesAllowed?: 1 } diff --git a/egklib/src/jvmTest/kotlin/electionguard/testvectors/PlaintextBallotJsonV.kt b/egklib/src/jvmTest/kotlin/electionguard/testvectors/PlaintextBallotJsonV.kt index fd926acb..7c69ca6c 100644 --- a/egklib/src/jvmTest/kotlin/electionguard/testvectors/PlaintextBallotJsonV.kt +++ b/egklib/src/jvmTest/kotlin/electionguard/testvectors/PlaintextBallotJsonV.kt @@ -57,6 +57,10 @@ class PlaintextBallotJsonManifestFacade(ballot : PlaintextBallotJsonV) : Manifes } override fun contestsForBallotStyle(ballotStyle : String) = contests + override fun findContest(contestId: String): ManifestIF.Contest? { + return contests.find{ it.contestId == contestId } + } + override fun contestLimit(contestId: String): Int { return contests.find{ it.contestId == contestId }?.votesAllowed?: 1 }