From 417dd8a0beeec0238ee0c0c4fb4a5df5e451638d Mon Sep 17 00:00:00 2001 From: JohnLCaron Date: Wed, 13 Dec 2023 12:10:41 -0700 Subject: [PATCH] Remove protobuf serialization for ContestData, use ad-hoc ByteArraySerialization. Add preecryption to EncryptedBallotJson serialization. Move common publish utilities to JSON classes. --- .../ballot/ByteArraySerialize.kt | 8 ++ .../electionguard/ballot/ContestData.kt | 71 ++------------ .../ballot/DecryptedTallyOrBallot.kt | 2 +- .../electionguard/decrypt/TallyDecryptor.kt | 1 - .../json2/EncryptedBallotJson.kt | 63 +++++++++++- .../protoconvert/DecryptedTallyProto.kt | 44 +++++++-- .../electionguard/publish/PublisherJson.kt | 2 +- .../preencrypt/PreEncryptorOutputTest.kt | 13 ++- .../preencrypt/PreEncryptorTest.kt | 14 +-- .../electionguard/workflow/ContestDataTest.kt | 4 +- .../ballot/ByteArraySerialize.kt | 95 +++++++++++++++++++ .../electionguard/publish/ConsumerJson.kt | 22 +++++ .../electionguard/publish/ConsumerProto.kt | 21 ---- .../electionguard/publish/PublisherJson.kt | 48 ++++++++++ .../electionguard/publish/PublisherProto.kt | 65 ++++--------- 15 files changed, 311 insertions(+), 162 deletions(-) create mode 100644 egklib/src/commonMain/kotlin/electionguard/ballot/ByteArraySerialize.kt create mode 100644 egklib/src/jvmMain/kotlin/electionguard/ballot/ByteArraySerialize.kt diff --git a/egklib/src/commonMain/kotlin/electionguard/ballot/ByteArraySerialize.kt b/egklib/src/commonMain/kotlin/electionguard/ballot/ByteArraySerialize.kt new file mode 100644 index 00000000..1aa852c8 --- /dev/null +++ b/egklib/src/commonMain/kotlin/electionguard/ballot/ByteArraySerialize.kt @@ -0,0 +1,8 @@ +package electionguard.ballot + +import com.github.michaelbull.result.Result + +expect fun ContestData.encodeToByteArray(fill: String? = null): ByteArray + +expect fun ByteArray.decodeToContestData() : Result + diff --git a/egklib/src/commonMain/kotlin/electionguard/ballot/ContestData.kt b/egklib/src/commonMain/kotlin/electionguard/ballot/ContestData.kt index f4fdbdf1..0045bc68 100644 --- a/egklib/src/commonMain/kotlin/electionguard/ballot/ContestData.kt +++ b/egklib/src/commonMain/kotlin/electionguard/ballot/ContestData.kt @@ -1,12 +1,9 @@ package electionguard.ballot import com.github.michaelbull.result.Err -import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Result import electionguard.core.* import io.github.oshai.kotlinlogging.KotlinLogging -import pbandk.decodeFromByteArray -import pbandk.encodeToByteArray import kotlin.math.max private val logger = KotlinLogging.logger("ContestData") @@ -27,30 +24,6 @@ data class ContestData( val writeIns: List, val status: ContestDataStatus = if (overvotes.isNotEmpty()) ContestDataStatus.over_vote else ContestDataStatus.normal, ) { - - fun publish(filler: String = ""): electionguard.protogen.ContestData { - return publish( - this.status, - this.overvotes, - this.writeIns, - filler, - ) - } - - fun publish( - status: ContestDataStatus, - overvotes: List, - writeIns: List, - filler: String = "" - ): electionguard.protogen.ContestData { - return electionguard.protogen.ContestData( - status.publishContestDataStatus(), - overvotes, - writeIns, - filler, - ) - } - // Make sure that the HashedElGamalCiphertext message is exactly (votesAllowed + 1) * BLOCK_SIZE // If too large, remove extra writeIns, add "*" to list to indicate some were removed // If still too large, truncate writeIns to CHOP_WRITE_INS characters, append "*" to string to indicate truncated @@ -68,7 +41,7 @@ data class ContestData( val messageSize = (1 + contestLimit) * BLOCK_SIZE var trialContestData = this - var trialContestDataBA = trialContestData.publish().encodeToByteArray() + var trialContestDataBA = trialContestData.encodeToByteArray() var trialSize = trialContestDataBA.size val trialSizes = mutableListOf() trialSizes.add(trialSize) @@ -81,7 +54,7 @@ data class ContestData( trialContestData = trialContestData.copy( writeIns = truncateWriteIns, ) - trialContestDataBA = trialContestData.publish().encodeToByteArray() + trialContestDataBA = trialContestData.encodeToByteArray() trialSize = trialContestDataBA.size trialSizes.add(trialSize) } @@ -93,7 +66,7 @@ data class ContestData( if (it.length <= CHOP_WRITE_INS) it else it.substring(0, chop) + "*" } trialContestData = trialContestData.copy(writeIns = truncateWriteIns) - trialContestDataBA = trialContestData.publish().encodeToByteArray() + trialContestDataBA = trialContestData.encodeToByteArray() trialSize = trialContestDataBA.size trialSizes.add(trialSize) } @@ -102,7 +75,7 @@ data class ContestData( while (trialSize > messageSize && (trialContestData.overvotes.size > contestLimit + 1)) { val chopList = trialContestData.overvotes.subList(0, contestLimit + 1) + (-1) trialContestData = trialContestData.copy(overvotes = chopList) - trialContestDataBA = trialContestData.publish().encodeToByteArray() + trialContestDataBA = trialContestData.encodeToByteArray() trialSize = trialContestDataBA.size trialSizes.add(trialSize) } @@ -112,7 +85,7 @@ data class ContestData( val filler = StringBuilder().apply { repeat(messageSize - trialSize - 2) { append("*") } } - trialContestDataBA = trialContestData.publish(filler.toString()).encodeToByteArray() + trialContestDataBA = trialContestData.encodeToByteArray(filler.toString()) trialSize = trialContestDataBA.size trialSizes.add(trialSize) } @@ -204,8 +177,7 @@ fun HashedElGamalCiphertext.decryptWithBetaToContestData( val ba: ByteArray = this.decryptContestData(publicKey, extendedBaseHash, contestId, c0, beta) ?: return Err( "decryptWithBetaToContestData did not succeed") - val proto = electionguard.protogen.ContestData.decodeFromByteArray(ba) - return importContestData(proto) + return ba.decodeToContestData() } fun HashedElGamalCiphertext.decryptWithNonceToContestData( @@ -220,8 +192,7 @@ fun HashedElGamalCiphertext.decryptWithNonceToContestData( val (alpha, beta) = 0.encrypt(publicKey, contestDataNonce.toElementModQ(group)) val ba: ByteArray = this.decryptContestData(publicKey, extendedBaseHash, contestId, alpha, beta) ?: return Err( "decryptWithNonceToContestData did not succeed") - val proto = electionguard.protogen.ContestData.decodeFromByteArray(ba) - return importContestData(proto) + return ba.decodeToContestData() } fun HashedElGamalCiphertext.decryptWithSecretKey( @@ -266,32 +237,4 @@ fun HashedElGamalCiphertext.decryptContestData( } } -////////////////////////////////////////////////////////////////// -// LOOK maybe move to protoconvert - -fun importContestData(proto : electionguard.protogen.ContestData?): Result { - if (proto == null) return Err( "ContestData is missing") - return Ok(ContestData( - proto.overVotes, - proto.writeIns, - importContestDataStatus(proto.status)?: ContestDataStatus.normal, - )) -} - -private fun importContestDataStatus(proto: electionguard.protogen.ContestData.Status): ContestDataStatus? { - val result = safeEnumValueOf(proto.name) - if (result == null) { - logger.error { "ContestDataStatus $proto has missing or unknown name" } - } - return result -} - -private fun ContestDataStatus.publishContestDataStatus(): electionguard.protogen.ContestData.Status { - return try { - electionguard.protogen.ContestData.Status.fromName(this.name) - } catch (e: IllegalArgumentException) { - logger.error { "ContestDataStatus $this has missing or unknown name" } - electionguard.protogen.ContestData.Status.NORMAL - } -} diff --git a/egklib/src/commonMain/kotlin/electionguard/ballot/DecryptedTallyOrBallot.kt b/egklib/src/commonMain/kotlin/electionguard/ballot/DecryptedTallyOrBallot.kt index a337b012..27bf6248 100644 --- a/egklib/src/commonMain/kotlin/electionguard/ballot/DecryptedTallyOrBallot.kt +++ b/egklib/src/commonMain/kotlin/electionguard/ballot/DecryptedTallyOrBallot.kt @@ -29,7 +29,7 @@ data class DecryptedTallyOrBallot( data class DecryptedContestData( val contestData: ContestData, - val encryptedContestData : HashedElGamalCiphertext, // same as EncryptedTally.Selection.ciphertext + val encryptedContestData : HashedElGamalCiphertext, // same as EncryptedTally.Contest.contestData val proof: ChaumPedersenProof, var beta: ElementModP, // needed to verify 10.2 ) diff --git a/egklib/src/commonMain/kotlin/electionguard/decrypt/TallyDecryptor.kt b/egklib/src/commonMain/kotlin/electionguard/decrypt/TallyDecryptor.kt index fbb53e20..e0774bab 100644 --- a/egklib/src/commonMain/kotlin/electionguard/decrypt/TallyDecryptor.kt +++ b/egklib/src/commonMain/kotlin/electionguard/decrypt/TallyDecryptor.kt @@ -9,7 +9,6 @@ import electionguard.ballot.decryptWithBetaToContestData import electionguard.core.* import electionguard.util.ErrorMessages import electionguard.util.Stats -import io.github.oshai.kotlinlogging.KotlinLogging private const val doVerifierSelectionProof = true diff --git a/egklib/src/commonMain/kotlin/electionguard/json2/EncryptedBallotJson.kt b/egklib/src/commonMain/kotlin/electionguard/json2/EncryptedBallotJson.kt index 1fa78bc0..930e6a7f 100644 --- a/egklib/src/commonMain/kotlin/electionguard/json2/EncryptedBallotJson.kt +++ b/egklib/src/commonMain/kotlin/electionguard/json2/EncryptedBallotJson.kt @@ -4,6 +4,9 @@ import electionguard.ballot.EncryptedBallot import electionguard.core.* import electionguard.core.Base16.fromHex import electionguard.core.Base16.toHex +import electionguard.preencrypt.RecordedPreBallot +import electionguard.preencrypt.RecordedPreEncryption +import electionguard.preencrypt.RecordedSelectionVector import electionguard.util.ErrorMessages import kotlinx.serialization.Serializable @@ -139,6 +142,64 @@ fun EncryptedSelectionJson.import(group : GroupContext, errs : ErrorMessages): E ) } +//////////////////////////////////////////////////////////////////////////////////////////////// +// preencrypt + +fun EncryptedBallot.publishJson(recordedPreBallot: RecordedPreBallot) = EncryptedBallotJson( + this.ballotId, + this.ballotStyleId, + this.encryptingDevice, + this.timestamp, + this.codeBaux.toHex(), + this.confirmationCode.publishJson(), + this.electionId.publishJson(), + this.contests.map { it.publishJson(recordedPreBallot) }, + this.state.name, + this.encryptedSn?.publishJson(), + true, + null, +) + +private fun EncryptedBallot.Contest.publishJson(recordedPreBallot: RecordedPreBallot): EncryptedContestJson { + + val rcontest = recordedPreBallot.contests.find { it.contestId == this.contestId } + ?: throw IllegalArgumentException("Cant find ${this.contestId}") + + return EncryptedContestJson( + this.contestId, + this.sequenceOrder, + this.votesAllowed, + this.contestHash.publishJson(), + this.selections.map { + EncryptedSelectionJson( + it.selectionId, + it.sequenceOrder, + it.encryptedVote.publishJson(), + it.proof.publishJson(), + ) + }, + this.proof.publishJson(), + this.contestData.publishJson(), + rcontest.publishJson(), + ) +} + +private fun RecordedPreEncryption.publishJson(): PreEncryptionJson { + return PreEncryptionJson( + this.preencryptionHash.publishJson(), + this.allSelectionHashes.map { it.publishJson() }, + this.selectedVectors.map { it.publishJson() }, + ) +} + +private fun RecordedSelectionVector.publishJson(): SelectionVectorJson { + return SelectionVectorJson( + this.selectionHash.toUInt256safe().publishJson(), + this.shortCode, + this.encryptions.map { it.publishJson() }, + ) +} + @Serializable data class PreEncryptionJson( val preencryption_hash: UInt256Json, @@ -192,4 +253,4 @@ fun SelectionVectorJson.import(group: GroupContext, errs: ErrorMessages): Encryp this.short_code, encryptions.filterNotNull(), ) -} +} \ No newline at end of file diff --git a/egklib/src/commonMain/kotlin/electionguard/protoconvert/DecryptedTallyProto.kt b/egklib/src/commonMain/kotlin/electionguard/protoconvert/DecryptedTallyProto.kt index da2aafab..f1f1838c 100644 --- a/egklib/src/commonMain/kotlin/electionguard/protoconvert/DecryptedTallyProto.kt +++ b/egklib/src/commonMain/kotlin/electionguard/protoconvert/DecryptedTallyProto.kt @@ -1,13 +1,11 @@ package electionguard.protoconvert 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.DecryptedTallyOrBallot -import electionguard.ballot.importContestData -import electionguard.core.ElGamalCiphertext -import electionguard.core.ElementModP -import electionguard.core.GroupContext -import electionguard.core.UInt256 +import electionguard.ballot.* +import electionguard.core.* import electionguard.util.ErrorMessages fun electionguard.protogen.DecryptedTallyOrBallot.import(group: GroupContext, errs : ErrorMessages): DecryptedTallyOrBallot? { @@ -109,4 +107,36 @@ private fun DecryptedTallyOrBallot.DecryptedContestData.publishProto() = this.encryptedContestData.publishProto(), this.proof.publishProto(), this.beta.publishProto(), - ) \ No newline at end of file + ) + +////////////////////////////////////////////////////////////////// + +fun ContestData.publish(filler: String = ""): electionguard.protogen.ContestData { + return electionguard.protogen.ContestData( + status.publishContestDataStatus(), + overvotes, + writeIns, + filler, + ) +} + +fun importContestData(proto : electionguard.protogen.ContestData?): Result { + if (proto == null) return Err( "ContestData is missing") + return Ok(ContestData( + proto.overVotes, + proto.writeIns, + importContestDataStatus(proto.status)?: ContestDataStatus.normal, + )) +} + +private fun importContestDataStatus(proto: electionguard.protogen.ContestData.Status): ContestDataStatus? { + return safeEnumValueOf(proto.name) +} + +private fun ContestDataStatus.publishContestDataStatus(): electionguard.protogen.ContestData.Status { + return try { + electionguard.protogen.ContestData.Status.fromName(this.name) + } catch (e: IllegalArgumentException) { + electionguard.protogen.ContestData.Status.NORMAL + } +} \ No newline at end of file diff --git a/egklib/src/commonMain/kotlin/electionguard/publish/PublisherJson.kt b/egklib/src/commonMain/kotlin/electionguard/publish/PublisherJson.kt index 76986653..ed6bc1a8 100644 --- a/egklib/src/commonMain/kotlin/electionguard/publish/PublisherJson.kt +++ b/egklib/src/commonMain/kotlin/electionguard/publish/PublisherJson.kt @@ -4,7 +4,7 @@ import electionguard.ballot.* import electionguard.core.UInt256 import electionguard.keyceremony.KeyCeremonyTrustee -/** Read/write the Election Record as protobuf files. */ +/** Read/write the Election Record as JSON files. */ expect class PublisherJson(topDir: String, createNew: Boolean = false) : Publisher { override fun isJson() : Boolean diff --git a/egklib/src/commonTest/kotlin/electionguard/preencrypt/PreEncryptorOutputTest.kt b/egklib/src/commonTest/kotlin/electionguard/preencrypt/PreEncryptorOutputTest.kt index a6b93ba8..2966cedc 100644 --- a/egklib/src/commonTest/kotlin/electionguard/preencrypt/PreEncryptorOutputTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/preencrypt/PreEncryptorOutputTest.kt @@ -1,12 +1,11 @@ package electionguard.preencrypt -import com.github.michaelbull.result.unwrap import electionguard.ballot.Manifest import electionguard.core.* import electionguard.encrypt.cast import electionguard.cli.ManifestBuilder -import electionguard.protoconvert.import -import electionguard.protoconvert.publishProto +import electionguard.json2.import +import electionguard.json2.publishJson import electionguard.publish.makePublisher import electionguard.publish.readElectionRecord import electionguard.util.ErrorMessages @@ -15,7 +14,7 @@ import kotlin.test.Test private val random = Random -internal class PreEncryptorOutputTest { +class PreEncryptorOutputTest { // multiple selections per contest @Test @@ -100,10 +99,10 @@ internal class PreEncryptorOutputTest { println() } - // roundtrip through the proto, combines the recordedBallot + // roundtrip through the serialization, which combines the recordedBallot val encryptedBallot = ciphertextBallot.cast() - val proto = encryptedBallot.publishProto(recordedBallot) - val fullEncryptedBallot = proto.import(group, ErrorMessages(""))!! + val json = encryptedBallot.publishJson(recordedBallot) + val fullEncryptedBallot = json.import(group, ErrorMessages(""))!! // show what ends up in the election record if (show) { diff --git a/egklib/src/commonTest/kotlin/electionguard/preencrypt/PreEncryptorTest.kt b/egklib/src/commonTest/kotlin/electionguard/preencrypt/PreEncryptorTest.kt index 09697a82..067293e7 100644 --- a/egklib/src/commonTest/kotlin/electionguard/preencrypt/PreEncryptorTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/preencrypt/PreEncryptorTest.kt @@ -11,8 +11,8 @@ import electionguard.core.* import electionguard.decryptBallot.DecryptPreencryptWithNonce import electionguard.encrypt.cast import electionguard.cli.ManifestBuilder -import electionguard.protoconvert.import -import electionguard.protoconvert.publishProto +import electionguard.json2.import +import electionguard.json2.publishJson import electionguard.publish.readElectionRecord import electionguard.util.ErrorMessages import electionguard.util.Stats @@ -26,8 +26,8 @@ import kotlin.test.* private val random = Random -internal class PreEncryptorTest { - val input = "src/commonTest/data/workflow/allAvailableProto" +class PreEncryptorTest { + val input = "src/commonTest/data/workflow/allAvailableJson" val group = productionGroup() // sanity check that PreEncryptor.preencrypt doesnt barf @@ -227,10 +227,10 @@ internal fun runComplete( println() } - // roundtrip through the proto, combines the recordedBallot + // roundtrip through the serialization, which combines the recordedBallot val encryptedBallot = ciphertextBallot.cast() - val proto = encryptedBallot.publishProto(recordedBallot) - val fullEncryptedBallot = proto.import(group, ErrorMessages(""))!! + val json = encryptedBallot.publishJson(recordedBallot) + val fullEncryptedBallot = json.import(group, ErrorMessages(""))!! // show what ends up in the election record if (show) { diff --git a/egklib/src/commonTest/kotlin/electionguard/workflow/ContestDataTest.kt b/egklib/src/commonTest/kotlin/electionguard/workflow/ContestDataTest.kt index 20825920..5da64978 100644 --- a/egklib/src/commonTest/kotlin/electionguard/workflow/ContestDataTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/workflow/ContestDataTest.kt @@ -10,7 +10,6 @@ import electionguard.input.BallotInputBuilder import electionguard.publish.makePublisher import electionguard.publish.readElectionRecord import electionguard.util.ErrorMessages -import pbandk.decodeFromByteArray import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -76,8 +75,7 @@ class ContestDataTest { keypair.secretKey ) assertNotNull(baRT) - val protoRoundtrip = electionguard.protogen.ContestData.decodeFromByteArray(baRT) - val contestDataResult = importContestData(protoRoundtrip) + val contestDataResult = baRT.decodeToContestData() assertTrue(contestDataResult is Ok) val contestDataRoundtrip = contestDataResult.unwrap() diff --git a/egklib/src/jvmMain/kotlin/electionguard/ballot/ByteArraySerialize.kt b/egklib/src/jvmMain/kotlin/electionguard/ballot/ByteArraySerialize.kt new file mode 100644 index 00000000..86c59f1d --- /dev/null +++ b/egklib/src/jvmMain/kotlin/electionguard/ballot/ByteArraySerialize.kt @@ -0,0 +1,95 @@ +package electionguard.ballot + +import com.github.michaelbull.result.Ok +import com.github.michaelbull.result.Result +import electionguard.core.safeEnumValueOf +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.io.OutputStream + +/////////////////////////////////////////////////////////// +// ad-hoc encoding + +// val overvotes: List, +// val writeIns: List, +// val status: ContestDataStatus = if (overvotes.isNotEmpty()) ContestDataStatus.over_vote else ContestDataStatus.normal, +actual fun ContestData.encodeToByteArray(fill: String?): ByteArray { + val bas = ByteArrayOutputStream() + writeVlen(this.overvotes.size, bas) + this.overvotes.forEach { + writeVlen(it, bas) + } + writeVlen(this.writeIns.size, bas) + this.writeIns.forEach { + writeString(it, bas) + } + writeString(this.status.name, bas) + // pad with zeros + if (fill != null && fill.length > 0) { + bas.write(ByteArray(fill.length)) + } + return bas.toByteArray() +} + +private fun writeVlen(input: Int, output: OutputStream) { + var value = input + while (true) { + if (value and 0x7F.inv() == 0) { + output.write(value) + return + } else { + output.write(value and 0x7F or 0x80) + value = value ushr 7 + } + } +} +private fun writeString(input: String, output: OutputStream) { + val ba = input.toByteArray() + writeVlen(ba.size, output) + output.write(ba) +} + +actual fun ByteArray.decodeToContestData() : Result { + val bas = ByteArrayInputStream(this) + val novervotes = readVlen(bas) + val overvotes = mutableListOf() + repeat(novervotes) { + val vote = readVlen(bas) + overvotes.add(vote) + } + val nwriteins = readVlen(bas) + val writeins = mutableListOf() + repeat(nwriteins) { + val writein = readString(bas) + writeins.add(writein) + } + val name = readString(bas) + val status = safeEnumValueOf(name)?: ContestDataStatus.normal + return Ok(ContestData(overvotes, writeins, status)) +} + +private fun readVlen(input: InputStream): Int { + var ib: Int = input.read() + if (ib == -1) { + return -1 + } + var result = ib.and(0x7F) + var shift = 7 + while (ib.and(0x80) != 0) { + ib = input.read() + if (ib == -1) { + return -1 + } + val im = ib.and(0x7F).shl(shift) + result = result.or(im) + shift += 7 + } + return result +} +private fun readString(input: InputStream): String { + val size = readVlen(input) + val ba = ByteArray(size) + input.read(ba) + return String(ba) +} \ No newline at end of file diff --git a/egklib/src/jvmMain/kotlin/electionguard/publish/ConsumerJson.kt b/egklib/src/jvmMain/kotlin/electionguard/publish/ConsumerJson.kt index bfd66321..4e954852 100644 --- a/egklib/src/jvmMain/kotlin/electionguard/publish/ConsumerJson.kt +++ b/egklib/src/jvmMain/kotlin/electionguard/publish/ConsumerJson.kt @@ -13,6 +13,7 @@ import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream import java.io.ByteArrayInputStream +import java.io.InputStream import java.nio.file.* import java.nio.file.spi.FileSystemProvider import java.util.function.Predicate @@ -494,4 +495,25 @@ fun Path.pathListNoDirs(): List { return Files.walk(this, 1).use { fileStream -> fileStream.filter { it != this && !it.isDirectory() }.toList() } +} + +// variable length (base 128) int32 +fun readVlen(input: InputStream): Int { + var ib: Int = input.read() + if (ib == -1) { + return -1 + } + + var result = ib.and(0x7F) + var shift = 7 + while (ib.and(0x80) != 0) { + ib = input.read() + if (ib == -1) { + return -1 + } + val im = ib.and(0x7F).shl(shift) + result = result.or(im) + shift += 7 + } + return result } \ No newline at end of file diff --git a/egklib/src/jvmMain/kotlin/electionguard/publish/ConsumerProto.kt b/egklib/src/jvmMain/kotlin/electionguard/publish/ConsumerProto.kt index 567bfc63..37ac489d 100644 --- a/egklib/src/jvmMain/kotlin/electionguard/publish/ConsumerProto.kt +++ b/egklib/src/jvmMain/kotlin/electionguard/publish/ConsumerProto.kt @@ -473,24 +473,3 @@ actual class ConsumerProto actual constructor(val topDir: String, val groupConte } } } - -// variable length (base 128) int32 -fun readVlen(input: InputStream): Int { - var ib: Int = input.read() - if (ib == -1) { - return -1 - } - - var result = ib.and(0x7F) - var shift = 7 - while (ib.and(0x80) != 0) { - ib = input.read() - if (ib == -1) { - return -1 - } - val im = ib.and(0x7F).shl(shift) - result = result.or(im) - shift += 7 - } - return result -} diff --git a/egklib/src/jvmMain/kotlin/electionguard/publish/PublisherJson.kt b/egklib/src/jvmMain/kotlin/electionguard/publish/PublisherJson.kt index ec91ec0b..31f0a4c3 100644 --- a/egklib/src/jvmMain/kotlin/electionguard/publish/PublisherJson.kt +++ b/egklib/src/jvmMain/kotlin/electionguard/publish/PublisherJson.kt @@ -6,7 +6,10 @@ import electionguard.keyceremony.KeyCeremonyTrustee import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.json.encodeToStream +import java.io.File import java.io.FileOutputStream +import java.io.OutputStream +import java.nio.file.Files import java.nio.file.Path import java.util.* @@ -155,4 +158,49 @@ actual class PublisherJson actual constructor(topDir: String, createNew: Boolean } } +} + +/** Delete everything in the given directory, but leave that directory. */ +fun removeAllFiles(path: Path) { + if (!path.toFile().exists()) { + return + } + Files.walk(path) + .filter { p: Path -> p != path } + .map { obj: Path -> obj.toFile() } + .sorted { o1: File, o2: File? -> -o1.compareTo(o2) } + .forEach { f: File -> f.delete() } +} + +fun writeVlen(input: Int, output: OutputStream) { + var value = input + while (true) { + if (value and 0x7F.inv() == 0) { + output.write(value) + return + } else { + output.write(value and 0x7F or 0x80) + value = value ushr 7 + } + } +} + +/** Make sure output directories exists and are writeable. */ +fun validateOutputDir(path: Path, error: Formatter): Boolean { + if (!Files.exists(path)) { + Files.createDirectories(path) + } + if (!Files.isDirectory(path)) { + error.format(" Output directory '%s' is not a directory%n", path) + return false + } + if (!Files.isWritable(path)) { + error.format(" Output directory '%s' is not writeable%n", path) + return false + } + if (!Files.isExecutable(path)) { + error.format(" Output directory '%s' is not executable%n", path) + return false + } + return true } \ No newline at end of file diff --git a/egklib/src/jvmMain/kotlin/electionguard/publish/PublisherProto.kt b/egklib/src/jvmMain/kotlin/electionguard/publish/PublisherProto.kt index 22ed8126..4eb9c6c7 100644 --- a/egklib/src/jvmMain/kotlin/electionguard/publish/PublisherProto.kt +++ b/egklib/src/jvmMain/kotlin/electionguard/publish/PublisherProto.kt @@ -188,56 +188,23 @@ actual class PublisherProto actual constructor(topDir: String, createNew: Boolea } -/** Delete everything in the given directory, but leave that directory. */ -fun removeAllFiles(path: Path) { - if (!path.toFile().exists()) { - return - } - Files.walk(path) - .filter { p: Path -> p != path } - .map { obj: Path -> obj.toFile() } - .sorted { o1: File, o2: File? -> -o1.compareTo(o2) } - .forEach { f: File -> f.delete() } +fun writeDelimitedTo(proto: pbandk.Message, output: OutputStream) { + val bb = ByteArrayOutputStream() + proto.encodeToStream(bb) + writeVlenForProto(bb.size(), output) + output.write(bb.toByteArray()) + output.flush() } - - fun writeDelimitedTo(proto: pbandk.Message, output: OutputStream) { - val bb = ByteArrayOutputStream() - proto.encodeToStream(bb) - writeVlen(bb.size(), output) - output.write(bb.toByteArray()) - output.flush() - } - - fun writeVlen(input: Int, output: OutputStream) { - var value = input - while (true) { - if (value and 0x7F.inv() == 0) { - output.write(value) - return - } else { - output.write(value and 0x7F or 0x80) - value = value ushr 7 - } +private fun writeVlenForProto(input: Int, output: OutputStream) { + var value = input + while (true) { + if (value and 0x7F.inv() == 0) { + output.write(value) + return + } else { + output.write(value and 0x7F or 0x80) + value = value ushr 7 } } - -/** Make sure output directories exists and are writeable. */ -fun validateOutputDir(path: Path, error: Formatter): Boolean { - if (!Files.exists(path)) { - Files.createDirectories(path) - } - if (!Files.isDirectory(path)) { - error.format(" Output directory '%s' is not a directory%n", path) - return false - } - if (!Files.isWritable(path)) { - error.format(" Output directory '%s' is not writeable%n", path) - return false - } - if (!Files.isExecutable(path)) { - error.format(" Output directory '%s' is not executable%n", path) - return false - } - return true -} \ No newline at end of file +}