diff --git a/dependencies.txt b/dependencies.txt new file mode 100644 index 00000000..8d9f8fcb --- /dev/null +++ b/dependencies.txt @@ -0,0 +1,20 @@ +org.jetbrains.kotlinx:kotlinx-cli:0.3.6 +org.jetbrains.kotlinx:kotlinx-datetime:0.4.1 +org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0 +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4 + org.jetbrains.kotlinx:atomicfu:0.20.2 +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 + org.jetbrains:annotations:13.0 + +com.michael-bull.kotlin-result:kotlin-result:1.1.18 + +io.github.microutils:kotlin-logging:3.0.5 + org.slf4j:slf4j-api:2.0.3 + +pro.streem.pbandk:pbandk-runtime:0.14.2 + ++--- ch.qos.logback:logback-classic:1.4.11 +| +--- ch.qos.logback:logback-core:1.4.11 +| \--- org.slf4j:slf4j-api:2.0.7 + + diff --git a/docs/CommandLineInterface.md b/docs/CommandLineInterface.md index 6441ed8b..7d49c163 100644 --- a/docs/CommandLineInterface.md +++ b/docs/CommandLineInterface.md @@ -303,7 +303,7 @@ Example: /usr/lib/jvm/jdk-19/bin/java \ -classpath egklib/egklib-all.jar \ electionguard.cli.RunVerifier \ - -in testOut/cliWorkflow/electionRecord \ + -in testOut/cliWorkflow/electionRecord ```` ## Encrypt plaintext files again for Pep @@ -345,7 +345,7 @@ Example: -in egklib/src/commonTest/data/workflow/allAvailableJson \ -trustees egklib/src/commonTest/data/workflow/allAvailableJson/private_data/trustees \ -scanned testOut/pep/testPepAllJson/encrypted_ballots/scanned \ - -out testOut/pep/testPepAllJson \ + -out testOut/pep/testPepAllJson/pepBallots \ -nthreads 25 ```` @@ -355,7 +355,7 @@ Example: Usage: RunVerifyPep options_list Options: --inputDir, -in -> Directory containing input election record (always required) { String } - --pepDirectory, -pep -> Directory containing PEP output (always required) { String } + --pepBallotDir, -pep -> Directory containing PEP output (always required) { String } --nthreads, -nthreads [11] -> Number of parallel threads to use { Int } --help, -h -> Usage info ```` @@ -366,6 +366,6 @@ Example: /usr/lib/jvm/jdk-19/bin/java \ -classpath egklib/egklib-all.jar \ electionguard.cli.RunVerifyPep \ - -in egklib/src/commonTest/data/workflow/allAvailableJson \ - -pep testOut/pep/testPepAllJson + --inputDir egklib/src/commonTest/data/workflow/allAvailableJson \ + --pepBallotDir testOut/pep/testPepAllJson/pepBallots ```` diff --git a/egklib/egklib-all.jar b/egklib/egklib-all.jar index 89a416f6..748a7cd0 100644 Binary files a/egklib/egklib-all.jar and b/egklib/egklib-all.jar differ diff --git a/egklib/src/commonMain/kotlin/electionguard/core/ChaumPedersen2.kt b/egklib/src/commonMain/kotlin/electionguard/core/ChaumPedersen2.kt index bd805fa1..44b26fd1 100644 --- a/egklib/src/commonMain/kotlin/electionguard/core/ChaumPedersen2.kt +++ b/egklib/src/commonMain/kotlin/electionguard/core/ChaumPedersen2.kt @@ -144,16 +144,15 @@ fun ChaumPedersenRangeProofKnownNonce.verify( val (alpha, beta) = ciphertext results.add( if (alpha.isValidResidue() && beta.isValidResidue()) Ok(true) else - Err(" 5.A,6.A invalid residue: alpha = ${alpha.inBounds()} beta = ${beta.inBounds()}") + Err(" 5.A,6.A values not in Zp^r: alpha = ${alpha.inBounds()} beta = ${beta.inBounds()}") ) val expandedProofs = proofs.mapIndexed { j, proof -> // recomputes all the a and b values val (cj, vj) = proof - results.add( - if (cj.inBounds() && vj.inBounds()) Ok(true) else - Err(" 5.B,6.B c = ${cj.inBounds()} v = ${vj.inBounds()} idx=$j") - ) + if (cj.inBounds() && vj.inBounds()) results.add(Ok(true)) + if (!cj.inBounds()) results.add(Err(" 5.B,6.B cj (idx $j) not in bounds")) + if (!vj.inBounds()) results.add(Err(" 5.C,6.C vj (idx $j) not in bounds")) val wj = (vj - j.toElementModQ(group) * cj) ExpandedChaumPedersenProof( diff --git a/egklib/src/commonMain/kotlin/electionguard/verifier/Verifier.kt b/egklib/src/commonMain/kotlin/electionguard/verifier/Verifier.kt index 01b63962..e359a1bc 100644 --- a/egklib/src/commonMain/kotlin/electionguard/verifier/Verifier.kt +++ b/egklib/src/commonMain/kotlin/electionguard/verifier/Verifier.kt @@ -45,19 +45,21 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) { val publicKeyOk = verifyElectionPublicKey() println(" 3. verifyElectionPublicKey= $publicKeyOk") + val baseHashOk = verifyExtendedBaseHash() + println(" 4. verifyExtendedBaseHAsh= $baseHashOk") + if (record.stage() < ElectionRecord.Stage.ENCRYPTED) { println("election record stage = ${record.stage()}, stopping verification now\n") val took = getSystemTimeInMillis() - starting13 - if (showTime) println(" verify 2,3 took $took millisecs") + if (showTime) println(" verify 2,3,4 took $took millisecs") return true } - // encryption and vote limits 5,6,7 val verifyEncryptions = VerifyEncryptedBallots(group, manifest, jointPublicKey, He, config, nthreads) // Note we are validating all ballots, not just CAST val ballotResult = verifyEncryptions.verifyBallots(record.encryptedAllBallots { true }, stats, showTime) - println(" 5,6,7,16,17. verifyEncryptedBallots $ballotResult") + println(" 5,6,17,18. verifyEncryptedBallots $ballotResult") val chainResults = if (config.chainConfirmationCodes) { val chainResult = verifyEncryptions.verifyConfirmationChain(record) @@ -75,7 +77,7 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) { // tally accumulation, box 7 and 9E val verifyAggregation = VerifyAggregation(group, verifyEncryptions.aggregator) val aggResult = verifyAggregation.verify(record.encryptedTally()!!, showTime) - println(" 7. verifyBallotAggregation $aggResult") + println(" 8. verifyBallotAggregation $aggResult") if (record.stage() < ElectionRecord.Stage.DECRYPTED) { println("election record stage = ${record.stage()}, stopping verification now\n") @@ -85,14 +87,15 @@ 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(" 8,9. verifyTallyDecryption $tallyResult") + println(" 9,10,11. verifyTallyDecryption $tallyResult") - // 10, 11, 12, 13, 14 spoiled ballots + // 12, 13, 14 spoiled ballots val spoiledResult = verifyDecryption.verifySpoiledBallotTallies(record.decryptedBallots(), nthreads, stats, showTime) - println(" 10,11,12,13,14. verifySpoiledBallotTallies $spoiledResult") + println(" 12,13,14. verifySpoiledBallotTallies $spoiledResult") - val allOk = (parametersOk is Ok) && (guardiansOk is Ok) && (publicKeyOk is Ok) && (ballotResult is Ok) && + 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) println("verify allOK = $allOk\n") return allOk @@ -151,15 +154,33 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) { return checkProofs.merge() } - // Verification Box 3 + // Verification 3 (Election public-key validation) + //An election verifier must verify the correct computation of the joint election public key. + //(3.A) The value Ki is in Zpr and Ki ΜΈ= 1 mod p private fun verifyElectionPublicKey(): Result { val errors = mutableListOf>() val guardiansSorted = this.record.guardians().sortedBy { it.xCoordinate } + guardiansSorted.forEach { + val Ki = it.publicKey() + if (!Ki.isValidResidue()) { + errors.add(Err(" 3.A publicKey Ki (${it.guardianId} is not in Zp^r")) + } + if (Ki == group.ONE_MOD_P) { + errors.add(Err(" 3.A publicKey Ki is equal to ONE_MOD_P")) + } + } + val jointPublicKeyComputed = guardiansSorted.map { it.publicKey() }.reduce { a, b -> a * b } if (!jointPublicKey.equals(jointPublicKeyComputed)) { - errors.add(Err(" 3.A jointPublicKey K does not equal computed K = Prod(K_i)")) + errors.add(Err(" 3.B jointPublicKey K does not equal computed K = Prod(K_i)")) } + return errors.merge() + } + + private fun verifyExtendedBaseHash(): Result { + val errors = mutableListOf>() + val guardiansSorted = this.record.guardians().sortedBy { it.xCoordinate } val commitments = mutableListOf() guardiansSorted.forEach { commitments.addAll(it.coefficientCommitments()) } @@ -168,10 +189,8 @@ class Verifier(val record: ElectionRecord, val nthreads: Int = 11) { // He = H(HB ; 0x12, K) ; spec 2.0.0 p.25, eq 23. val computeHe = hashFunction(record.electionBaseHash().bytes, 0x12.toByte(), jointPublicKey.key) if (He != computeHe) { - errors.add(Err(" 3.B extendedBaseHash does not match computed")) - println("extendedBaseHash $He != computed $computeHe") + errors.add(Err(" 4.A extendedBaseHash does not match computed")) } - return errors.merge() } diff --git a/egklib/src/commonTest/kotlin/electionguard/cli/RunTrustedPepBatchTest.kt b/egklib/src/commonTest/kotlin/electionguard/cli/RunTrustedPepBatchTest.kt index 6899e905..d9be05e7 100644 --- a/egklib/src/commonTest/kotlin/electionguard/cli/RunTrustedPepBatchTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/cli/RunTrustedPepBatchTest.kt @@ -80,10 +80,10 @@ class RunTrustedPepBatchTest { ) } - @Test + // @Test fun testVerifyPep() { val inputDir = "src/commonTest/data/workflow/allAvailableJson" - val outputDir = "testOut/pep/testPepAllJson" + val outputDir = "../testOut/pep/testPepAllJson" RunVerifyPep.main( arrayOf( @@ -93,4 +93,5 @@ class RunTrustedPepBatchTest { ) } + }