diff --git a/.idea/misc.xml b/.idea/misc.xml index e0995c0..7b269bb 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/README.md b/README.md index be9d64f..b12664b 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/blob/main/LICENSE.txt) ![GitHub branch checks state](https://img.shields.io/github/actions/workflow/status/JohnLCaron/egk-ec/unit-tests.yml) -![Coverage](https://img.shields.io/badge/coverage-90.4%25%20LOC%20(6991/7730)-blue) +![Coverage](https://img.shields.io/badge/coverage-90.5%25%20LOC%20(6991/7729)-blue) # ElectionGuard-Kotlin Elliptic Curve -_last update 04/30/2024_ +_last update 05/05/2024_ EGK Elliptic Curve (egk-ec) is an experimental implementation of [ElectionGuard](https://github.com/microsoft/electionguard), [version 2.0](https://github.com/microsoft/electionguard/releases/download/v2.0/EG_Spec_2_0.pdf), diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/DLogarithm.kt b/src/main/kotlin/org/cryptobiotic/eg/core/DLogarithm.kt index 6c25a09..5fd6234 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/core/DLogarithm.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/core/DLogarithm.kt @@ -17,10 +17,10 @@ class DLogarithm(val base: ElementModP) { private val dLogMapping: MutableMap = ConcurrentHashMap() .apply { - this[base.context.ONE_MOD_P] = 0 + this[base.group.ONE_MOD_P] = 0 } - private var dLogMaxElement = base.context.ONE_MOD_P + private var dLogMaxElement = base.group.ONE_MOD_P private var dLogMaxExponent = 0 private val mutex = Mutex() diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/ElGamalCiphertext.kt b/src/main/kotlin/org/cryptobiotic/eg/core/ElGamalCiphertext.kt index 5c079f8..c1cdb15 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/core/ElGamalCiphertext.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/core/ElGamalCiphertext.kt @@ -91,13 +91,13 @@ fun List.add(other: List): List ctx.uLongToElementModQ(this.toULong()) } +/** + * Throughout our bignum arithmetic, every operation needs to check that its operands are compatible + * (i.e., that we're not trying to use the test group and the production group interchangeably). + * This will verify that compatibility and throw an `ArithmeticException` if they're not. + */ +fun GroupContext.assertCompatible(other: GroupContext) { + if (!this.isCompatible(other)) { + throw ArithmeticException("incompatible group contexts") + } +} + /** * Verifies that every element has a compatible [GroupContext] and returns the first context. * @@ -217,12 +227,12 @@ fun compatibleContextOrFail(vararg elements: Element): GroupContext { if (elements.isEmpty()) throw IllegalArgumentException("no arguments") - val headContext = elements[0].context + val headContext = elements[0].group // Note: this is comparing the head of the list to itself, which seems inefficient, // but adding something like drop(1) in here would allocate an ArrayList and // entail a bunch of copying overhead. What's here is almost certainly cheaper. - val allCompat = elements.all { it.context.isCompatible(headContext) } + val allCompat = elements.all { it.group.isCompatible(headContext) } if (!allCompat) { throw IllegalArgumentException("incompatible contexts") diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/Nonces.kt b/src/main/kotlin/org/cryptobiotic/eg/core/Nonces.kt index 8c75dc2..849a1f5 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/core/Nonces.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/core/Nonces.kt @@ -53,10 +53,7 @@ operator fun Nonces.get(index: Int): ElementModQ = getWithHeaders(index) fun Nonces.getWithHeaders(index: Int, vararg headers: String) = hashFunction(internalSeed, index, *headers).toElementModQ(internalGroup) -/** - * Get an infinite (lazy) sequences of nonces. Equivalent to indexing with [Nonces.get] starting at - * 0. - */ +/** Get an infinite (lazy) sequences of nonces. Equivalent to indexing with [Nonces.get] starting at 1 */ fun Nonces.asSequence(): Sequence = generateSequence(0) { it + 1 }.map { this[it] } /** Gets a list of the desired number (`n`) of nonces. */ diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/Utils.kt b/src/main/kotlin/org/cryptobiotic/eg/core/Utils.kt index 48d8e59..f5397f9 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/core/Utils.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/core/Utils.kt @@ -69,17 +69,6 @@ fun fileReadBytes(filename: String): ByteArray = File(filename).readBytes() fun fileReadText(filename: String): String = File(filename).readText() -/** - * Throughout our bignum arithmetic, every operation needs to check that its operands are compatible - * (i.e., that we're not trying to use the test group and the production group interchangeably). - * This will verify that compatibility and throw an `ArithmeticException` if they're not. - */ -fun GroupContext.assertCompatible(other: GroupContext) { - if (!this.isCompatible(other)) { - throw ArithmeticException("incompatible group contexts") - } -} - /** * Convert an unsigned 64-bit long into a big-endian byte array of size 1, 2, 4, or 8 bytes, as * necessary to fit the value. diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/EcElementModP.kt b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/EcElementModP.kt index a352b88..ff07b02 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/EcElementModP.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/EcElementModP.kt @@ -3,8 +3,7 @@ package org.cryptobiotic.eg.core.ecgroup import org.cryptobiotic.eg.core.* import java.util.concurrent.atomic.AtomicInteger -class EcElementModP(val group: EcGroupContext, val ec: VecElementP): ElementModP { - override val context: GroupContext = group +class EcElementModP(override val group: EcGroupContext, val ec: VecElementP): ElementModP { override fun acceleratePow(): ElementModP { return EcElementModP(this.group, this.ec.acceleratePow()) @@ -20,7 +19,7 @@ class EcElementModP(val group: EcGroupContext, val ec: VecElementP): ElementModP override fun div(denominator: ElementModP): ElementModP { require (denominator is EcElementModP) val inv = denominator.ec.inv() - return EcElementModP(group, ec.mul(inv)) + return EcElementModP(this.group, ec.mul(inv)) } /** Validate that this element is a member of the elliptic curve Group.*/ @@ -29,18 +28,18 @@ class EcElementModP(val group: EcGroupContext, val ec: VecElementP): ElementModP } override fun multInv(): ElementModP { - return EcElementModP(group, ec.inv()) + return EcElementModP(this.group, ec.inv()) } override fun powP(exp: ElementModQ): ElementModP { require (exp is EcElementModQ) - group.opCounts.getOrPut("exp") { AtomicInteger(0) }.incrementAndGet() - return EcElementModP(group, ec.exp(exp.element)) + this.group.opCounts.getOrPut("exp") { AtomicInteger(0) }.incrementAndGet() + return EcElementModP(this.group, ec.exp(exp.element)) } override fun times(other: ElementModP): ElementModP { require (other is EcElementModP) - return EcElementModP(group, ec.mul(other.ec)) + return EcElementModP(this.group, ec.mul(other.ec)) } override fun toString(): String { @@ -57,14 +56,14 @@ class EcElementModP(val group: EcGroupContext, val ec: VecElementP): ElementModP other as EcElementModP - if (group != other.group) return false + if (this.group != other.group) return false if (ec != other.ec) return false return true } override fun hashCode(): Int { - var result = group.hashCode() + var result = this.group.hashCode() result = 31 * result + ec.hashCode() return result } diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/EcElementModQ.kt b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/EcElementModQ.kt index e17d1dc..b61e68f 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/EcElementModQ.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/EcElementModQ.kt @@ -4,37 +4,35 @@ import org.cryptobiotic.eg.core.* import org.cryptobiotic.eg.core.Base64.toBase64 import java.math.BigInteger -class EcElementModQ(val group: EcGroupContext, val element: BigInteger): ElementModQ { +// Theres not really any difference with the Integer Group ElementModQ. +class EcElementModQ(override val group: EcGroupContext, val element: BigInteger): ElementModQ { override fun byteArray(): ByteArray = element.toByteArray().normalize(32) - private fun BigInteger.modWrap(): ElementModQ = this.mod(group.vecGroup.order).wrap() - private fun BigInteger.wrap(): ElementModQ = EcElementModQ(group, this) + private fun BigInteger.modWrap(): ElementModQ = this.mod(this@EcElementModQ.group.vecGroup.order).wrap() + private fun BigInteger.wrap(): ElementModQ = EcElementModQ(this@EcElementModQ.group, this) override fun isZero() = element == BigInteger.ZERO - override val context: GroupContext - get() = group + override fun isValidElement() = element >= BigInteger.ZERO && element < this.group.vecGroup.order - override fun isValidElement() = element >= BigInteger.ZERO && element < group.vecGroup.order - - override operator fun compareTo(other: ElementModQ): Int = element.compareTo(other.getCompat(group)) + override operator fun compareTo(other: ElementModQ): Int = element.compareTo(other.getCompat(this.group)) override operator fun plus(other: ElementModQ) = - (this.element + other.getCompat(group)).modWrap() + (this.element + other.getCompat(this.group)).modWrap() override operator fun minus(other: ElementModQ) = this + (-other) override operator fun times(other: ElementModQ) = - (this.element * other.getCompat(group)).modWrap() + (this.element * other.getCompat(this.group)).modWrap() - override fun multInv(): ElementModQ = element.modInverse(group.vecGroup.order).wrap() + override fun multInv(): ElementModQ = element.modInverse(this.group.vecGroup.order).wrap() override operator fun unaryMinus(): ElementModQ = - if (this == group.ZERO_MOD_Q) + if (this == this.group.ZERO_MOD_Q) this else - (group.vecGroup.order - element).wrap() + (this.group.vecGroup.order - element).wrap() override infix operator fun div(denominator: ElementModQ): ElementModQ = this * denominator.multInv() @@ -50,7 +48,7 @@ class EcElementModQ(val group: EcGroupContext, val element: BigInteger): Element override fun toString() = byteArray().toBase64() fun Element.getCompat(other: GroupContext): BigInteger { - context.assertCompatible(other) + group.assertCompatible(other) return when (this) { is EcElementModQ -> this.element else -> throw NotImplementedError("should only be two kinds of elements") diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/EcGroupContext.kt b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/EcGroupContext.kt index 66a292b..dfd1df0 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/EcGroupContext.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/EcGroupContext.kt @@ -1,6 +1,7 @@ package org.cryptobiotic.eg.core.ecgroup import org.cryptobiotic.eg.core.* +import org.cryptobiotic.eg.core.intgroup.ProductionElementModQ import org.cryptobiotic.eg.core.intgroup.toBigInteger import java.math.BigInteger import java.util.concurrent.atomic.AtomicInteger @@ -31,15 +32,15 @@ class EcGroupContext(val name: String, useNative: Boolean = true): GroupContext return EcElementModQ(this, BigInteger(1, b)) } - override fun randomElementModQ(minimum: Int) : ElementModQ { - val b = randomBytes(MAX_BYTES_Q) - val bigMinimum = if (minimum <= 0) BigInteger.ZERO else minimum.toBigInteger() + /** Returns a random number in [2, Q). */ + override fun randomElementModQ(statBytes:Int) : ElementModQ { + val b = randomBytes(MAX_BYTES_Q + statBytes) val tmp = b.toBigInteger().mod(vecGroup.order) - val big = if (tmp < bigMinimum) tmp + bigMinimum else tmp - return EcElementModQ(this, big) + val tmp2 = if (tmp < BigInteger.TWO) tmp + BigInteger.TWO else tmp + return EcElementModQ(this, tmp2) } - override fun randomElementModP() = EcElementModP(this, vecGroup.randomElement()) + override fun randomElementModP(statBytes:Int) = EcElementModP(this, vecGroup.randomElement(statBytes)) override fun dLogG(p: ElementModP, maxResult: Int): Int? { require(p is EcElementModP) diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/RFC9380.kt b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/RFC9380.kt new file mode 100644 index 0000000..e2fed9e --- /dev/null +++ b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/RFC9380.kt @@ -0,0 +1,106 @@ +package org.cryptobiotic.eg.core.ecgroup + +// import jdk.vm.ci.code.CodeUtil.log2 +import kotlin.math.ceil + +// https://www.rfc-editor.org/rfc/rfc9380.pdf + +class RFC9380(val DST: ByteArray, p: Int, k: Int, val m: Int) { + // val L = ceil((ceil(log2(p)) + k) / 8) + + // hash_to_field(msg, count) + //Parameters: + //- DST, a domain separation tag (see Section 3.1). + //- F, a finite field of characteristic p and order q = p^m. + //- p, the characteristic of F (see immediately above). + //- m, the extension degree of F, m >= 1 (see immediately above). + //- L = ceil((ceil(log2(p)) + k) / 8), where k is the security + // parameter of the suite (e.g., k = 128). + // + //- expand_message, a function that expands a byte string and + // domain separation tag into a uniformly random byte string (see Section 5.3). + //Input: + //- msg, a byte string containing the message to hash. + //- count, the number of elements of F to output. + //Output: + //- (u_0, ..., u_(count - 1)), a list of field elements. + //Steps: + //1. len_in_bytes = count * m * L + //2. uniform_bytes = expand_message(msg, DST, len_in_bytes) + //3. for i in (0, ..., count - 1): + //4. for j in (0, ..., m - 1): + //5. elm_offset = L * (j + i * m) + //6. tv = substr(uniform_bytes, elm_offset, L) + //7. e_j = OS2IP(tv) mod p + //8. u_i = (e_0, ..., e_(m - 1)) + //9. return (u_0, ..., u_(count - 1)) + + /* + fun hash_to_field(msg: ByteArray, count: Int) : ByteArray { + //Steps: + val len_in_bytes = count * m * L + val uniform_bytes = expand_message(msg, DST, len_in_bytes) + //3. for i in (0, ..., count - 1): + //4. for j in (0, ..., m - 1): + //5. elm_offset = L * (j + i * m) + //6. tv = substr(uniform_bytes, elm_offset, L) + //7. e_j = OS2IP(tv) mod p + //8. u_i = (e_0, ..., e_(m - 1)) + //9. return (u_0, ..., u_(count - 1)) + } + + */ + +// expand_message_xmd(msg, DST, len_in_bytes) +// Parameters: +// - H, a hash function (see requirements above). +// - b_in_bytes, b / 8 for b the output size of H in bits. +// For example, for b = 256, b_in_bytes = 32. +// - s_in_bytes, the input block size of H, measured in bytes (see +// discussion above). For example, for SHA-256, s_in_bytes = 64. + +// Input: +// - msg, a byte string. +// - DST, a byte string of at most 255 bytes. +// See below for information on using longer DSTs. +// - len_in_bytes, the length of the requested output in bytes, +// not greater than the lesser of (255 * b_in_bytes) or 2^16-1. +// Output: +// - uniform_bytes, a byte string. + +// Steps: +// 1. ell = ceil(len_in_bytes / b_in_bytes) +// 2. ABORT if ell > 255 or len_in_bytes > 65535 or len(DST) > 255 +// 3. DST_prime = DST || I2OSP(len(DST), 1) +// 4. Z_pad = I2OSP(0, s_in_bytes) +// 5. l_i_b_str = I2OSP(len_in_bytes, 2) +// 6. msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime +// 7. b_0 = H(msg_prime) +// 8. b_1 = H(b_0 || I2OSP(1, 1) || DST_prime) +// 9. for i in (2, ..., ell): +// 10. b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) +// 11. uniform_bytes = b_1 || ... || b_ell +// 12. return substr(uniform_bytes, 0, len_in_bytes) + + /* + fun expand_message(msg: ByteArray, DST: ByteArray, len_in_bytes: Int) { + val ell = ceil(len_in_bytes / b_in_bytes) + require (ell < 255 && len_in_bytes < 65535 && DST.size < 255) + + val DST_prime = DST || I2OSP(len(DST), 1) + val Z_pad = I2OSP(0, s_in_bytes) + val l_i_b_str = I2OSP(len_in_bytes, 2) + val msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime + val b_0 = H(msg_prime) + val b_1 = H(b_0 || I2OSP(1, 1) || DST_prime) + for (i in (2..ell)) { + val b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) + } + val uniform_bytes = b_1 || ... || b_ell + return substr(uniform_bytes, 0, len_in_bytes) + } + + */ + + +} \ No newline at end of file diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/VecGroup.kt b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/VecGroup.kt index b743538..021722f 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/VecGroup.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/VecGroup.kt @@ -136,10 +136,10 @@ open class VecGroup( } // this value will always > 1, since 0, 1 are not on the curve. - fun randomElement(): VecElementP { + fun randomElement(statBytes:Int): VecElementP { for (j in 0 until 1000) { // limited in case theres a bug try { - val x = BigInteger(1, randomBytes(pbyteLength)) + val x = BigInteger(1, randomBytes(pbyteLength + statBytes)).mod(primeModulus) val fx = equationf(x) if (jacobiSymbol(fx, primeModulus) == 1) { @@ -355,6 +355,14 @@ open class VecGroup( return x == MINUS_ONE && y == MINUS_ONE } + /** + * Returns the Jacobi symbol of this instance modulo the input. + * This is an implementation of the binary Jacobi-symbol algorithm. + * + * @param value Integer to test. + * @param modulus An odd modulus. + * @return Jacobi symbol of this instance modulo the input. + */ fun jacobiSymbol(value: BigInteger, modulus: BigInteger): Int { var a = value var n = modulus diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/IntGroup.kt b/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/IntGroup.kt index cec3f56..e89b6ce 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/IntGroup.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/IntGroup.kt @@ -57,6 +57,8 @@ class ProductionGroupContext( } val groupConstants = IntGroupConstants(name, p, q, r, g) + val pm1overq = (p - BigInteger.ONE).div(q) // (p-1)/q + override val constants = groupConstants.constants override fun toString() : String = name @@ -89,16 +91,14 @@ class ProductionGroupContext( return (ctx is ProductionGroupContext) && productionMode == ctx.productionMode } - /** - * Returns a random number in [2, P). 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 - */ - override fun randomElementModP(): ElementModP { - val b = randomBytes(MAX_BYTES_P) - val tmp = b.toBigInteger().mod(p) - val tmp2 = if (tmp < BigInteger.TWO) tmp + BigInteger.TWO else tmp - return ProductionElementModP(tmp2, this) + /** Returns a random number in [2, P). */ + override fun randomElementModP(statBytes:Int): ElementModP { + val b = randomBytes(MAX_BYTES_P+statBytes) + val bi = b.toBigInteger() + val ti = bi.modPow(pm1overq, p) // by magic this makes it into a group element + + val tinbounds = if (ti < BigInteger.TWO) ti + BigInteger.TWO else ti + return ProductionElementModP(tinbounds, this) } override fun binaryToElementModP(b: ByteArray): ElementModP? = @@ -109,11 +109,11 @@ class ProductionGroupContext( null } - override fun randomElementModQ(minimum: Int) : ElementModQ { - val b = randomBytes(MAX_BYTES_Q) - val bigMinimum = if (minimum <= 0) BigInteger.ZERO else minimum.toBigInteger() + /** Returns a random number in [2, Q). */ + override fun randomElementModQ(statBytes:Int) : ElementModQ { + val b = randomBytes(MAX_BYTES_Q + statBytes) val tmp = b.toBigInteger().mod(q) - val tmp2 = if (tmp < bigMinimum) tmp + bigMinimum else tmp + val tmp2 = if (tmp < BigInteger.TWO) tmp + BigInteger.TWO else tmp return ProductionElementModQ(tmp2, this) } @@ -159,7 +159,7 @@ class ProductionGroupContext( } private fun Element.getCompat(other: ProductionGroupContext): BigInteger { - context.assertCompatible(other) + group.assertCompatible(other) return when (this) { is ProductionElementModP -> this.element is ProductionElementModQ -> this.element @@ -167,39 +167,35 @@ private fun Element.getCompat(other: ProductionGroupContext): BigInteger { } } -class ProductionElementModQ(internal val element: BigInteger, val groupContext: ProductionGroupContext): ElementModQ, +class ProductionElementModQ(internal val element: BigInteger, override val group: ProductionGroupContext): ElementModQ, Element, Comparable { override fun byteArray(): ByteArray = element.toByteArray().normalize(32) - private fun BigInteger.modWrap(): ElementModQ = this.mod(groupContext.q).wrap() - private fun BigInteger.wrap(): ElementModQ = ProductionElementModQ(this, groupContext) - - override val context: GroupContext - get() = groupContext + private fun BigInteger.modWrap(): ElementModQ = this.mod(this@ProductionElementModQ.group.q).wrap() + private fun BigInteger.wrap(): ElementModQ = ProductionElementModQ(this, this@ProductionElementModQ.group) override fun isZero() = element == BigInteger.ZERO + override fun isValidElement() = element >= BigInteger.ZERO && element < this.group.q - override fun isValidElement() = element >= BigInteger.ZERO && element < groupContext.q - - override operator fun compareTo(other: ElementModQ): Int = element.compareTo(other.getCompat(groupContext)) + override operator fun compareTo(other: ElementModQ): Int = element.compareTo(other.getCompat(this.group)) override operator fun plus(other: ElementModQ) = - (this.element + other.getCompat(groupContext)).modWrap() + (this.element + other.getCompat(this.group)).modWrap() override operator fun minus(other: ElementModQ) = this + (-other) override operator fun times(other: ElementModQ) = - (this.element * other.getCompat(groupContext)).modWrap() + (this.element * other.getCompat(this.group)).modWrap() - override fun multInv(): ElementModQ = element.modInverse(groupContext.q).wrap() + override fun multInv(): ElementModQ = element.modInverse(this.group.q).wrap() override operator fun unaryMinus(): ElementModQ = - if (this == groupContext.zeroModQ) + if (this == this.group.zeroModQ) this else - (groupContext.q - element).wrap() + (this.group.q - element).wrap() override infix operator fun div(denominator: ElementModQ): ElementModQ = this * denominator.multInv() @@ -223,7 +219,7 @@ open class ProductionElementModP(internal val element: BigInteger, val groupCont private fun BigInteger.modWrap(): ElementModP = this.mod(groupContext.p).wrap() private fun BigInteger.wrap(): ElementModP = ProductionElementModP(this, groupContext) - override val context: GroupContext + override val group: GroupContext get() = groupContext override operator fun compareTo(other: ElementModP): Int = element.compareTo(other.getCompat(groupContext)) diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/IntGroups.kt b/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/IntGroups.kt index da6b585..85b7757 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/IntGroups.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/IntGroups.kt @@ -103,76 +103,3 @@ fun productionIntGroup(acceleration: PowRadixOption = PowRadixOption.LOW_MEMORY_ ProductionMode.Mode4096 -> productionGroups4096[acceleration] ?: throw Error("can't happen") ProductionMode.Mode3072 -> productionGroups3072[acceleration] ?: throw Error("can't happen") } - -// -// /* -// * Verifies that every element has a compatible [GroupContext] and returns the first context. -// * -// * @throws IllegalArgumentException if there's an incompatibility. -// fun compatibleContextOrFail(vararg elements: Element): GroupContext { -// // Engineering note: If this method fails, that means we have a bug in our program. -// // We should never allow incompatible data to be processed. We should catch -// // this when we're loading the data in the first place. -// -// if (elements.isEmpty()) throw IllegalArgumentException("no arguments") -// -// val headContext = elements[0].context -// -// // Note: this is comparing the head of the list to itself, which seems inefficient, -// // but adding something like drop(1) in here would allocate an ArrayList and -// // entail a bunch of copying overhead. What's here is almost certainly cheaper. -// val allCompat = elements.all { it.context.isCompatible(headContext) } -// -// if (!allCompat) { -// throw IllegalArgumentException("incompatible contexts") -// } -// -// return headContext -// } -// -// * Given an element x for which there exists an e, such that g^e = x, this will find e, -// * so long as e is less than [maxResult], which if unspecified defaults to a platform-specific -// * 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 ElementModP.dLogG(maxResult: Int = -1): Int? = context.dLogG(this, maxResult) -// -// * Converts from an external [ElectionConstants] to an internal [GroupContext]. Note the optional -// * `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? { -// val group4096 = productionGroup(acceleration = acceleration, mode = ProductionMode.Mode4096) -// val group3072 = productionGroup(acceleration = acceleration, mode = ProductionMode.Mode3072) -// -// return when { -// group4096.isCompatible(this) -> group4096 -// group3072.isCompatible(this) -> group3072 -// else -> { -// logger.error { -// "unrecognized cryptographic parameters; this election was encrypted using a " + -// "library incompatible with this one: $this" -// } -// null -// } -// } -// } -// -// /** -// * Computes the sum of the given elements, mod q; this can be faster than using the addition -// * operation for large numbers of inputs by potentially reusing scratch-space memory. -// */ -// fun GroupContext.addQ(vararg elements: ElementModQ) = elements.asIterable().addQ() -// -// /** -// * Computes the product of the given elements, mod p; this can be faster than using the -// * multiplication operation for large numbers of inputs by potentially reusing scratch-space memory. -// */ -// fun GroupContext.multP(vararg elements: ElementModP) = elements.asIterable().multP() -// -// */ \ No newline at end of file diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/PowRadix.kt b/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/PowRadix.kt index 28a88cd..b514c43 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/PowRadix.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/PowRadix.kt @@ -71,7 +71,7 @@ class PowRadix(val basis: ProductionElementModP, val acceleration: PowRadixOptio init { val k = acceleration.numBits val mBasis = basis.toMontgomeryElementModP() - montgomeryOne = (basis.context.ONE_MOD_P as ProductionElementModP).toMontgomeryElementModP() + montgomeryOne = (basis.group.ONE_MOD_P as ProductionElementModP).toMontgomeryElementModP() if (k == 0) { tableLength = 0 @@ -101,7 +101,7 @@ class PowRadix(val basis: ProductionElementModP, val acceleration: PowRadixOptio } fun pow(e: ElementModQ): ElementModP { - basis.context.assertCompatible(e.context) + basis.group.assertCompatible(e.group) return if (acceleration.numBits == 0) basis powP e else { val slices = e.byteArray().kBitsPerSlice(acceleration, tableLength) diff --git a/src/main/kotlin/org/cryptobiotic/eg/decrypt/DecryptingTrustee.kt b/src/main/kotlin/org/cryptobiotic/eg/decrypt/DecryptingTrustee.kt index 27eaf09..276918e 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/decrypt/DecryptingTrustee.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/decrypt/DecryptingTrustee.kt @@ -33,7 +33,7 @@ data class DecryptingTrustee( texts: List, ): PartialDecryptions { - val seed = group.randomElementModQ(2) // random value u in Zq + val seed = group.randomElementModQ() // random value u in Zq val batchId = randomInt() runBlocking { mutex.withLock { diff --git a/src/main/kotlin/org/cryptobiotic/eg/keyceremony/KeyCeremonyTrustee.kt b/src/main/kotlin/org/cryptobiotic/eg/keyceremony/KeyCeremonyTrustee.kt index 0798fba..5fabd9e 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/keyceremony/KeyCeremonyTrustee.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/keyceremony/KeyCeremonyTrustee.kt @@ -202,10 +202,10 @@ open class KeyCeremonyTrustee( open fun shareEncryption( Pil: ElementModQ, other: PublicKeys, - nonce: ElementModQ = group.randomElementModQ(minimum = 2) + nonce: ElementModQ = group.randomElementModQ() ): HashedElGamalCiphertext { - val hp = Pil.context.constants.parameterBaseHash + val hp = Pil.group.constants.parameterBaseHash val i = xCoordinate val l = other.guardianXCoordinate diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/ChaumPedersenTest.kt b/src/test/kotlin/org/cryptobiotic/eg/core/ChaumPedersenTest.kt index 869cf1d..2f8f164 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/ChaumPedersenTest.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/ChaumPedersenTest.kt @@ -338,12 +338,12 @@ class ChaumPedersenTest { fun testAccumDifferentNonces(context: GroupContext) { val constant = 42 - val contestNonce = context.randomElementModQ(2) + val contestNonce = context.randomElementModQ() val hashHeader = UInt256.random() - val keyPair = elGamalKeyPairFromSecret(context.randomElementModQ(2)) + val keyPair = elGamalKeyPairFromSecret(context.randomElementModQ()) val publicKey = keyPair.publicKey - val randomQ = context.randomElementModQ(2) + val randomQ = context.randomElementModQ() val nonceSequence = Nonces(randomQ, contestNonce) val vote0 = 0.encrypt(publicKey, nonceSequence[0]) diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/ElGamalEncryptionTest.kt b/src/test/kotlin/org/cryptobiotic/eg/core/ElGamalEncryptionTest.kt index ae1e557..f7c94da 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/ElGamalEncryptionTest.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/ElGamalEncryptionTest.kt @@ -83,7 +83,7 @@ private fun testEncryption(name: String, group: GroupContext) = wordSpec { smallInts() ) { keypair, message -> val encryption = message.encrypt(keypair) - val reencryption = encryption.reencrypt(keypair.publicKey, group.randomElementModQ(minimum = 1)) + val reencryption = encryption.reencrypt(keypair.publicKey, group.randomElementModQ()) val decryption = reencryption.decrypt(keypair) message shouldBe decryption } @@ -100,7 +100,7 @@ private fun testEncryption(name: String, group: GroupContext) = wordSpec { elGamalKeypairs(group), smallInts() ) { keypair, message -> val org = message.encrypt(keypair) - val extra: ElementModQ = group.randomElementModQ(minimum = 1) + val extra: ElementModQ = group.randomElementModQ() val extraEncryption = ElGamalCiphertext(org.pad * group.gPowP(extra), org.data * (keypair.publicKey powP extra)) val decryption = extraEncryption.decrypt(keypair) diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/ElGamalKeysTest.kt b/src/test/kotlin/org/cryptobiotic/eg/core/ElGamalKeysTest.kt index 857a050..443cde7 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/ElGamalKeysTest.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/ElGamalKeysTest.kt @@ -17,7 +17,7 @@ class ElGamalKeysTest : WordSpec({ private fun testKeys(name: String, group: GroupContext) = wordSpec { name should { "have correct public key using elGamalKeyPairFromSecret" { - val secret = group.randomElementModQ(2) + val secret = group.randomElementModQ() val keypair = elGamalKeyPairFromSecret(secret) keypair.secretKey shouldBe secret keypair.publicKey shouldBe group.gPowP(secret) diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/GroupTest.kt b/src/test/kotlin/org/cryptobiotic/eg/core/GroupTest.kt index 9994d58..2ee570b 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/GroupTest.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/GroupTest.kt @@ -20,6 +20,8 @@ class GroupTest { fun basics() { groups.forEach { testBasics(it) } groups.forEach { testBasicsL(it) } + groups.forEach { testRandom(it) } + groups.forEach { testRandomWithStatBytes(it) } } fun testBasics(context: GroupContext) { @@ -36,6 +38,27 @@ class GroupTest { assertEquals(seven, three + four) } + fun testRandom(group: GroupContext) { + val randomP = group.randomElementModP() + val randomQ = group.randomElementModQ() + if (!randomP.isValidElement()) { + randomP.isValidElement() + } + assertTrue(randomP.isValidElement(),"group ${group.constants.name}") + assertTrue(randomQ.isValidElement(), "group ${group.constants.name}") + + println("random p= ${randomP.toStringShort()} random q = $randomQ are ok") + } + + fun testRandomWithStatBytes(group: GroupContext) { + val randomP = group.randomElementModP(16) + val randomQ = group.randomElementModQ(16) + assertTrue(randomP.isValidElement(), "group ${group.constants.name}") + assertTrue(randomQ.isValidElement(), "group ${group.constants.name}") + + println("random p= ${randomP.toStringShort()} random q = $randomQ are ok") + } + @Test fun comparisonOperations() { groups.forEach { comparisonOperations(it) } @@ -89,7 +112,8 @@ class GroupTest { fun binaryArrayRoundTrip(context: GroupContext) { runTest { forAll(propTestFastConfig, elementsModP(context)) { - it == context.binaryToElementModP(it.byteArray()) + val what = context.binaryToElementModP(it.byteArray()) + it == what } forAll(propTestFastConfig, elementsModQ(context)) { it == context.binaryToElementModQ(it.byteArray()) diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/KotestGenerators.kt b/src/test/kotlin/org/cryptobiotic/eg/core/KotestGenerators.kt index 83256a9..14b7eb0 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/KotestGenerators.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/KotestGenerators.kt @@ -20,16 +20,10 @@ fun elementsModP(ctx: GroupContext): Arb { // fun elementsModPNoZero(ctx: GroupContext) = elementsModP(ctx) /** Generate an arbitrary ElementModQ in [minimum, Q) for the given group context. */ -fun elementsModQ(ctx: GroupContext, minimum: Int = 0): Arb = arbitrary{ ctx.randomElementModQ() } - -/* fun elementsModQ(ctx: GroupContext, minimum: Int = 0): Arb = - Arb.byteArray(Arb.constant(ctx.MAX_BYTES_Q), Arb.byte()) - .map { ctx.randomElementModQ() } - - */ +fun elementsModQ(ctx: GroupContext): Arb = arbitrary{ ctx.randomElementModQ() } /** Generate an arbitrary ElementModQ in [1, Q) for the given group context. */ -fun elementsModQNoZero(ctx: GroupContext) = elementsModQ(ctx, 1) +fun elementsModQNoZero(ctx: GroupContext) = elementsModQ(ctx) /** * Generates a valid element of the subgroup of ElementModP where there exists an e in Q such that v @@ -43,7 +37,7 @@ fun validResiduesOfP(ctx: GroupContext): Arb = * accelerated using the default PowRadixOption in the GroupContext. */ fun elGamalKeypairs(ctx: GroupContext): Arb = - elementsModQ(ctx, minimum = 2).map { elGamalKeyPairFromSecret(it) } + elementsModQ(ctx).map { elGamalKeyPairFromSecret(it) } /** Generates arbitrary UInt256 values. */ fun uint256s(): Arb = Arb.byteArray(Arb.constant(32), Arb.byte()).map { UInt256(it) } diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/SchnorrTest.kt b/src/test/kotlin/org/cryptobiotic/eg/core/SchnorrTest.kt index 2111f01..b61f615 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/SchnorrTest.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/SchnorrTest.kt @@ -29,7 +29,7 @@ private fun testSchnorrProof(name: String, group: GroupContext) = wordSpec { Arb.int(0, 10), elementsModQ(group), elementsModQ(group) - ) { kp, i, j, nonce, fakeElementModQ -> + ) { kp, i, j, nonce, _ -> val goodProof = kp.schnorrProof(i, j, nonce) (goodProof.validate(i, j) is Ok) shouldBe true } diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/TestBase64.kt b/src/test/kotlin/org/cryptobiotic/eg/core/TestBase64.kt index 05fbad0..9564d7c 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/TestBase64.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/TestBase64.kt @@ -69,7 +69,7 @@ class TestBase64 { runTest { val context = productionGroup() val test = "1234567890".repeat(1000) - val e1 = assertFailsWith(block = { println(" convert = ${context.base64ToElementModQ(test)}") }) + assertFailsWith(block = { println(" convert = ${context.base64ToElementModQ(test)}") }) } } diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/TestRandom.kt b/src/test/kotlin/org/cryptobiotic/eg/core/TestRandom.kt index cd185b9..ed331e4 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/TestRandom.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/TestRandom.kt @@ -1,17 +1,15 @@ package org.cryptobiotic.eg.core import org.junit.jupiter.api.Test +import java.security.DrbgParameters import java.security.SecureRandom import java.security.Security import java.util.* +import kotlin.test.assertFalse +import kotlin.test.assertTrue -class TestRandom { - @Test - fun testRandom() { - val rng = Random() - println("Random $rng") - } +class TestRandom { @Test fun testSecureRandom() { @@ -29,4 +27,62 @@ class TestRandom { algorithms.forEach { println(" $it") } } + @Test + fun showDRBG() { + val rng = SecureRandom.getInstance("DRBG") + println("SecureRandom.getInstanceStrong") + println(" algo=${rng.algorithm}") + println(" params=${rng.parameters}") + println(" provider=${rng.provider}") + println(" rng=${rng}") + println(" class=${rng.javaClass.getName()}") + } + + @Test + fun testDRBG() { + val n = 1 + val r1 = SecureRandom.getInstance("DRBG") + val r2 = SecureRandom.getInstance("DRBG") + assertFalse(r1 === r2) + + r1.setSeed(1234567L) + r2.setSeed(1234567L) + + val ba1 = r1.fill(32, n) + val ba2 = r2.fill(32, n) + + // not deterministic + repeat (n) { + assertFalse(ba1[it].contentEquals(ba2[it])) + } + } + + @Test + fun testRandom() { + val n = 1000 + val r1 = Random() + val r2 = Random() + assertFalse(r1 === r2) + + r1.setSeed(1234567L) + r2.setSeed(1234567L) + + val ba1 = r1.fill(32, n) + val ba2 = r2.fill(32, n) + + repeat (n) { + assertTrue(ba1[it].contentEquals(ba2[it])) + } + } + + fun Random.fill(size: Int, n: Int): List { + val result = mutableListOf() + repeat (n) { + val ba = ByteArray(size) + this.nextBytes(ba) + result.add(ba) + } + return result + } + } \ No newline at end of file diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestElem.kt b/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestElem.kt index 8b96a1d..5bbf667 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestElem.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestElem.kt @@ -94,7 +94,7 @@ class TestElem { val group = VecGroups.getEcGroup("P-256") val n = 100 - val elems = List(n) { group.randomElement() } + val elems = List(n) { group.randomElement(16) } val exps = List(n) { BigInteger(256, r)} val stopwatch = Stopwatch() elems.forEachIndexed { idx, elem -> diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestJacobi.kt b/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestJacobi.kt new file mode 100644 index 0000000..5049317 --- /dev/null +++ b/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestJacobi.kt @@ -0,0 +1,43 @@ +package org.cryptobiotic.eg.core.ecgroup + +import org.cryptobiotic.eg.core.ecgroup.VecGroup.Companion.jacobiSymbol +import org.cryptobiotic.eg.core.randomBytes +import java.math.BigInteger +import kotlin.test.Test +import kotlin.test.assertEquals + +class TestJacobi { + + @Test + fun testJacobiJava() { + val group = EcGroupContext("P-256", false) + testJacobi(group.vecGroup) + } + + @Test + fun testJacobiNative() { + if (!VecGroups.hasNativeLibrary()) return + + val group = EcGroupContext("P-256", true) + testJacobi(group.vecGroup) + } + + fun testJacobi(vgroup: VecGroup) { + val pM1over2= (vgroup.primeModulus - BigInteger.ONE) / BigInteger.TWO + + val ntrials = 1000 + var countTrue = 0 + repeat(ntrials) { + val x = BigInteger(1, randomBytes(vgroup.pbyteLength)) + val fx = vgroup.equationf(x) + val isJacobiOne = jacobiSymbol(fx, vgroup.primeModulus) == 1 + if (isJacobiOne) countTrue++ + + // presumably its equivalent to y^((p-1)/2) == 1 as described in \cite{Haines20} + val testHaines = fx.modPow(pM1over2, vgroup.primeModulus) == BigInteger.ONE + // println("isJacobiOne = $isJacobiOne testHaines=$testHaines") + assertEquals(isJacobiOne, testHaines) + } + println(" ${vgroup.javaClass} isJacobiOne= $countTrue / $ntrials") + } +} \ No newline at end of file diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/intgroup/TinyGroup.kt b/src/test/kotlin/org/cryptobiotic/eg/core/intgroup/TinyGroup.kt index c3ec4fc..c4f63f5 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/intgroup/TinyGroup.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/intgroup/TinyGroup.kt @@ -24,7 +24,7 @@ internal val tinyGroupContext = fun tinyGroup(): GroupContext = tinyGroupContext internal fun Element.getCompat(other: GroupContext): UInt { - context.assertCompatible(other) + group.assertCompatible(other) return when (this) { is TinyElementModP -> this.element is TinyElementModQ -> this.element @@ -48,6 +48,7 @@ internal class TinyGroupContext( val twoModQ: ElementModQ val dlogger: DLogarithm val qMinus1Q: ElementModQ + val pm1overq = TinyElementModQ ((p - 1U).div(q), this) // (p-1)/q init { oneModP = TinyElementModP(1U, this) @@ -102,7 +103,7 @@ internal class TinyGroupContext( * If the modulus is zero, it's ignored, and the intermediate value is truncated to a UInt and * returned. */ - internal fun ByteArray.toUIntMod(modulus: UInt = 0U): UInt { + internal fun ByteArray.toUIntMod(modulus: UInt): UInt { val preModulus = this.fold(0UL) { prev, next -> ((prev shl 8) or next.toUByte().toULong()) } return if (modulus == 0U) { preModulus.toUInt() @@ -111,19 +112,22 @@ internal class TinyGroupContext( } } - override fun randomElementModQ(minimum: Int) : ElementModQ { + override fun randomElementModQ(statBytes:Int) : ElementModQ { val b = randomBytes(MAX_BYTES_Q) - val useMinimum = if (minimum <= 0) 0U else minimum.toUInt() val u32 = b.toUIntMod(q) - val result = if (u32 < useMinimum) u32 + useMinimum else u32 + val result = if (u32 < 2U) u32 + 2U else u32 return uIntToElementModQ(result) } - override fun binaryToElementModP(b: ByteArray): ElementModP? { - if (b.size > 4) return null // guaranteed to be out of bounds + override fun randomElementModP(statBytes:Int): ElementModP { + val tmp = binaryToElementModP(randomBytes(MAX_BYTES_P)) as TinyElementModP + val modp = if (tmp.element < 2U) uIntToElementModP(tmp.element + 2U) else tmp + return modp powP pm1overq // by magic this makes it into a group element + } - val u32: UInt = b.toUIntMod() - return if (u32 >= p) null else uIntToElementModP(u32) + override fun binaryToElementModP(b: ByteArray): ElementModP { + val u32: UInt = b.toUIntMod(p) + return uIntToElementModP(u32) } override fun binaryToElementModQ(b: ByteArray): ElementModQ { @@ -161,15 +165,6 @@ internal class TinyGroupContext( override fun dLogG(p: ElementModP, maxResult: Int): Int? = dlogger.dLog(p, maxResult) - override fun randomElementModP() = binaryToElementModPmin(randomBytes(MAX_BYTES_P), 2) - - private fun binaryToElementModPmin(b: ByteArray, minimum: Int): ElementModP { - val useMinimum = if (minimum <= 0) 0U else minimum.toUInt() - val u32 = b.toUIntMod(p) - val result = if (u32 < useMinimum) u32 + useMinimum else u32 - return uIntToElementModP(result) - } - override fun getAndClearOpCounts() = emptyMap() } @@ -181,8 +176,8 @@ internal class TinyElementModP(val element: UInt, val groupContext: TinyGroupCon override fun isValidElement(): Boolean { val inBounds = element < groupContext.p - val residue = this powP TinyElementModQ(groupContext.q, groupContext) == groupContext.ONE_MOD_P - return inBounds && residue + val residue = this powP TinyElementModQ(groupContext.q, groupContext) + return inBounds && (residue == groupContext.ONE_MOD_P) } override fun powP(exp: ElementModQ): ElementModP { @@ -216,7 +211,7 @@ internal class TinyElementModP(val element: UInt, val groupContext: TinyGroupCon override fun compareTo(other: ElementModP): Int = element.compareTo(other.getCompat(groupContext)) - override val context: GroupContext + override val group: GroupContext get() = groupContext // fun inBounds(): Boolean = element < groupContext.p @@ -291,7 +286,7 @@ internal class TinyElementModQ(val element: UInt, val groupContext: TinyGroupCon override fun compareTo(other: ElementModQ): Int = element.compareTo(other.getCompat(groupContext)) - override val context: GroupContext + override val group: GroupContext get() = groupContext override fun isValidElement(): Boolean = element < groupContext.q diff --git a/src/test/kotlin/org/cryptobiotic/eg/decrypt/EncryptDecryptTest.kt b/src/test/kotlin/org/cryptobiotic/eg/decrypt/EncryptDecryptTest.kt index 1e6c64f..63865aa 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/decrypt/EncryptDecryptTest.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/decrypt/EncryptDecryptTest.kt @@ -66,7 +66,7 @@ fun encryptDecrypt( val missing = trustees.filter {!present.contains(it.xCoordinate())}.map { it.id() } println("present $present, missing $missing") val vote = 42 - val evote = vote.encrypt(publicKey, group.randomElementModQ(minimum = 1)) + val evote = vote.encrypt(publicKey, group.randomElementModQ()) val available = trustees.filter {present.contains(it.xCoordinate())} val lagrangeCoefficients = available.associate { it.id() to computeLagrangeCoefficient(group, it.xCoordinate(), present) } diff --git a/src/test/kotlin/org/cryptobiotic/eg/keyceremony/ShareEncryptDecryptTest.kt b/src/test/kotlin/org/cryptobiotic/eg/keyceremony/ShareEncryptDecryptTest.kt index 2b134d4..58cd3f4 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/keyceremony/ShareEncryptDecryptTest.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/keyceremony/ShareEncryptDecryptTest.kt @@ -28,7 +28,7 @@ class ShareEncryptDecryptTest { checkAll( propTestFastConfig, Arb.int(min = 1, max = 100), - elementsModQ(group, minimum = 2) + elementsModQ(group) ) { xcoord, pil -> val trustee1 = KeyCeremonyTrustee(group, "id1", xcoord, 4, 4) val trustee2 = KeyCeremonyTrustee(group, "id2", xcoord + 1, 4, 4) diff --git a/src/test/kotlin/org/cryptobiotic/eg/preencrypt/PreEncryptorTest.kt b/src/test/kotlin/org/cryptobiotic/eg/preencrypt/PreEncryptorTest.kt index cb8bd03..68ce56e 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/preencrypt/PreEncryptorTest.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/preencrypt/PreEncryptorTest.kt @@ -186,7 +186,7 @@ internal fun runComplete( ) { if (show) println("===================================================================") val qbar = 4242U.toUInt256() - val secret = group.randomElementModQ(minimum = 1) + val secret = group.randomElementModQ() val publicKey = ElGamalPublicKey(group.gPowP(secret)) val primaryNonce = UInt256.random() diff --git a/src/test/kotlin/org/cryptobiotic/eg/publish/json/KeyCeremonyJsonTest.kt b/src/test/kotlin/org/cryptobiotic/eg/publish/json/KeyCeremonyJsonTest.kt index 02b8a5b..98d280c 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/publish/json/KeyCeremonyJsonTest.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/publish/json/KeyCeremonyJsonTest.kt @@ -64,7 +64,7 @@ class KeyCeremonyJsonTest { Arb.string(minSize = 3), Arb.string(minSize = 3), ) { id, owner, secretShareFor, -> - val kshare = KeyShare(id, owner, secretShareFor, group.randomElementModQ(2)) + val kshare = KeyShare(id, owner, secretShareFor, group.randomElementModQ()) assertEquals(kshare, kshare.publishJson().import(group)) } } diff --git a/src/test/kotlin/org/cryptobiotic/eg/publish/json/WebappDecryptionTest.kt b/src/test/kotlin/org/cryptobiotic/eg/publish/json/WebappDecryptionTest.kt index 2bc2b4b..66d5598 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/publish/json/WebappDecryptionTest.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/publish/json/WebappDecryptionTest.kt @@ -36,7 +36,7 @@ class WebappDecryptionTest { propTestFastConfig, Arb.string(minSize = 3), Arb.int(min = 1, max = 5), - elementsModQ(group, minimum = 2) + elementsModQ(group) ) { name, nmissing, lc -> val miss = List(nmissing) { name + it } val org = SetMissingRequest(lc, miss) @@ -111,7 +111,7 @@ class WebappDecryptionTest { Arb.int(min = 1, max = 122221), Arb.int(min = 1, max = 11), ) { batchId, nrequests -> - val crs = List(nrequests) { elementsModQ(group, minimum = 2).single() } + val crs = List(nrequests) { elementsModQ(group).single() } // ChallengeRequest(val batchId: Int, val texts: List) val org = ChallengeRequest(batchId, crs) val responses = org.publishJson().import(group) @@ -133,7 +133,7 @@ class WebappDecryptionTest { Arb.string(minSize = 3), Arb.int(min = 1, max = 11), ) { name, nrequests -> - val crs = List(nrequests) { elementsModQ(group, minimum = 2).single() } + val crs = List(nrequests) { elementsModQ(group).single() } val org = ChallengeResponses(null, 42, crs) val responses = org.publishJson().import(group) assertTrue(responses is Ok) diff --git a/src/test/kotlin/org/cryptobiotic/eg/workflow/FakeKeyCeremony.kt b/src/test/kotlin/org/cryptobiotic/eg/workflow/FakeKeyCeremony.kt index 28f471e..d8b15d2 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/workflow/FakeKeyCeremony.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/workflow/FakeKeyCeremony.kt @@ -135,7 +135,7 @@ fun testDoerreDecrypt(group: GroupContext, val missing = trustees.filter {!present.contains(it.xCoordinate())}.map { it.id } println("present $present, missing $missing") val vote = 42 - val evote = vote.encrypt(publicKey, group.randomElementModQ(minimum = 1)) + val evote = vote.encrypt(publicKey, group.randomElementModQ()) val available = trustees.filter {present.contains(it.xCoordinate())} val lagrangeCoefficients = available.associate { it.id to computeLagrangeCoefficient(group, it.xCoordinate, present) } diff --git a/src/test/kotlin/org/cryptobiotic/eg/workflow/TestWorkflowEncryptDecrypt.kt b/src/test/kotlin/org/cryptobiotic/eg/workflow/TestWorkflowEncryptDecrypt.kt index c9532c4..e44e379 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/workflow/TestWorkflowEncryptDecrypt.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/workflow/TestWorkflowEncryptDecrypt.kt @@ -19,10 +19,10 @@ class TestWorkflowEncryptDecrypt { fun singleTrusteeZero() { runTest { val group = productionGroup() - val secret = group.randomElementModQ(minimum = 1) + val secret = group.randomElementModQ() val publicKey = ElGamalPublicKey(group.gPowP(secret)) val keypair = ElGamalKeypair(ElGamalSecretKey(secret), publicKey) - val nonce = group.randomElementModQ(minimum = 1) + val nonce = group.randomElementModQ() // accumulate random sequence of 1 and 0 val vote = 0 @@ -44,10 +44,10 @@ class TestWorkflowEncryptDecrypt { fun singleTrusteeOne() { runTest { val group = productionGroup() - val secret = group.randomElementModQ(minimum = 1) + val secret = group.randomElementModQ() val publicKey = ElGamalPublicKey(group.gPowP(secret)) val keypair = ElGamalKeypair(ElGamalSecretKey(secret), publicKey) - val nonce = group.randomElementModQ(minimum = 1) + val nonce = group.randomElementModQ() // acumulate random sequence of 1 and 0 val vote = 1 @@ -68,15 +68,15 @@ class TestWorkflowEncryptDecrypt { fun singleTrusteeTally() { runTest { val group = productionGroup() - val secret = group.randomElementModQ(minimum = 1) + val secret = group.randomElementModQ() val publicKey = ElGamalPublicKey(group.gPowP(secret)) assertEquals(group.gPowP(secret), publicKey.key) val keypair = ElGamalKeypair(ElGamalSecretKey(secret), publicKey) val vote = 1 - val evote1 = vote.encrypt(publicKey, group.randomElementModQ(minimum = 1)) - val evote2 = vote.encrypt(publicKey, group.randomElementModQ(minimum = 1)) - val evote3 = vote.encrypt(publicKey, group.randomElementModQ(minimum = 1)) + val evote1 = vote.encrypt(publicKey, group.randomElementModQ()) + val evote2 = vote.encrypt(publicKey, group.randomElementModQ()) + val evote3 = vote.encrypt(publicKey, group.randomElementModQ()) val accum = listOf(evote1, evote2, evote3) val eAccum = accum.encryptedSum()?: 0.encrypt(publicKey) @@ -102,9 +102,9 @@ class TestWorkflowEncryptDecrypt { val publicKey = ElGamalPublicKey( group.multP(pkeys) ) val vote = 1 - val evote1 = vote.encrypt(publicKey, group.randomElementModQ(minimum = 1)) - val evote2 = vote.encrypt(publicKey, group.randomElementModQ(minimum = 1)) - val evote3 = vote.encrypt(publicKey, group.randomElementModQ(minimum = 1)) + val evote1 = vote.encrypt(publicKey, group.randomElementModQ()) + val evote2 = vote.encrypt(publicKey, group.randomElementModQ()) + val evote3 = vote.encrypt(publicKey, group.randomElementModQ()) // tally val accum = listOf(evote1, evote2, evote3) diff --git a/src/test/kotlin/org/cryptobiotic/gmp/VecTest.kt b/src/test/kotlin/org/cryptobiotic/gmp/VecTest.kt index ec7e2e8..424fcd2 100644 --- a/src/test/kotlin/org/cryptobiotic/gmp/VecTest.kt +++ b/src/test/kotlin/org/cryptobiotic/gmp/VecTest.kt @@ -116,7 +116,7 @@ class VecTest { assertTrue(h is EcElementModP) assertTrue(hn is EcElementModP) - assertTrue((h as EcElementModP).ec is VecElementP) + assertTrue((h as EcElementModP).ec !is VecElementPnative) assertTrue((hn as EcElementModP).ec is VecElementPnative) val prodpow: ElementModP = nonces.map { h powP it }.reduce { a, b -> a * b }