diff --git a/egklib/build.gradle.kts b/egklib/build.gradle.kts index bf6cca24..fba4d516 100644 --- a/egklib/build.gradle.kts +++ b/egklib/build.gradle.kts @@ -10,7 +10,7 @@ repositories { } group = "electionguard-kotlin-multiplatform" -version = "2.0.3-SNAPSHOT" +version = "2.0.4-SNAPSHOT" kotlin { jvm { diff --git a/egklib/src/commonMain/kotlin/electionguard/core/Base16.kt b/egklib/src/commonMain/kotlin/electionguard/core/Base16.kt index d132de7b..eefdb7e7 100644 --- a/egklib/src/commonMain/kotlin/electionguard/core/Base16.kt +++ b/egklib/src/commonMain/kotlin/electionguard/core/Base16.kt @@ -1,5 +1,6 @@ package electionguard.core +import electionguard.core.Base16.fromHex import io.github.oshai.kotlinlogging.KotlinLogging /** @@ -88,5 +89,11 @@ object Base16 { */ fun String.fromHexSafe(): ByteArray = fromHex() ?: throw IllegalArgumentException("fromHexSafe invalid input= '$this'") +} -} \ No newline at end of file +/** + * Converts a base-16 (hexadecimal) string to an [ElementModP]. Returns null if the number is out of + * bounds or the string is malformed. + */ +fun GroupContext.base16ToElementModP(s: String): ElementModP? = + s.fromHex()?.let { binaryToElementModP(it) } \ No newline at end of file diff --git a/egklib/src/commonMain/kotlin/electionguard/core/Base64.kt b/egklib/src/commonMain/kotlin/electionguard/core/Base64.kt index 22a91519..4ebe319a 100644 --- a/egklib/src/commonMain/kotlin/electionguard/core/Base64.kt +++ b/egklib/src/commonMain/kotlin/electionguard/core/Base64.kt @@ -104,8 +104,7 @@ object Base64 { /** * Encodes all bytes from the specified byte array into a newly-allocated byte array using - * the [Base64] encoding scheme. The returned byte array is of the length of the resulting - * bytes. + * the [Base64] encoding scheme. The returned byte array is of the length of the resulting bytes. * * @param src the byte array to encode * @param useBase64URL use Base64URL else use Base64 diff --git a/egklib/src/commonMain/kotlin/electionguard/core/ElGamalCiphertext.kt b/egklib/src/commonMain/kotlin/electionguard/core/ElGamalCiphertext.kt index 765f70ed..e9b26a46 100644 --- a/egklib/src/commonMain/kotlin/electionguard/core/ElGamalCiphertext.kt +++ b/egklib/src/commonMain/kotlin/electionguard/core/ElGamalCiphertext.kt @@ -89,6 +89,7 @@ fun List.add(other: List): List.multP(): ElementModP /** Computes G^e mod p, where G is our generator */ - fun gPowP(e: ElementModQ): ElementModP + fun gPowP(exp: ElementModQ): ElementModP /** * Given an element x for which there exists an e, such that g^e = x, this will find e, @@ -199,9 +175,26 @@ interface GroupContext { * value designed not to consume too much memory (perhaps 10 million). This will consume O(e) * time, the first time, after which the results are memoized for all values between 0 and e, * for better future performance. + * If the result is not found, null is returned. */ fun dLogG(p: ElementModP, maxResult: Int = - 1): Int? + /** + * Returns a random number in [minimum, Q), where minimum defaults to zero. Promises to use a + * "secure" random number generator, such that the results are suitable for use as cryptographic keys. + * @throws IllegalArgumentException if the minimum is negative + */ + fun randomElementModQ(minimum: Int = 0) = + binaryToElementModQsafe(randomBytes(MAX_BYTES_Q), minimum) + + /** + * Returns a random number in [minimum, P), where minimum defaults to zero. Promises to use a + * "secure" random number generator, such that the results are suitable for use as cryptographic keys. + * @throws IllegalArgumentException if the minimum is negative + */ + fun randomElementModP(minimum: Int = 0) = + binaryToElementModPsafe(randomBytes(MAX_BYTES_P), minimum) + /** debugging operation counts. TODO sidechannel attack? */ fun getAndClearOpCounts(): Map } @@ -222,17 +215,6 @@ interface Element { */ fun inBounds(): Boolean - /** - * Normal computations should ensure that every [Element] is in the modular bounds defined by - * the group, but deserialization of hostile inputs or buggy code might not preserve this - * property, so it's valuable to have a way to check. This method allows anything in [1, N) - * where N is the group modulus. - */ - fun inBoundsNoZero(): Boolean - - /** Checks whether this element is zero. */ - fun isZero(): Boolean - /** Converts from any [Element] to a big-endian [ByteArray] representation. */ fun byteArray(): ByteArray @@ -252,9 +234,6 @@ interface ElementModQ : Element, Comparable { /** Computes the additive inverse */ operator fun unaryMinus(): ElementModQ - /** Computes b^e mod q. LOOK seems to be not ever used */ - infix fun powQ(e: ElementModQ): ElementModQ - /** Finds the multiplicative inverse */ fun multInv(): ElementModQ @@ -263,6 +242,9 @@ interface ElementModQ : Element, Comparable { /** Allows elements to be compared (<, >, <=, etc.) using the usual arithmetic operators. */ override operator fun compareTo(other: ElementModQ): Int + + /** Checks whether this element is zero. */ + fun isZero(): Boolean } interface ElementModP : Element, Comparable { @@ -273,7 +255,7 @@ interface ElementModP : Element, Comparable { fun isValidResidue(): Boolean /** Computes b^e mod p */ - infix fun powP(e: ElementModQ): ElementModP + infix fun powP(exp: ElementModQ): ElementModP /** Modular multiplication */ operator fun times(other: ElementModP): ElementModP @@ -299,7 +281,7 @@ interface ElementModP : Element, Comparable { /** Short version of the String for readability */ fun toStringShort(): String { - val s = base16() + val s = toHex() val len = s.length return "${s.substring(0, 7)}...${s.substring(len-8, len)}" } @@ -320,68 +302,6 @@ interface MontgomeryElementModP { val context: GroupContext } -/** - * Converts a base-16 (hexadecimal) string to an [ElementModP]. Returns null if the number is out of - * bounds or the string is malformed. - */ -fun GroupContext.base16ToElementModP(s: String): ElementModP? = - s.fromHex()?.let { binaryToElementModP(it) } - -/** - * Converts a base-16 (hexadecimal) string to an [ElementModQ]. Returns null if the number is out of - * bounds or the string is malformed. - */ -fun GroupContext.base16ToElementModQ(s: String): ElementModQ? = - s.fromHex()?.let { binaryToElementModQ(it) } - -/** - * Converts a base-16 (hexadecimal) string to an [ElementModP]. Guarantees the result is in [0, P), - * by computing the result mod P. - */ -fun GroupContext.base16ToElementModPsafe(s: String): ElementModP = - s.fromHex()?.let { binaryToElementModPsafe(it) } ?: ZERO_MOD_P - -/** - * Converts a base-16 (hexadecimal) string to an [ElementModQ]. Guarantees the result is in [0, Q), - * by computing the result mod Q. - */ -fun GroupContext.base16ToElementModQsafe(s: String): ElementModQ = - s.fromHex()?.let { binaryToElementModQsafe(it) } ?: ZERO_MOD_Q - -/** - * Converts a base-64 string to an [ElementModP]. Returns null if the number is out of bounds or the - * string is malformed. - */ -fun GroupContext.base64ToElementModP(s: String): ElementModP? = - s.fromBase64()?.let { binaryToElementModP(it) } - -/** - * Converts a base-64 string to an [ElementModQ]. Returns null if the number is out of bounds or the - * string is malformed. - */ -fun GroupContext.base64ToElementModQ(s: String): ElementModQ? = - s.fromBase64()?.let { binaryToElementModQ(it) } - -/** - * Converts a base-64 string to an [ElementModP]. Guarantees the result is in [0, P), by computing - * the result mod P. - */ -fun GroupContext.base64ToElementModPsafe(s: String): ElementModP = - s.fromBase64()?.let { binaryToElementModPsafe(it) } ?: ZERO_MOD_P - -/** - * Converts a base-64 string to an [ElementModQ]. Guarantees the result is in [0, Q), by computing - * the result mod Q. - */ -fun GroupContext.base64ToElementModQsafe(s: String): ElementModQ = - s.fromBase64()?.let { binaryToElementModQsafe(it) } ?: ZERO_MOD_Q - -/** Converts from any [Element] to a base64 string representation. */ -fun Element.base64(): String = byteArray().toBase64() - -/** Converts from any [Element] to a base16 (hexadecimal) string representation. */ -fun Element.base16(): String = byteArray().toHex() - /** Converts an integer to an ElementModQ, with optimizations when possible for small integers */ fun Int.toElementModQ(ctx: GroupContext) = when { @@ -400,28 +320,6 @@ fun Long.toElementModQ(ctx: GroupContext) = else -> ctx.uLongToElementModQ(this.toULong()) } -/** - * Returns a random number in [minimum, Q), where minimum defaults to zero. Promises to use a - * "secure" random number generator, such that the results are suitable for use as cryptographic keys. - * - * @throws IllegalArgumentException if the minimum is negative - */ -fun GroupContext.randomElementModQ(minimum: Int = 0) = - binaryToElementModQsafe(randomBytes(MAX_BYTES_Q), minimum) - -/** - * We often want to raise g to small powers, for which we've conveniently pre-computed the answers. - * This function will fall back to use [GroupContext.gPowP] if the input isn't precomputed. - */ -fun GroupContext.gPowPSmall(e: Int) = - when { - e == 0 -> ONE_MOD_P - e == 1 -> G_MOD_P - e == 2 -> G_SQUARED_MOD_P - e < 0 -> throw ArithmeticException("not defined for negative values") - else -> gPowP(e.toElementModQ(this)) - } - /** * Verifies that every element has a compatible [GroupContext] and returns the first context. * @@ -462,7 +360,7 @@ fun ElementModP.dLogG(maxResult: Int = -1): Int? = context.dLogG(this, maxResult * `acceleration` parameter, to specify the speed versus memory tradeoff for subsequent computation. * See [PowRadixOption] for details. Note that this function can return `null`, which indicates that * the [ElectionConstants] were incompatible with this particular library. - */ + * fun ElectionConstants.toGroupContext( acceleration: PowRadixOption = PowRadixOption.LOW_MEMORY_USE ) : GroupContext? { @@ -481,6 +379,7 @@ fun ElectionConstants.toGroupContext( } } } +*/ /** * Computes the sum of the given elements, mod q; this can be faster than using the addition diff --git a/egklib/src/commonMain/kotlin/electionguard/core/Utils.kt b/egklib/src/commonMain/kotlin/electionguard/core/Utils.kt index 4e0d9d8a..ebd12722 100644 --- a/egklib/src/commonMain/kotlin/electionguard/core/Utils.kt +++ b/egklib/src/commonMain/kotlin/electionguard/core/Utils.kt @@ -106,8 +106,7 @@ fun List.noNullValuesOrNull(): List? { /** * Normally, Kotlin's `Enum.valueOf` or [enumValueOf] method will throw an exception for an invalid - * input. This method will instead return `null` if the string doesn't map to a valid value of the - * enum. + * input. This method will instead return `null` if the string doesn't map to a valid value of the enum. */ inline fun > safeEnumValueOf(name: String?): T? { if (name == null) { diff --git a/egklib/src/commonMain/kotlin/electionguard/json/GuardianJsonR.kt b/egklib/src/commonMain/kotlin/electionguard/json/GuardianJsonR.kt index fa1bda44..e9a74799 100644 --- a/egklib/src/commonMain/kotlin/electionguard/json/GuardianJsonR.kt +++ b/egklib/src/commonMain/kotlin/electionguard/json/GuardianJsonR.kt @@ -43,7 +43,7 @@ fun GuardianJsonR.import(group: GroupContext, errs : ErrorMessages) : GuardianR? fun GuardianR.convert(group: GroupContext) = Guardian( this.name, this.i, - // Schnorr proof is missing - fake it + // Schnorr proof is missing - fake it TODO WTF? this.coefficient_commitments.map { SchnorrProof(it, group.ONE_MOD_Q, group.TWO_MOD_Q) } ) diff --git a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyTally.kt b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyTally.kt index 377666b5..619896a6 100644 --- a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyTally.kt +++ b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyTally.kt @@ -39,12 +39,12 @@ class VerifyTally( if (selection.encryptedVote.data != accum.data) { errs.add(" 8.B Ballot Aggregation does not match: $key") } - } else { + } /* else { // TODO what is it? is it needed? left over from placeholders ?? if (selection.encryptedVote.pad != group.ZERO_MOD_P || selection.encryptedVote.data != group.ZERO_MOD_P) { errs.add(" Ballot Aggregation empty does not match $key") } - } + } */ } } diff --git a/egklib/src/commonTest/kotlin/electionguard/core/GroupTest.kt b/egklib/src/commonTest/kotlin/electionguard/core/GroupTest.kt index fcd9cfae..537ffe5a 100644 --- a/egklib/src/commonTest/kotlin/electionguard/core/GroupTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/core/GroupTest.kt @@ -1,5 +1,7 @@ package electionguard.core +import electionguard.core.Base64.fromBase64 +import electionguard.core.Base64.toBase64 import io.kotest.property.Arb import io.kotest.property.arbitrary.int import io.kotest.property.checkAll @@ -110,6 +112,23 @@ class GroupTest { } } + /** + * Converts a base-64 string to an [ElementModQ]. Returns null if the number is out of bounds or the + * string is malformed. + */ + fun GroupContext.base64ToElementModQ(s: String): ElementModQ? = + s.fromBase64()?.let { binaryToElementModQ(it) } + + /** + * Converts a base-64 string to an [ElementModP]. Returns null if the number is out of bounds or the + * string is malformed. + */ + fun GroupContext.base64ToElementModP(s: String): ElementModP? = + s.fromBase64()?.let { binaryToElementModP(it) } + + /** Converts from any [Element] to a base64 string representation. */ + fun Element.base64(): String = byteArray().toBase64() + @Test fun baseConversionFails() { runTest { @@ -325,39 +344,6 @@ class GroupTest { } } - @Test - fun exponentiationQ4096() = exponentiationQ { productionGroup(mode = ProductionMode.Mode4096) } - - @Test - fun exponentiationQ3072() = exponentiationQ { productionGroup(mode = ProductionMode.Mode3072) } - - fun exponentiationQ(contextF: () -> GroupContext) { - runTest { - val context = contextF() - val qMinus1 = context.ZERO_MOD_Q - context.ONE_MOD_Q - - checkAll(propTestFastConfig, elementsModQNoZero(context)) { - assertEquals(it * it, it powQ context.TWO_MOD_Q) - assertEquals(context.ONE_MOD_Q, it powQ qMinus1) - } - } - } - - @Test - fun exponentiationQSm() { - runTest { - val context = tinyGroup() - val qMinus1 = context.ZERO_MOD_Q - context.ONE_MOD_Q - - // note: unlike the production group, here we're going to let the - // checker search much harder for a counterexample - checkAll(elementsModQNoZero(context)) { - assertEquals(it * it, it powQ context.TWO_MOD_Q) - assertEquals(context.ONE_MOD_Q, it powQ qMinus1) - } - } - } - @Test fun exponentiation4096() = exponentiation { productionGroup(mode = ProductionMode.Mode4096) } diff --git a/egklib/src/commonTest/kotlin/electionguard/core/HashTest.kt b/egklib/src/commonTest/kotlin/electionguard/core/HashTest.kt index bcfc704d..21e7cb91 100644 --- a/egklib/src/commonTest/kotlin/electionguard/core/HashTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/core/HashTest.kt @@ -79,9 +79,9 @@ class HashTest { runTest { val group = productionGroup() val s1q = "C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206".fromHexSafe() - .toUInt256safe().toElementModQ(group).base16() + .toUInt256safe().toElementModQ(group).toHex() val s2q = "000C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206".fromHexSafe() - .toUInt256safe().toElementModQ(group).base16() + .toUInt256safe().toElementModQ(group).toHex() assertEquals(s1q, s2q) assertEquals(s1q.encodeToByteArray().size, s2q.encodeToByteArray().size) assertEquals(hashFunction(s1q.encodeToByteArray(), s1q), hashFunction(s2q.encodeToByteArray(), s2q)) @@ -94,7 +94,7 @@ class HashTest { fun testElementModQToHex() { runTest { val group = productionGroup() - val subject = group.TWO_MOD_Q.base16() + val subject = group.TWO_MOD_Q.toHex() println(" len = ${subject.length} hex = '${subject}'") assertEquals(64, subject.length) } diff --git a/egklib/src/commonTest/kotlin/electionguard/core/PowRadixTiming.kt b/egklib/src/commonTest/kotlin/electionguard/core/PowRadixTiming.kt index 8800b3ad..debe1754 100644 --- a/egklib/src/commonTest/kotlin/electionguard/core/PowRadixTiming.kt +++ b/egklib/src/commonTest/kotlin/electionguard/core/PowRadixTiming.kt @@ -80,7 +80,7 @@ class PowRadixTiming { val nonces = List(n) { group.randomElementModQ() } var stopwatch = Stopwatch() - repeat(n) { require( !group.gPowP(nonces[it]).isZero()) } + repeat(n) { group.gPowP(nonces[it]) } var duration = stopwatch.stop() val peracc = duration.toDouble() / n / 1_000_000 println(" ${count.pad(4)} acc $n = ${peracc.sigfig(3)} msec per acc") @@ -102,14 +102,14 @@ class PowRadixTiming { val h = group.gPowP(group.randomElementModQ()) var stopwatch = Stopwatch() - repeat(n) { require( !group.gPowP(nonces[it]).isZero()) } + repeat(n) { group.gPowP(nonces[it]) } var duration = stopwatch.stop() val peracc = duration.toDouble() / n / 1_000_000 println(" acc took $duration msec for $n = $peracc msec per acc") stopwatch.start() - repeat(n) { require(!(h powP nonces[it]).isZero()) } + repeat(n) { h powP nonces[it] } duration = stopwatch.stop() val perexp = duration.toDouble() / n / 1_000_000 @@ -133,7 +133,7 @@ class PowRadixTiming { val elemps = nonces.map { group.gPowP(it) } var starting = getSystemTimeInMillis() - val prod = elemps.reduce { a, b -> a * b } + elemps.reduce { a, b -> a * b } var duration = getSystemTimeInMillis() - starting var peracc = duration.toDouble() / n println(" multiply took $duration msec for $n = $peracc msec per multiply") diff --git a/egklib/src/commonTest/kotlin/electionguard/core/TestHelpers.kt b/egklib/src/commonTest/kotlin/electionguard/core/TestHelpers.kt index b4952b12..ede53fa6 100644 --- a/egklib/src/commonTest/kotlin/electionguard/core/TestHelpers.kt +++ b/egklib/src/commonTest/kotlin/electionguard/core/TestHelpers.kt @@ -72,7 +72,7 @@ fun generateUInt256(context: GroupContext): UInt256 { return generateElementModQ(context).toUInt256safe(); } -fun generateElementModP(context: GroupContext) = context.uIntToElementModP(Random.nextUInt(1879047647.toUInt())) +fun generateElementModP(context: GroupContext) = context.randomElementModP() fun generatePublicKey(group: GroupContext): ElementModP = group.gPowP(group.randomElementModQ()) diff --git a/egklib/src/commonTest/kotlin/electionguard/core/TinyGroup.kt b/egklib/src/commonTest/kotlin/electionguard/core/TinyGroup.kt index ed537e4d..0c006513 100644 --- a/egklib/src/commonTest/kotlin/electionguard/core/TinyGroup.kt +++ b/egklib/src/commonTest/kotlin/electionguard/core/TinyGroup.kt @@ -38,13 +38,10 @@ internal class TinyGroupContext( val r: UInt, val name: String, ) : GroupContext { - val zeroModP: ElementModP val oneModP: ElementModP - val twoModP: ElementModP val gModP: ElementModP val gInvModP by lazy { gPowP(qMinus1Q) } val gSquaredModP: ElementModP - val qModP: ElementModP val zeroModQ: ElementModQ val oneModQ: ElementModQ val twoModQ: ElementModQ @@ -52,12 +49,9 @@ internal class TinyGroupContext( val qMinus1Q: ElementModQ init { - zeroModP = TinyElementModP(0U, this) oneModP = TinyElementModP(1U, this) - twoModP = TinyElementModP(2U, this) gModP = TinyElementModP(g, this).acceleratePow() gSquaredModP = TinyElementModP((g * g) % p, this) - qModP = TinyElementModP(q, this) zeroModQ = TinyElementModQ(0U, this) oneModQ = TinyElementModQ(1U, this) twoModQ = TinyElementModQ(2U, this) @@ -80,20 +74,14 @@ internal class TinyGroupContext( override fun toString(): String = name - override val ZERO_MOD_P: ElementModP - get() = zeroModP override val ONE_MOD_P: ElementModP get() = oneModP - override val TWO_MOD_P: ElementModP - get() = twoModP override val G_MOD_P: ElementModP get() = gModP override val GINV_MOD_P: ElementModP get() = gInvModP override val G_SQUARED_MOD_P: ElementModP get() = gSquaredModP - override val Q_MOD_P: ElementModP - get() = qModP override val ZERO_MOD_Q: ElementModQ get() = zeroModQ override val ONE_MOD_Q: ElementModQ @@ -172,7 +160,7 @@ internal class TinyGroupContext( else TinyElementModQ(i.toUInt(), this) // hmmm, could we use a different group here? - override fun uIntToElementModP(i: UInt): ElementModP = + fun uIntToElementModP(i: UInt): ElementModP = if (i >= p) throw ArithmeticException("out of bounds for TestElementModP: $i") else @@ -186,7 +174,7 @@ internal class TinyGroupContext( fold(1UL) { a, b -> (a * b.getCompat(this@TinyGroupContext).toULong()) % p }.toUInt() ) - override fun gPowP(e: ElementModQ): ElementModP = gModP powP e + override fun gPowP(exp: ElementModQ): ElementModP = gModP powP exp override fun dLogG(p: ElementModP, maxResult: Int): Int? = dlogger.dLog(p, maxResult) @@ -205,15 +193,15 @@ internal class TinyElementModP(val element: UInt, val groupContext: TinyGroupCon return inBounds() && residue } - override fun powP(e: ElementModQ): ElementModP { + override fun powP(exp: ElementModQ): ElementModP { var result: ULong = 1U var base: ULong = element.toULong() - val exp: UInt = e.getCompat(groupContext) + val expc: UInt = exp.getCompat(groupContext) // we know that all the bits above this are zero because q < 2^28 (0..28) .forEach { bit -> - val eBitSet = ((exp shr bit) and 1U) == 1U + val eBitSet = ((expc shr bit) and 1U) == 1U // We're doing arithmetic in the larger 64-bit space, but we'll never overflow // because the internal values are always mod p or q, and thus fit in 32 bits. @@ -241,10 +229,6 @@ internal class TinyElementModP(val element: UInt, val groupContext: TinyGroupCon override fun inBounds(): Boolean = element < groupContext.p - override fun inBoundsNoZero(): Boolean = element > 0U && inBounds() - - override fun isZero(): Boolean = element == 0U - override fun byteArray(): ByteArray = element.toByteArray() override fun equals(other: Any?): Boolean = other is TinyElementModP && element == other.element @@ -312,24 +296,6 @@ internal class TinyElementModQ(val element: UInt, val groupContext: TinyGroupCon override fun div(denominator: ElementModQ): ElementModQ = this * denominator.multInv() - override infix fun powQ(e: ElementModQ): ElementModQ { - var result: ULong = 1U - var base: ULong = element.toULong() - val exp: UInt = e.getCompat(groupContext) - - // we know that all the bits above this are zero because q < 2^28 - (0..28) - .forEach { bit -> - val eBitSet = ((exp shr bit) and 1U) == 1U - - // We're doing arithmetic in the larger 64-bit space, but we'll never overflow - // because the internal values are always mod p or q, and thus fit in 32 bits. - if (eBitSet) result = (result * base) % groupContext.q - base = (base * base) % groupContext.q - } - return result.wrap() - } - override fun compareTo(other: ElementModQ): Int = element.compareTo(other.getCompat(groupContext)) @@ -338,8 +304,6 @@ internal class TinyElementModQ(val element: UInt, val groupContext: TinyGroupCon override fun inBounds(): Boolean = element < groupContext.q - override fun inBoundsNoZero(): Boolean = element > 0U && inBounds() - override fun isZero(): Boolean = element == 0U override fun byteArray(): ByteArray = element.toByteArray() diff --git a/egklib/src/commonTest/kotlin/electionguard/core/UInt256Test.kt b/egklib/src/commonTest/kotlin/electionguard/core/UInt256Test.kt index a5d35c08..68589580 100644 --- a/egklib/src/commonTest/kotlin/electionguard/core/UInt256Test.kt +++ b/egklib/src/commonTest/kotlin/electionguard/core/UInt256Test.kt @@ -34,7 +34,7 @@ class UInt256Test { assertEquals(32, s1u.bytes.size) val s1q = s1u.toElementModQ(group) - assertEquals(64, s1q.base16().length) + assertEquals(64, s1q.toHex().length) } } } \ No newline at end of file diff --git a/egklib/src/commonTest/kotlin/electionguard/workflow/WorkflowEncryptDecryptTest.kt b/egklib/src/commonTest/kotlin/electionguard/workflow/WorkflowEncryptDecryptTest.kt index 2ce9a05f..bb328353 100644 --- a/egklib/src/commonTest/kotlin/electionguard/workflow/WorkflowEncryptDecryptTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/workflow/WorkflowEncryptDecryptTest.kt @@ -9,7 +9,6 @@ import electionguard.core.elGamalKeyPairFromRandom import electionguard.core.encrypt import electionguard.core.encryptedSum import electionguard.core.productionGroup -import electionguard.core.randomElementModQ import electionguard.core.runTest import electionguard.core.toElementModQ import kotlin.test.Test diff --git a/egklib/src/jvmMain/kotlin/electionguard/core/Group.kt b/egklib/src/jvmMain/kotlin/electionguard/core/Group.kt index 2d956a9e..33016ae5 100644 --- a/egklib/src/jvmMain/kotlin/electionguard/core/Group.kt +++ b/egklib/src/jvmMain/kotlin/electionguard/core/Group.kt @@ -1,6 +1,7 @@ package electionguard.core import electionguard.ballot.ElectionConstants +import electionguard.core.Base16.toHex import java.math.BigInteger import java.util.concurrent.atomic.AtomicInteger @@ -74,13 +75,10 @@ class ProductionGroupContext( val q: BigInteger val g: BigInteger val r: BigInteger - val zeroModP: ProductionElementModP val oneModP: ProductionElementModP - val twoModP: ProductionElementModP val gModP: ProductionElementModP val gInvModP by lazy { gPowP(qMinus1Q) } val gSquaredModP: ProductionElementModP - val qModP: ProductionElementModP val zeroModQ: ProductionElementModQ val oneModQ: ProductionElementModQ val twoModQ: ProductionElementModQ @@ -95,12 +93,9 @@ class ProductionGroupContext( q = qBytes.toBigInteger() g = gBytes.toBigInteger() r = rBytes.toBigInteger() - zeroModP = ProductionElementModP(0U.toBigInteger(), this) oneModP = ProductionElementModP(1U.toBigInteger(), this) - twoModP = ProductionElementModP(2U.toBigInteger(), this) gModP = ProductionElementModP(g, this).acceleratePow() as ProductionElementModP gSquaredModP = ProductionElementModP((g * g).mod(p), this) - qModP = ProductionElementModP(q, this) zeroModQ = ProductionElementModQ(0U.toBigInteger(), this) oneModQ = ProductionElementModQ(1U.toBigInteger(), this) twoModQ = ProductionElementModQ(2U.toBigInteger(), this) @@ -119,15 +114,9 @@ class ProductionGroupContext( override fun toString() : String = name - override val ZERO_MOD_P - get() = zeroModP - override val ONE_MOD_P get() = oneModP - override val TWO_MOD_P - get() = twoModP - override val G_MOD_P get() = gModP @@ -137,9 +126,6 @@ class ProductionGroupContext( override val G_SQUARED_MOD_P get() = gSquaredModP - override val Q_MOD_P - get() = qModP - override val ZERO_MOD_Q get() = zeroModQ @@ -213,13 +199,6 @@ class ProductionGroupContext( else -> ProductionElementModQ(i.toBigInteger(), this) } - override fun uIntToElementModP(i: UInt) : ElementModP = when (i) { - 0U -> ZERO_MOD_P - 1U -> ONE_MOD_P - 2U -> TWO_MOD_P - else -> ProductionElementModP(i.toBigInteger(), this) - } - override fun Iterable.addQ(): ElementModQ { val input = iterator().asSequence().toList() @@ -262,7 +241,7 @@ class ProductionGroupContext( return ProductionElementModP(result, this@ProductionGroupContext) } - override fun gPowP(e: ElementModQ) = gModP powP e + override fun gPowP(exp: ElementModQ) = gModP powP exp override fun dLogG(p: ElementModP, maxResult: Int): Int? = dlogger.dLog(p, maxResult) @@ -299,8 +278,6 @@ class ProductionElementModQ(internal val element: BigInteger, val groupContext: override fun inBounds() = element >= BigInteger.ZERO && element < groupContext.q - override fun inBoundsNoZero() = inBounds() && !isZero() - override operator fun compareTo(other: ElementModQ): Int = element.compareTo(other.getCompat(groupContext)) override operator fun plus(other: ElementModQ) = @@ -323,10 +300,6 @@ class ProductionElementModQ(internal val element: BigInteger, val groupContext: override infix operator fun div(denominator: ElementModQ): ElementModQ = this * denominator.multInv() - override infix fun powQ(e: ElementModQ): ElementModQ { - groupContext.opCounts.getOrPut("exp") { AtomicInteger(0) }.incrementAndGet() - return this.element.modPow(e.getCompat(groupContext), groupContext.q).wrap() - } override fun equals(other: Any?) = when (other) { is ElementModQ -> byteArray().contentEquals(other.byteArray()) @@ -335,7 +308,7 @@ class ProductionElementModQ(internal val element: BigInteger, val groupContext: override fun hashCode() = byteArray().contentHashCode() - override fun toString() = base16() + override fun toString() = byteArray().toHex() } open class ProductionElementModP(internal val element: BigInteger, val groupContext: ProductionGroupContext): ElementModP, @@ -349,12 +322,8 @@ open class ProductionElementModP(internal val element: BigInteger, val groupCont override val context: GroupContext get() = groupContext - override fun isZero() = element == BigInteger.ZERO - override fun inBounds() = element >= BigInteger.ZERO && element < groupContext.p - override fun inBoundsNoZero() = inBounds() && !isZero() - override operator fun compareTo(other: ElementModP): Int = element.compareTo(other.getCompat(groupContext)) override fun isValidResidue(): Boolean { @@ -363,9 +332,9 @@ open class ProductionElementModP(internal val element: BigInteger, val groupCont return inBounds() && residue } - override infix fun powP(e: ElementModQ) : ElementModP { + override infix fun powP(exp: ElementModQ) : ElementModP { groupContext.opCounts.getOrPut("exp") { AtomicInteger(0) }.incrementAndGet() - return this.element.modPow(e.getCompat(groupContext), groupContext.p).wrap() + return this.element.modPow(exp.getCompat(groupContext), groupContext.p).wrap() } override operator fun times(other: ElementModP) = @@ -397,7 +366,7 @@ open class ProductionElementModP(internal val element: BigInteger, val groupCont override fun hashCode() = byteArray().contentHashCode() - override fun toString() = base16() + override fun toString() = byteArray().toHex() } class AcceleratedElementModP(p: ProductionElementModP) : ProductionElementModP(p.element, p.groupContext) { @@ -408,9 +377,9 @@ class AcceleratedElementModP(p: ProductionElementModP) : ProductionElementModP(p override fun acceleratePow(): ElementModP = this - override infix fun powP(e: ElementModQ) : ElementModP { + override infix fun powP(exp: ElementModQ) : ElementModP { groupContext.opCounts.getOrPut("acc") { AtomicInteger(0) }.incrementAndGet() - return powRadix.pow(e) + return powRadix.pow(exp) } } diff --git a/egklib/src/jvmMain/kotlin/electionguard/util/StopWatch.kt b/egklib/src/jvmMain/kotlin/electionguard/util/StopWatch.kt index 91c22cad..1d637ecb 100644 --- a/egklib/src/jvmMain/kotlin/electionguard/util/StopWatch.kt +++ b/egklib/src/jvmMain/kotlin/electionguard/util/StopWatch.kt @@ -54,7 +54,7 @@ class Stopwatch(running: Boolean = true) { fun perRow(took: Long, nrows: Int): String { val tookMs = took / 1_000_000 val perRow = tookMs.toDouble() / nrows - return "took ${tookMs} ms for $nrows rows, ${perRow.sigfig(2)} ms per row" + return "took ${tookMs} ms for $nrows rows, ${perRow.sigfig(3)} ms per row" } // TODO units option @@ -62,13 +62,13 @@ class Stopwatch(running: Boolean = true) { val ratio = num.toDouble() / den val numValue = num / 1_000_000 val denValue = den / 1_000_000 - return "$numValue / $denValue ms = ${ratio.sigfig(2)}" + return "$numValue / $denValue ms = ${ratio.sigfig(3)}" } fun perRow(num: Long, den: Long, nrows: Int): String { val numValue = num.toDouble() / nrows / 1_000_000 val denValue = den.toDouble() / nrows / 1_000_000 - return "${numValue.sigfig(2)} / ${denValue.sigfig(2)} ms per row" + return "${numValue.sigfig(2)} / ${denValue.sigfig(3)} ms per row" } fun ratioAndPer(num: Long, den: Long, nrows: Int): String { diff --git a/egklib/src/jvmTest/kotlin/electionguard/core/ElementEqualityJvmTest.kt b/egklib/src/jvmTest/kotlin/electionguard/core/ElementEqualityJvmTest.kt index 72081a96..b17e862a 100644 --- a/egklib/src/jvmTest/kotlin/electionguard/core/ElementEqualityJvmTest.kt +++ b/egklib/src/jvmTest/kotlin/electionguard/core/ElementEqualityJvmTest.kt @@ -22,7 +22,7 @@ class ElementEqualityJvmTest { assertEquals(biu2.toString(), "UInt256(0xC59AAD302F149A018F925AEC7B819C6F890441F0954C36C198FD0066C5A93F11)") assertEquals(biu2, biu) - assertEquals(biq2.base16(), biq.base16()) + assertEquals(biq2.toHex(), biq.toHex()) // ElementModQ now equal because it uses normalized bytes assertNotEquals(bi2, bi) diff --git a/egklib/src/jvmTest/kotlin/electionguard/core/TestSetMembership.kt b/egklib/src/jvmTest/kotlin/electionguard/core/TestSetMembership.kt index e7cbb4a6..bbc785a2 100644 --- a/egklib/src/jvmTest/kotlin/electionguard/core/TestSetMembership.kt +++ b/egklib/src/jvmTest/kotlin/electionguard/core/TestSetMembership.kt @@ -9,7 +9,13 @@ class TestSetMembership { @Test fun testSetMembershipZero() { - assertFalse(checkMembership(group.ZERO_MOD_P)) + val zero = group.binaryToElementModP(ByteArray(11))!! + assertFalse(checkMembership(zero)) + } + + @Test + fun testSetMembershipOne() { + assertTrue(checkMembership(group.ONE_MOD_P)) } @Test @@ -21,7 +27,8 @@ class TestSetMembership { @Test fun testSetMembershiNotG() { - val checkit = group.TWO_MOD_P powP group.TWO_MOD_Q + val two = group.binaryToElementModP(ByteArray(1) { 2 })!! + val checkit = two powP group.TWO_MOD_Q assertFalse(checkMembership(checkit)) } diff --git a/egklib/src/jvmTest/kotlin/electionguard/testvectors/BallotChainingTestVector.kt b/egklib/src/jvmTest/kotlin/electionguard/testvectors/BallotChainingTestVector.kt index 96bf609a..517942ff 100644 --- a/egklib/src/jvmTest/kotlin/electionguard/testvectors/BallotChainingTestVector.kt +++ b/egklib/src/jvmTest/kotlin/electionguard/testvectors/BallotChainingTestVector.kt @@ -143,7 +143,7 @@ class BallotChainingTestVector { jsonReader.decodeFromStream(inp) } - val publicKey = ElGamalPublicKey(group.base16ToElementModPsafe(testVector.joint_public_key)) + val publicKey = ElGamalPublicKey(group.base16ToElementModP(testVector.joint_public_key)!!) val extendedBaseHash = UInt256(testVector.extended_base_hash.fromHexSafe()) val configBaux0 : ByteArray = testVector.configBaux0 val ballotsZipped = testVector.ballots.zip(testVector.expected_encrypted_ballots) diff --git a/egklib/src/jvmTest/kotlin/electionguard/testvectors/ConfirmationCodeTestVector.kt b/egklib/src/jvmTest/kotlin/electionguard/testvectors/ConfirmationCodeTestVector.kt index f64a3bc2..aa153ba7 100644 --- a/egklib/src/jvmTest/kotlin/electionguard/testvectors/ConfirmationCodeTestVector.kt +++ b/egklib/src/jvmTest/kotlin/electionguard/testvectors/ConfirmationCodeTestVector.kt @@ -130,7 +130,7 @@ class ConfirmationCodeTestVector { jsonReader.decodeFromStream(inp) } - val publicKey = ElGamalPublicKey(group.base16ToElementModPsafe(testVector.joint_public_key)) + val publicKey = ElGamalPublicKey(group.base16ToElementModP(testVector.joint_public_key)!!) val extendedBaseHash = UInt256(testVector.extended_base_hash.fromHex()!!) val ballotsZipped = testVector.ballots.zip(testVector.expected_encrypted_ballots) diff --git a/egkliball/build.gradle.kts b/egkliball/build.gradle.kts index 02e04af5..27d96597 100644 --- a/egkliball/build.gradle.kts +++ b/egkliball/build.gradle.kts @@ -9,7 +9,7 @@ repositories { } dependencies { - implementation(files("../egklib/build/libs/egklib-jvm-2.0.3-SNAPSHOT.jar")) // add the library to the fatJar + implementation(files("../egklib/build/libs/egklib-jvm-2.0.4-SNAPSHOT.jar")) // add the library to the fatJar implementation(libs.bundles.eglib) implementation(libs.logback.classic) }