Skip to content

Commit

Permalink
Use the new encryption API.
Browse files Browse the repository at this point in the history
New test files.
Improve RunVerifyDecryptions.runCompareBallots.
  • Loading branch information
JohnLCaron committed Apr 16, 2024
1 parent 61e5cfb commit 7c43122
Show file tree
Hide file tree
Showing 202 changed files with 41,138 additions and 19,350 deletions.
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@
![GitHub branch checks state](https://img.shields.io/github/actions/workflow/status/JohnLCaron/egk-ec-mixnet/unit-tests.yml)
![Coverage](https://img.shields.io/badge/coverage-88.7%25%20LOC%20(1345/1516)-blue)


# Egk Elliptic Curves Mixnet

_last update 04/10/2024_

(Work in Progress)
_last update 04/16/2024_

Implementation of a mixnet using the [ElectionGuard Kotlin Elliptical Curve library](https://github.com/JohnLCaron/egk-ec),
and the [Verificatum library](https://www.verificatum.org/). The mixnet uses the Terelius / Wikström (TW) mixnet
Expand Down Expand Up @@ -102,7 +99,7 @@ If the library has changed and you need to update it:
````
cd ~/dev/github/egk-ec-mixnet:
git fetch origin
git rebase -i origin/main
git rebase origin/main
````

Then rebuild the code:
Expand Down Expand Up @@ -142,7 +139,8 @@ The components of this workflow are:
#### generate-and-encrypt-ballots.sh

* Generates random plaintext ballots from the given manifest, and writes their encryptions to the public mixnet directory.
* This is the main functionality that needs to be implemented by the election voting system.
* This is the main functionality that needs to be implemented by the election voting system. Likely the voting system will
write the plaintext ballot to disk and call RunEncryptBallot.main() with appropriate parameters.

#### eg-tally.sh

Expand Down Expand Up @@ -172,22 +170,26 @@ The components of this workflow are:

#### table-mixnet.sh

* From the last mix's ShuffledBallots, generate table of decrypted (K^sn) serial numbers and proofs.
* From the last mix's ShuffledBallots, generate table of decrypted (K^sn) serial numbers and proofs.
* This table is written to _working/public/decrypted_sns.json_.

#### table-pballot.sh

* Simulate a table of paper ballot serial numbers and their physical locations.
Pass in "--missingPct percent" to simulate some percent of paper ballots were not received.
* Pass in "--missingPct percent" to simulate some percent of paper ballots were not received.
* This table is written to _working/public/pballot_table.json_.

#### pballot-decrypt

* From a paper ballot's serial number, find the corresponding shuffled ballot and decrypt it.
Place decrypted ballot into private directory.
* From a paper ballot's serial number (psn), find the corresponding shuffled ballot and decrypt it.
* Place decrypted ballot into _private/decrypted_ballots/_ directory.
* Use psn as the decrypted ballot id, and the filename is _dballlot-psn.json_.

#### verify-decryptions

* Verify the proofs in the decrypted serial numbers and decrypted ballots.
If a digital copy of the paper ballots are available, compare the ballot decryptions to the originals.
* Verify the proofs in the decrypted serial numbers (decrypted_sns.json).
* Verify the decrypted ballot proofs.
* If digital copies of the paper ballots are available, compare the ballot decryptions to the originals.


## Directory file layout (strawman)
Expand Down
Binary file modified libs/egk-ec-2.1-SNAPSHOT.jar
Binary file not shown.
5 changes: 3 additions & 2 deletions scripts/generate-and-encrypt-ballots.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ echo " RunExampleEncryption for ${NUM_BALLOTS} ballots, 2 devices but single d
-in ${PUBLIC_DIR} \
-nballots 21 \
-pballotDir ${PRIVATE_DIR}/input_ballots \
-eballotDir ${PUBLIC_DIR}/encrypted_ballots \
-device device42,yerDevice
-out ${PUBLIC_DIR} \
-device device42,yerDevice \
--noDeviceNameInDir

echo " [DONE] Generating encrypted ballots into ${PUBLIC_DIR}/encrypted_ballots"
1 change: 0 additions & 1 deletion src/main/kotlin/org/cryptobiotic/maths/ProdColumnPow.kt
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ class ProdColumnPow(val group: GroupContext, val nthreads: Int, val alg: ProdCol

for (pair in producer) {
val (column, idx) = calculate(pair)
// println("calculated column $idx")
mutex.withLock {
val colIdx = idx / 2
val mlist = if (idx % 2 == 0) pads.getOrPut(colIdx) { mutableListOf() }
Expand Down
53 changes: 14 additions & 39 deletions src/main/kotlin/org/cryptobiotic/mixnet/cli/RunVerifyDecryptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ import org.cryptobiotic.eg.election.ElectionInitialized
import org.cryptobiotic.eg.publish.Consumer
import org.cryptobiotic.eg.publish.makeConsumer
import org.cryptobiotic.eg.verifier.VerifyDecryption
import org.cryptobiotic.mixnet.writer.PballotEntry
import org.cryptobiotic.mixnet.writer.import
import org.cryptobiotic.mixnet.writer.makePballotMap
import org.cryptobiotic.mixnet.writer.readDecryptedSnsFromFile
import org.cryptobiotic.mixnet.writer.*
import org.cryptobiotic.util.ErrorMessages
import org.cryptobiotic.util.Stats

Expand Down Expand Up @@ -70,11 +67,11 @@ class RunVerifyDecryptions {
val electionInit = initResult.unwrap()

val errsSns = ErrorMessages("runVerifySnDecryptions")
val verifySns = runVerifySnDecryptions(publicDir, consumerIn, electionInit, errsSns, show)
val snMap = runVerifySnDecryptions(publicDir, consumerIn, electionInit, errsSns, show)
if (errsSns.hasErrors()) {
logger.error { errsSns.toString() }
} else {
logger.info { "verify sn decryptions = $verifySns" }
logger.info { "verify sn decryptions all ok" }
}

if (decryptedBallotDir != null) {
Expand Down Expand Up @@ -111,7 +108,8 @@ class RunVerifyDecryptions {
electionInit: ElectionInitialized,
errs: ErrorMessages,
show: Boolean,
): Boolean {
): List<DecryptedSn> {
val decSns = mutableListOf<DecryptedSn>()
var allOk = true

val decryptedSnsFile = "$publicDir/${RunMixnet.decryptedSnsFilename}"
Expand All @@ -121,7 +119,6 @@ class RunVerifyDecryptions {
throw RuntimeException("failed $decryptedSnsResult")
}
val decryptedSns = decryptedSnsResult.unwrap()

decryptedSns.decryptedSnJsons.forEach { json ->
val decryptedSn =
json.import(consumerIn.group, errs.nested("import decrypted ballot row=${json.shuffled_row}"))
Expand All @@ -134,11 +131,12 @@ class RunVerifyDecryptions {
electionInit.extendedBaseHash, electionInit.jointPublicKey, decryptedSn.encrypted_sn,
decryptedSn.Ksn
)
decSns.add(decryptedSn)
if (show) println(" verify decryptedSn row=${decryptedSn.shuffledRow} is $verify")
allOk = allOk && verify
}
}
return allOk
return decSns
}

fun runVerifyBallots(
Expand Down Expand Up @@ -178,7 +176,6 @@ class RunVerifyDecryptions {
errs: ErrorMessages,
show: Boolean,
): Boolean {
val showDetails = show && details
var allOk = true
val consumerIn = makeConsumer(egkMixnetDir)
consumerIn.iterateDecryptedBallots(decryptedBallotDir).forEach { decryptedBallot ->
Expand All @@ -192,36 +189,14 @@ class RunVerifyDecryptions {
allOk = false
} else {
val orgBallot = orgBallotResult.unwrap()
val pcontestMap = orgBallot.contests.associateBy { it.contestId }
var ballotOk = true

decryptedBallot.contests.forEach { dcontest ->
val pcontest = pcontestMap[dcontest.contestId]
if (pcontest == null) {
errs.add(" missing contest ${dcontest.contestId}")
ballotOk = false
} else {
if (showDetails) println(" contest ${dcontest.contestId}")
val pselectionMap = pcontest.selections.associateBy { it.selectionId }
dcontest.selections.forEach { dselection ->
val pselection = pselectionMap[dselection.selectionId]
if (pselection == null) {
errs.add(" missing selection ${dselection.selectionId}")
ballotOk = false
} else {
if (dselection.tally != pselection.vote) {
errs.add(" ${dselection.selectionId} vote ${dselection.tally} != ${pselection.vote}")
allOk = false
}
val isEqual = if (dselection.tally == pselection.vote) "==" else "NOT"
if (showDetails) println(" selection ${dselection.selectionId} ${dselection.tally} $isEqual ${pselection.vote}")
}
}
}
val errs = ErrorMessages("Compare Original and Decrypted Ballot id=${orgBallot.ballotId} psn=$psn")
if (!decryptedBallot.compare(orgBallot, errs)) {
logger.error { errs.toString() }
allOk = false
} else {
val decryptedFilename = "$decryptedBallotDir/dballot-${decryptedBallot.id}.json" // TODO
logger.info { "decrypted $decryptedFilename compare plaintext $pballotFilename ($psn) is ok" }
}
val decryptedFilename = "$decryptedBallotDir/dballot-${decryptedBallot.id}.json" // TODO
logger.info { "decrypted $decryptedFilename compare plaintext $pballotFilename ($psn) == $ballotOk" }
allOk = allOk && ballotOk
}
}
println("all ballots compare equal == $allOk")
Expand Down
14 changes: 14 additions & 0 deletions src/test/data/duplicate/private/constants.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "P-256",
"type": "EllipticCurve",
"protocolVersion": "v2.1.0",
"constants": {
"a": "ffffffff00000001000000000000000000000000fffffffffffffffffffffffc",
"b": "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
"primeModulus": "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
"order": "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
"g.x": "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"g.y": "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
"h": "1"
}
}
Loading

0 comments on commit 7c43122

Please sign in to comment.