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/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 +224,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..8aa0505 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,12 +32,12 @@ class EcGroupContext(val name: String, useNative: Boolean = true): GroupContext
return EcElementModQ(this, BigInteger(1, b))
}
- override fun randomElementModQ(minimum: Int) : ElementModQ {
+ /** Returns a random number in [2, Q). */
+ override fun randomElementModQ() : ElementModQ {
val b = randomBytes(MAX_BYTES_Q)
- val bigMinimum = if (minimum <= 0) BigInteger.ZERO else minimum.toBigInteger()
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())
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..4cc9ee9 100644
--- a/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/VecGroup.kt
+++ b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/VecGroup.kt
@@ -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..dfac810 100644
--- a/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/IntGroup.kt
+++ b/src/main/kotlin/org/cryptobiotic/eg/core/intgroup/IntGroup.kt
@@ -89,11 +89,7 @@ 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
- */
+ /** Returns a random number in [2, P). */
override fun randomElementModP(): ElementModP {
val b = randomBytes(MAX_BYTES_P)
val tmp = b.toBigInteger().mod(p)
@@ -109,11 +105,11 @@ class ProductionGroupContext(
null
}
- override fun randomElementModQ(minimum: Int) : ElementModQ {
+ /** Returns a random number in [2, Q). */
+ override fun randomElementModQ() : ElementModQ {
val b = randomBytes(MAX_BYTES_Q)
- val bigMinimum = if (minimum <= 0) BigInteger.ZERO else minimum.toBigInteger()
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 +155,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 +163,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 +215,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/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/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..d456139 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
@@ -111,11 +111,10 @@ internal class TinyGroupContext(
}
}
- override fun randomElementModQ(minimum: Int) : ElementModQ {
+ override fun randomElementModQ() : 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)
}
@@ -216,7 +215,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 +290,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/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/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)