Skip to content

Commit

Permalink
Add return value to RunEncryptBallot.
Browse files Browse the repository at this point in the history
Add sample script that uses it
  • Loading branch information
JohnLCaron committed Apr 25, 2024
1 parent 04562ad commit a61aa37
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 96 deletions.
Binary file modified libs/egk-ec-2.1-SNAPSHOT.jar
Binary file not shown.
39 changes: 39 additions & 0 deletions scripts/generate-and-encrypt-ballot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/bash

INPUT_DIR="src/test/data/working/public"
PLAINTEXT_BALLOT="src/test/data/working/private/input_ballots/pballot-ballot11.json"
OUTPUT_DIR="src/test/data/testOut/egkmixnet/RunEncryptBallot"


if [ -z "${INPUT_DIR}" ]; then
echo "No input directory provided."
exit 1
fi

if [ -z "${PLAINTEXT_BALLOT}" ]; then
echo "No ballot filename provided."
exit 1
fi

if [ -z "${OUTPUT_DIR}" ]; then
echo "No output directory provided."
exit 1
fi

mkdir -p ${OUTPUT_DIR}

CLASSPATH="build/libs/egk-ec-mixnet-2.1-SNAPSHOT-uber.jar"

echo " RunEncryptBallot for ${PLAINTEXT_BALLOT}"

/usr/bin/java -classpath $CLASSPATH \
org.cryptobiotic.eg.cli.RunEncryptBallot \
--inputDir ${INPUT_DIR} \
--ballotFilepath ${PLAINTEXT_BALLOT} \
--outputDir ${OUTPUT_DIR} \
-device device42 \
--noDeviceNameInDir

retval=$?

echo " [DONE] RunEncryptBallot return value $retval"
41 changes: 19 additions & 22 deletions src/main/kotlin/org/cryptobiotic/mixnet/writer/BinaryBallots.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,9 @@ import java.io.FileOutputStream
import java.io.OutputStream

fun writeBallotsBinaryToFile(filename: String, ballots: List<VectorCiphertext>) {
try {
FileOutputStream(filename).use { out ->
ballots.forEach { it.write(out) }
out.flush()
}
} catch (t: Throwable) {
println("Exception on $filename")
t.printStackTrace()
throw t
FileOutputStream(filename).use { out ->
ballots.forEach { it.write(out) }
out.flush()
}
}

Expand All @@ -37,10 +31,10 @@ fun readBinaryBallotsFromFile(
filename: String,
width: Int
): Result<List<VectorCiphertext>, ErrorMessages> {

val errs = ErrorMessages("readBinaryBallotsFromFile $filename with width $width")
val textSize = group.MAX_BYTES_P
val blockSize = 2 * textSize * width
val result = mutableListOf<VectorCiphertext>()
val result = mutableListOf<VectorCiphertext?>()
var totalBytes = 0
try {
val file = File(filename) // gulp the entire file to a byte array
Expand All @@ -50,28 +44,31 @@ fun readBinaryBallotsFromFile(
totalBytes += bytesRead
}
if (show) println(" read ${totalBytes} bytes nrows= ${result.size} from $filename")
return Ok(result)
return if (result.any { it == null }) errs.add("malformed") else Ok(result.filterNotNull())

} catch (t: Throwable) {
println("Exception on $filename")
t.printStackTrace()
throw t
return errs.add(t.toString())
}
}

private fun processRow(group: GroupContext, textSize: Int, width: Int, ba: ByteArray): VectorCiphertext {
private fun processRow(group: GroupContext, textSize: Int, width: Int, ba: ByteArray): VectorCiphertext? {
val result = mutableListOf<ElGamalCiphertext>()
var offset = 0
var allOk = true
repeat(width) {
val padArray = ByteArray(textSize) { ba[offset + it] }
offset += textSize
val dataArray = ByteArray(textSize) { ba[offset + it] }
offset += textSize
result.add(
ElGamalCiphertext(
group.binaryToElementModPsafe(padArray, 0),
group.binaryToElementModPsafe(dataArray, 0),
)
)

val pad = group.binaryToElementModP(padArray)
val data = group.binaryToElementModP(dataArray)
if (pad != null && data != null) {
result.add(ElGamalCiphertext(pad, data))
} else {
allOk = false
}
}
return VectorCiphertext(group, result)
return if (allOk) VectorCiphertext(group, result) else null
}
74 changes: 50 additions & 24 deletions src/main/kotlin/org/cryptobiotic/mixnet/writer/ShuffledBallots.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ class ShuffledBallotsJson(
val rows: List<VectorCiphertextJson>,
)

fun ShuffledBallotsJson.import(group: GroupContext) : List<VectorCiphertext> {
return rows.map{ it.import(group) }
fun ShuffledBallotsJson.import(group: GroupContext, errs: ErrorMessages) : List<VectorCiphertext> {
val importedRows = rows.map { it.import(group) }
if (importedRows.any { it == null }) errs.add("malformed Json file")
return importedRows.filterNotNull()
}

fun List<VectorCiphertext>.publishJson() : ShuffledBallotsJson {
Expand All @@ -60,7 +62,7 @@ fun readShuffledBallotsJsonFromFile(group: GroupContext, filename: String): Resu
return try {
Files.newInputStream(filepath, StandardOpenOption.READ).use { inp ->
val json = jsonReader.decodeFromStream<ShuffledBallotsJson>(inp)
val matrixRows = json.import(group)
val matrixRows = json.import(group, errs)
if (errs.hasErrors()) Err(errs) else Ok(matrixRows)
}
} catch (t: Throwable) {
Expand Down Expand Up @@ -90,7 +92,6 @@ class ProofOfShuffleJson(
val Dp: ElementModPJson,
val Fp: VectorCiphertextJson,


val kA: ElementModQJson,
val kB: VectorQJson,
val kC: ElementModQJson,
Expand All @@ -99,22 +100,38 @@ class ProofOfShuffleJson(
val kF: VectorQJson,
)

fun ProofOfShuffleJson.import(group: GroupContext) : ProofOfShuffle {
return ProofOfShuffle(
fun ProofOfShuffleJson.import(group : GroupContext, errs : ErrorMessages): ProofOfShuffle? {
val u: VectorP? = this.u.import(group) ?: errs.addNull("malformed u") as VectorP?
val Ap = this.Ap.import(group) ?: errs.addNull("malformed Ap") as ElementModP?
val B = this.B.import(group) ?: errs.addNull("malformed B") as VectorP?
val Bp = this.Bp.import(group) ?: errs.addNull("malformed Bp") as VectorP?
val Cp = this.Cp.import(group) ?: errs.addNull("malformed Cp") as ElementModP?
val Dp = this.Dp.import(group) ?: errs.addNull("malformed Dp") as ElementModP?
val Fp = this.Fp.import(group) ?: errs.addNull("malformed Fp") as VectorCiphertext?

val kA = this.kA.import(group) ?: errs.addNull("malformed kA") as ElementModQ?
val kB = this.kB.import(group) ?: errs.addNull("malformed kB") as VectorQ?
val kC = this.kC.import(group) ?: errs.addNull("malformed kC") as ElementModQ?
val kD = this.kD.import(group) ?: errs.addNull("malformed kD") as ElementModQ?
val kE = this.kE.import(group) ?: errs.addNull("malformed kE") as VectorQ?
val kF = this.kF.import(group) ?: errs.addNull("malformed kF") as VectorQ?

return if (errs.hasErrors()) null
else ProofOfShuffle(
this.mixname,
this.u.import(group),
this.Ap.import(group)!!,
this.B.import(group),
this.Bp.import(group),
this.Cp.import(group)!!,
this.Dp.import(group)!!,
this.Fp.import(group),
this.kA.import(group)!!,
this.kB.import(group),
this.kC.import(group)!!,
this.kD.import(group)!!,
this.kE.import(group),
this.kF.import(group),
u!!,
Ap!!,
B!!,
Bp!!,
Cp!!,
Dp!!,
Fp!!,
kA!!,
kB!!,
kC!!,
kD!!,
kE!!,
kF!!,
)
}

Expand Down Expand Up @@ -148,8 +165,8 @@ fun readProofOfShuffleJsonFromFile(group: GroupContext, filename: String): Resul
return try {
Files.newInputStream(filepath, StandardOpenOption.READ).use { inp ->
val json = jsonReader.decodeFromStream<ProofOfShuffleJson>(inp)
val shuffleProof = json.import(group)
if (errs.hasErrors()) Err(errs) else Ok(shuffleProof)
val shuffleProof = json.import(group, errs)
if (errs.hasErrors()) Err(errs) else Ok(shuffleProof!!)
}
} catch (t: Throwable) {
errs.add("Exception= ${t.message} ${t.stackTraceToString()}")
Expand All @@ -171,22 +188,31 @@ fun writeProofOfShuffleJsonToFile(shuffleProof: ProofOfShuffle, filename: String
class VectorCiphertextJson(
val elems: List<ElGamalCiphertextJson>,
)
fun VectorCiphertextJson.import(group: GroupContext) = VectorCiphertext(group, elems.map{ it.import(group)!! } )
fun VectorCiphertext.publishJson() = VectorCiphertextJson(this.elems.map { it.publishJson() })
fun VectorCiphertextJson.import(group: GroupContext): VectorCiphertext? {
val texts = elems.map { it.import(group) }
return if (texts.any { it == null }) null else VectorCiphertext(group, texts.filterNotNull())
}

@Serializable
class VectorQJson(
val elems: List<ElementModQJson>,
)
fun VectorQJson.import(group: GroupContext) = VectorQ(group, elems.map{ it.import(group)!! } )
fun VectorQ.publishJson() = VectorQJson(this.elems.map { it.publishJson() })
fun VectorQJson.import(group: GroupContext): VectorQ? {
val ques = elems.map { it.import(group) }
return if (ques.any { it == null }) null else VectorQ(group, ques.filterNotNull())
}

@Serializable
class VectorPJson(
val elems: List<ElementModPJson>,
)
fun VectorPJson.import(group: GroupContext) = VectorP(group, elems.map{ it.import(group)!! } )
fun VectorP.publishJson() = VectorPJson(this.elems.map { it.publishJson() })
fun VectorPJson.import(group: GroupContext): VectorP? {
val pees = elems.map { it.import(group) }
return if (pees.any { it == null }) null else VectorP(group, pees.filterNotNull())
}



2 changes: 0 additions & 2 deletions src/main/resources/logback.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,4 @@
<appender-ref ref="FILE"/>
</root>
<logger name="ch.qos.logback" level="WARN"/>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.netty" level="INFO"/>
</configuration>

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.cryptobiotic.mixnet.writer

import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.unwrap
import org.cryptobiotic.eg.core.*
import org.cryptobiotic.eg.core.elGamalKeyPairFromRandom
import org.cryptobiotic.eg.core.encrypt
import org.cryptobiotic.eg.core.productionGroup
import org.cryptobiotic.maths.*
import org.cryptobiotic.util.Testing
import org.junit.jupiter.api.Assertions.assertTrue
import org.opentest4j.AssertionFailedError
import java.io.FileNotFoundException
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

class ShuffledBallotRoundtripTest {
val group = productionGroup()

@Test
fun testBallotWriter() {
val testOutDir = "${Testing.testOutMixnet}/BinaryBallotRoundtripTest"
createDirectories(testOutDir)

println("group ${group.constants.name} write binary to ${testOutDir}")
testBallotWriter(group, 1, 1, testOutDir, false)
testBallotWriter(group, 100,34, testOutDir, false)
}

@Test
fun testBallotWriterJson() {
val testOutDir = "${Testing.testOutMixnet}/JsonBallotRoundtripTest"
createDirectories(testOutDir)
println("group ${group.constants.name} write JSON to ${testOutDir}")
testBallotWriter(group, 100,34, testOutDir, true)
}

fun testBallotWriter(group: GroupContext, nrows: Int, width: Int, testOutDir: String, isJson: Boolean) {
val keypair = elGamalKeyPairFromRandom(group)
val ballots: List<VectorCiphertext> = List(nrows) {
val ciphertexts = List(width) { Random.nextInt(11).encrypt(keypair) }
VectorCiphertext(group, ciphertexts)
}

writeShuffledBallotsToFile(isJson, testOutDir, ballots)
val roundtripResult = readShuffledBallotsFromFile(group, testOutDir, width)
assertTrue( roundtripResult is Ok)
val readBallots = roundtripResult.unwrap()

assertEquals(nrows, ballots.size)
assertEquals(ballots.size, readBallots.size)
assertEquals(ballots, readBallots)
}

@Test
fun testBallotWriterFails() {
val testOutDir = "${Testing.testOutMixnet}/bad"
assertFailsWith<FileNotFoundException> {
testBallotWriter(group, 100,34, testOutDir, true)
}
assertFailsWith<FileNotFoundException> {
testBallotWriter(group, 100,34, testOutDir, false)
}
}

@Test
fun testBallotWriterFailsBinOverides() {
val testOutDir = "${Testing.testOutMixnet}/testBallotWriterFailsBinOverides"
createDirectories(testOutDir)
testBallotWriter(group, 100,34, testOutDir, false)
val ex = assertFailsWith<AssertionFailedError> {
testBallotWriter(group, 100,34, testOutDir, true)
}
}

}

0 comments on commit a61aa37

Please sign in to comment.