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 @@
[](https://github.com/JohnLCaron/egk-ec/blob/main/LICENSE.txt)

--blue)
+-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 }