diff --git a/README.md b/README.md index c0271bc..f959e0e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ [![License](https://img.shields.io/github/license/JohnLCaron/egk-ec)](https://github.com/JohnLCaron/egk-ec-mixnet/blob/main/LICENSE.txt) ![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-90.0%25%20LOC%20(1573/1748)-blue) +![Coverage](https://img.shields.io/badge/coverage-89.9%25%20LOC%20(1651/1836)-blue) # Egk Elliptic Curves Mixnet -_last update 05/18/2024_ +_last update 05/22/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 diff --git a/docs/mixnet-maths.zip b/docs/mixnet-maths.zip index c98eec7..a34a605 100644 Binary files a/docs/mixnet-maths.zip and b/docs/mixnet-maths.zip differ diff --git a/docs/mixnet_maths.pdf b/docs/mixnet_maths.pdf index f845870..40580e4 100644 Binary files a/docs/mixnet_maths.pdf and b/docs/mixnet_maths.pdf differ diff --git a/libs/egk-ec-2.1-SNAPSHOT.jar b/libs/egk-ec-2.1-SNAPSHOT.jar index 1ee03d7..8f07663 100644 Binary files a/libs/egk-ec-2.1-SNAPSHOT.jar and b/libs/egk-ec-2.1-SNAPSHOT.jar differ diff --git a/src/main/kotlin/org/cryptobiotic/mixnet/cli/RunMixnetTally.kt b/src/main/kotlin/org/cryptobiotic/mixnet/cli/RunMixnetTally.kt new file mode 100644 index 0000000..ef51355 --- /dev/null +++ b/src/main/kotlin/org/cryptobiotic/mixnet/cli/RunMixnetTally.kt @@ -0,0 +1,136 @@ +package org.cryptobiotic.mixnet.cli + +import com.github.michaelbull.result.Err +import com.github.michaelbull.result.unwrap +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.core.* +import org.cryptobiotic.eg.election.* +import org.cryptobiotic.eg.publish.makeConsumer +import org.cryptobiotic.eg.publish.makePublisher +import org.cryptobiotic.eg.tally.AccumulateTally +import org.cryptobiotic.mixnet.writer.MixnetConfigJson +import org.cryptobiotic.util.ErrorMessages +import org.cryptobiotic.mixnet.writer.readMixnetConfigFromFile +import org.cryptobiotic.mixnet.writer.readShuffledBallotsFromFile +import kotlin.system.exitProcess + +class RunMixnetTally { + + companion object { + val logger = KotlinLogging.logger("RunMixnetTally") + + @JvmStatic + fun main(args: Array) { + val parser = ArgParser("RunMixnetTally") + val publicDir by parser.option( + ArgType.String, + shortName = "in", + description = "egk mixnet public directory" + ).required() + val mixDir by parser.option( + ArgType.String, + shortName = "mix", + description = "Mix directory" + ).required() + val outputDir by parser.option( + ArgType.String, + shortName = "out", + description = "output directory" + ).required() + val noexit by parser.option( + ArgType.Boolean, + shortName = "noexit", + description = "Dont call System.exit" + ).default(false) + + parser.parse(args) + + val info = buildString { + appendLine("starting MixnetTally") + appendLine(" publicDir= $publicDir") + appendLine(" mixDir= $mixDir") + append(" outputDir= $outputDir") + } + logger.info { info } + + val configFilename = "$mixDir/${RunMixnet.configFilename}" + val resultConfig = readMixnetConfigFromFile(configFilename) + if (resultConfig is Err) { + RunMixnet.logger.error { "Error reading MixnetConfig from $configFilename err = $resultConfig" } + if (!noexit) exitProcess(1) else return + } + val config = resultConfig.unwrap() + + val valid = runAccumulateBallots(publicDir, config, mixDir, outputDir, noexit) + logger.info { "valid = $valid" } + } + + fun runAccumulateBallots( + publicDir: String, + config: MixnetConfigJson, + mixDir: String, + outputDir: String, + noexit: Boolean + ) { + val consumerIn = makeConsumer(publicDir) + val initResult = consumerIn.readElectionInitialized() + if (initResult is Err) { + logger.error { "readElectionInitialized error ${initResult.error}" } + return + } + val electionInit = initResult.unwrap() + val manifest = consumerIn.makeManifest(electionInit.config.manifestBytes) + val group = consumerIn.group + + val shuffledResult = readShuffledBallotsFromFile( group, mixDir, config.width) + if (shuffledResult is Err) { + RunMixnetTable.logger.error {"Error reading shuffled ballots in $mixDir = $shuffledResult" } + if (!noexit) exitProcess(3) else return + } + val shuffled = shuffledResult.unwrap() + RunProofOfShuffleVerifier.logger.info { " Read ${shuffled.size} shuffled ballots" } + + //val reader = BallotReader(group, config.width) + //val shuffled = reader.readFromFile("$mixDir/$shuffledFilename") + + val accumulator = AccumulateTally( + group, + manifest, + config.mix_name, + electionInit.extendedBaseHash, + electionInit.jointPublicKey, + countNumberOfBallots = true, + ) + val errs = ErrorMessages("RunAccumulateTally on Shuffled Ballots") + var nrows = 0 + shuffled.forEach { + val ballotId = it.elems[0].hashCode().toString() + val eballot: EncryptedBallotIF = rehydrate(manifest, ballotId, electionInit.extendedBaseHash, 0, it) + if (!accumulator.addCastBallot(eballot, errs)) { + println(" got error $errs") + } + nrows++ + } + + val tally: EncryptedTally = accumulator.build() + + val publisher = makePublisher(outputDir, false) + publisher.writeTallyResult( + TallyResult( + electionInit, tally, emptyList(), + mapOf( + Pair("CreatedBy", "RunMixnetTally"), + Pair("CreatedOn", getSystemDate()), + Pair("CreatedFromDir", mixDir) + ) + ) + ) + println("writeTallyResult nrows=$nrows, width= ${config.width} per row" ) + logger.info { "writeTallyResult nrows=$nrows, width= ${config.width} per row" } + } + } +} diff --git a/src/test/kotlin/org/cryptobiotic/mixnet/cli/RunMixnetTallyTest.kt b/src/test/kotlin/org/cryptobiotic/mixnet/cli/RunMixnetTallyTest.kt new file mode 100644 index 0000000..f473bd8 --- /dev/null +++ b/src/test/kotlin/org/cryptobiotic/mixnet/cli/RunMixnetTallyTest.kt @@ -0,0 +1,37 @@ +package org.cryptobiotic.mixnet.cli + +import org.cryptobiotic.eg.core.createDirectories +import org.cryptobiotic.util.Testing +import kotlin.test.Test + +class RunMixnetTallyTest { + + @Test + fun testRunMixnetTally() { + val publicDir = "src/test/data/working/public" + val outputDir = "${Testing.testOutMixnet}/testRunMixnetTally" + createDirectories("$outputDir/mix1") + createDirectories("$outputDir/mix2") + createDirectories(outputDir) + + RunMixnetTally.main( + arrayOf( + "--publicDir", publicDir, + "--mixDir", "$publicDir/mix1", + "--outputDir", "$outputDir/mix1", + ) + ) + + /* + RunMixnetTally.main( + arrayOf( + "-publicDir", publicDir, + "--mixDir", "$outputDir/mix2", + "--outputDir", "$outputDir/mix2", + ) + ) + + */ + } + +} \ No newline at end of file