Skip to content

Commit

Permalink
Merge pull request #78 from JohnLCaron/isValidElement
Browse files Browse the repository at this point in the history
Is valid element
  • Loading branch information
JohnLCaron authored May 2, 2024
2 parents c00f1e3 + 006fdd7 commit c86d0ab
Show file tree
Hide file tree
Showing 18 changed files with 42 additions and 58 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![License](https://img.shields.io/github/license/JohnLCaron/egk-ec)](https://github.com/JohnLCaron/egk-ec/blob/main/LICENSE.txt)
![GitHub branch checks state](https://img.shields.io/github/actions/workflow/status/JohnLCaron/egk-ec/unit-tests.yml)
![Coverage](https://img.shields.io/badge/coverage-90.4%25%20LOC%20(6997/7741)-blue)
![Coverage](https://img.shields.io/badge/coverage-90.4%25%20LOC%20(6991/7730)-blue)

# ElectionGuard-Kotlin Elliptic Curve

Expand Down
10 changes: 5 additions & 5 deletions src/main/kotlin/org/cryptobiotic/eg/core/ChaumPedersen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ fun ChaumPedersenRangeProofKnownNonce.verify(
val expandedProofs = proofs.mapIndexed { j, proof ->
// recomputes all the a and b values
val (cj, vj) = proof
if (cj.inBounds() && vj.inBounds()) results.add(Ok(true))
if (!cj.inBounds()) results.add(Err(" 5.B,6.B cj (idx $j) not in bounds"))
if (!vj.inBounds()) results.add(Err(" 5.C,6.C vj (idx $j) not in bounds"))
if (cj.isValidElement() && vj.isValidElement()) results.add(Ok(true))
if (!cj.isValidElement()) results.add(Err(" 5.B,6.B cj (idx $j) not in bounds"))
if (!vj.isValidElement()) results.add(Err(" 5.C,6.C vj (idx $j) not in bounds"))

val wj = (vj - j.toElementModQ(group) * cj)
ExpandedChaumPedersenProof(
Expand Down Expand Up @@ -222,7 +222,7 @@ fun ChaumPedersenProof.verifyDecryption(
val b = (encryptedVote.pad powP this.r) * (M powP this.c) // 9.3

// 9.A The given value v is in the set Z_q.
if (!this.r.inBounds()) {
if (!this.r.isValidElement()) {
return false
}
// The challenge value c = H(HE ; 0x30, K, A, B, a, b, M ). eq 71, 9.B.
Expand Down Expand Up @@ -253,7 +253,7 @@ fun ChaumPedersenProof.verifyContestDataDecryption(
val b = (hashedCiphertext.c0 powP this.r) * (beta powP this.c) // 11.2

// 11.A The given value v is in the set Z_q.
if (!this.r.inBounds()) {
if (!this.r.isValidElement()) {
return false
}
// The challenge value c = H(HE ; 0x31, K, C0 , C1 , C2 , a, b, β) // 11.B
Expand Down
15 changes: 3 additions & 12 deletions src/main/kotlin/org/cryptobiotic/eg/core/GroupContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ interface GroupContext {
/** Useful constant: the group generator */
val G_MOD_P: ElementModP

/** Useful constant: the inverse of the group generator */
val GINV_MOD_P: ElementModP

/** Useful constant: the group generator, squared */
val G_SQUARED_MOD_P: ElementModP

/** Useful constant: zero mod q */
val ZERO_MOD_Q: ElementModQ

Expand Down Expand Up @@ -134,6 +128,9 @@ interface Element {
*/
val context: GroupContext

/** Validates that this element is a member of the Group */
fun isValidElement(): Boolean

/** Converts to a [ByteArray] representation. Inverse to group.binaryToElementModX(). */
fun byteArray(): ByteArray

Expand Down Expand Up @@ -164,16 +161,10 @@ interface ElementModQ : Element, Comparable<ElementModQ> {

/** Checks whether this element is zero. */
fun isZero(): Boolean

/** Validate the element is in [0,Q) */
fun inBounds(): Boolean
}

interface ElementModP : Element, Comparable<ElementModP> {

/** Validates that this element is a member of the Group */
fun isValidElement(): Boolean

/** Computes b^e mod p */
infix fun powP(exp: ElementModQ): ElementModP

Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/org/cryptobiotic/eg/core/Schnorr.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ data class SchnorrProof(
// therefore, whoever generated v knows s

val inBoundsK = publicCommitment.isValidElement() // 2.A
val inBoundsU = response.inBounds() // 2.B
val inBoundsU = response.isValidElement() // 2.B
val validChallenge = c == challenge // 2.C
val success = inBoundsK && inBoundsU && validChallenge

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class EcElementModP(val group: EcGroupContext, val ec: VecElementP): ElementModP

/** Validate that this element is a member of the elliptic curve Group.*/
override fun isValidElement(): Boolean {
return group.vecGroup.isPointOnCurve(this.ec.x, this.ec.y)
return this.ec.isValidElement()
}

override fun multInv(): ElementModP {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class EcElementModQ(val group: EcGroupContext, val element: BigInteger): Element
override val context: GroupContext
get() = group

override fun inBounds() = element >= BigInteger.ZERO && element < 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))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ class EcGroupContext(val name: String, useNative: Boolean = true): GroupContext
val vecGroup: VecGroup = VecGroups.getEcGroup(name, useNative)
val ONE = EcElementModP(this, vecGroup.ONE)

override val GINV_MOD_P: ElementModP = EcElementModP(this, vecGroup.g.inv())
override val G_MOD_P: ElementModP = EcElementModP(this, vecGroup.g)
override val G_SQUARED_MOD_P: ElementModP = EcElementModP(this, vecGroup.g.square())
override val NUM_P_BITS: Int = vecGroup.pbitLength
override val MAX_BYTES_P: Int = vecGroup.pbyteLength + 1 // x plus sign of y
override val ONE_MOD_P: ElementModP = this.ONE
Expand Down
12 changes: 8 additions & 4 deletions src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/VecElementP.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,23 @@ open class VecElementP(
val pGroup: VecGroup,
val x: BigInteger,
val y: BigInteger,
safe: Boolean = false // eg randomElement() knows its safe
) {
val modulus = pGroup.primeModulus

constructor(group: VecGroup, xs: String, ys: String): this(group, BigInteger(xs,16), BigInteger(ys, 16))

init {
if (!safe && !pGroup.isPointOnCurve(x, y)) {
pGroup.isPointOnCurve(x, y)
if (!isValidElement()) {
throw RuntimeException("Given point is not on the described curve")
}
}

fun isValidElement(): Boolean {
// TODO this has to be true unless it was constructed with safe=true.
// Note this also checks that both x and y are in [0,P)
return pGroup.isPointOnCurve(x, y)
}

open fun acceleratePow(): VecElementP {
return this
}
Expand Down Expand Up @@ -90,7 +94,7 @@ open class VecElementP(
// Otherwise we perform multiplication of two points in general position.
// s = (y-e.y)/(x-e.x)

val s = y.subtract(other.y).multiply(x.subtract(other.x).modInverse(modulus).mod(modulus));
val s = y.subtract(other.y).multiply(x.subtract(other.x).modInverse(modulus).mod(modulus))

// rx = s^2 - (x + e.x)
val rx = s.multiply(s).subtract(this.x).subtract(other.x).mod(modulus)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,13 @@ open class VecElementPnative(
pGroup: VecGroup,
x: BigInteger,
y: BigInteger,
safe: Boolean = false
) : VecElementP(pGroup, x, y, safe) {
) : VecElementP(pGroup, x, y) {
val vgNative = pGroup as VecGroupNative

constructor(group: VecGroup, xs: String, ys: String): this(group, BigInteger(xs,16), BigInteger(ys, 16))

override fun acceleratePow(): VecElementP {
return VecElementPnativeAcc(pGroup, x, y, true)
return VecElementPnativeAcc(pGroup, x, y)
}

/** Compute the product of this element with other. */
Expand Down Expand Up @@ -89,8 +88,7 @@ class VecElementPnativeAcc(
pGroup: VecGroup,
x: BigInteger,
y: BigInteger,
safe: Boolean = false
) : VecElementPnative(pGroup, x, y, safe) {
) : VecElementPnative(pGroup, x, y) {
val tablePtr: ByteArray by lazy { // TODO free the native memory....

/**
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/VecGroup.kt
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ open class VecGroup(
pIs3mod4 = primeModulus.testBit(0) && primeModulus.testBit(1) // p mod 4 = 3, true for P-256
}

open fun makeVecModP(x: BigInteger, y: BigInteger, safe: Boolean = false) = VecElementP(this, x, y, safe)
open fun makeVecModP(x: BigInteger, y: BigInteger) = VecElementP(this, x, y)

fun elementFromByteArray(ba: ByteArray): VecElementP? = elementFromByteArray1(ba)

Expand Down Expand Up @@ -144,7 +144,7 @@ open class VecGroup(

if (jacobiSymbol(fx, primeModulus) == 1) {
val y2 = sqrt(fx)
return makeVecModP(x, y2, true)
return makeVecModP(x, y2)
}
} catch (e: RuntimeException) {
throw RuntimeException("Unexpected format exception", e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class VecGroupNative(
/** Pointer to curve parameters in native space. */
val nativePointer: ByteArray = VEC.getCurve("P-256")

override fun makeVecModP(x: BigInteger, y: BigInteger, safe: Boolean) = VecElementPnative(this, x, y, safe)
override fun makeVecModP(x: BigInteger, y: BigInteger) = VecElementPnative(this, x, y)

override fun sqrt(x: BigInteger): BigInteger {
return VEC.sqrt(x, primeModulus).toPositive()
Expand Down
16 changes: 4 additions & 12 deletions src/main/kotlin/org/cryptobiotic/eg/core/intgroup/IntGroup.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ class ProductionGroupContext(
val r: BigInteger
val oneModP: ProductionElementModP
val gModP: ProductionElementModP
val gInvModP by lazy { gPowP(qMinus1Q) }
val gSquaredModP: ProductionElementModP
val zeroModQ: ProductionElementModQ
val oneModQ: ProductionElementModQ
val twoModQ: ProductionElementModQ
Expand All @@ -48,7 +46,6 @@ class ProductionGroupContext(
r = rBytes.toBigInteger()
oneModP = ProductionElementModP(1U.toBigInteger(), this)
gModP = ProductionElementModP(g, this).acceleratePow() as ProductionElementModP
gSquaredModP = ProductionElementModP((g * g).mod(p), this)
zeroModQ = ProductionElementModQ(0U.toBigInteger(), this)
oneModQ = ProductionElementModQ(1U.toBigInteger(), this)
twoModQ = ProductionElementModQ(2U.toBigInteger(), this)
Expand All @@ -70,12 +67,6 @@ class ProductionGroupContext(
override val G_MOD_P
get() = gModP

override val GINV_MOD_P
get() = gInvModP

override val G_SQUARED_MOD_P
get() = gSquaredModP

override val ZERO_MOD_Q
get() = zeroModQ

Expand Down Expand Up @@ -189,7 +180,7 @@ class ProductionElementModQ(internal val element: BigInteger, val groupContext:

override fun isZero() = element == BigInteger.ZERO

override fun inBounds() = element >= BigInteger.ZERO && element < groupContext.q
override fun isValidElement() = element >= BigInteger.ZERO && element < groupContext.q

override operator fun compareTo(other: ElementModQ): Int = element.compareTo(other.getCompat(groupContext))

Expand Down Expand Up @@ -238,8 +229,9 @@ open class ProductionElementModP(internal val element: BigInteger, val groupCont
override operator fun compareTo(other: ElementModP): Int = element.compareTo(other.getCompat(groupContext))

/**
* Validates that this element is a member of the Integer Group, ie in Z_p^r.
* "Z_p^r is the set of r-th-residues in Z∗p", see spec 2.0 p.9
* Validates that this element is in Z_p^r, "set of r-th-residues in Z_p".
* "A value x is in Z_p^r if and only if x is an integer such that 0 ≤ x < p
* and x^q mod p == 1", see spec 2.0 p.9.
*/
override fun isValidElement(): Boolean {
groupContext.opCounts.getOrPut("exp") { AtomicInteger(0) }.incrementAndGet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class VerifyDecryption(
continue
}

if (!selection.proof.r.inBounds()) {
if (!selection.proof.r.isValidElement()) {
val what = if (isBallot) "12.A" else "9.A"
serrs.add(" $what response out of bounds")
}
Expand Down Expand Up @@ -128,7 +128,7 @@ class VerifyDecryption(

private fun verifyContestData(decryptedContestData: DecryptedTallyOrBallot.DecryptedContestData, errs: ErrorMessages){
// (11.A,14.A) The given value v is in the set Zq.
if (!decryptedContestData.proof.r.inBounds()) {
if (!decryptedContestData.proof.r.isValidElement()) {
errs.add(" (11.A,14.A) The value v is not in the set Zq.")
}
if (!decryptedContestData.proof.verifyContestDataDecryption(publicKey, extendedBaseHash, decryptedContestData.beta, decryptedContestData.encryptedContestData)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import org.cryptobiotic.util.ErrorMessages

//////////////////////////////////////////////////////////////////////////////
// pre-encryption
// TODO Is verification boxes 15, 16 the same as 5 and 6?

/*
Every step of verification that applies to traditional ElectionGuard ballots also applies to pre-
encrypted ballots – with the exception of the process for computing confirmation codes. However,
52there are some additional verification steps that must be applied to pre-encrypted ballots. Specifi-
there are some additional verification steps that must be applied to pre-encrypted ballots. Specifi-
cally, the following verifications should be done for every pre-encrypted cast ballot contained in the
election record.
• The ballot confirmation code correctly matches the hash of all contest hashes on the ballot
Expand Down
4 changes: 2 additions & 2 deletions src/test/kotlin/org/cryptobiotic/eg/core/GroupTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class GroupTest {

fun qInBounds(context: GroupContext) {
runTest {
forAll(propTestFastConfig, elementsModQ(context)) { it.inBounds() }
forAll(propTestFastConfig, elementsModQ(context)) { it.isValidElement() }
}
}

Expand Down Expand Up @@ -128,7 +128,7 @@ class GroupTest {
checkAll(propTestFastConfig, Arb.int(min = 0, max = intTestQ - 1)) { i ->
val iq = context.uIntToElementModQ(i.toUInt())
val q = context.ZERO_MOD_Q - iq
assertTrue(q.inBounds())
assertTrue(q.isValidElement())
assertEquals(context.ZERO_MOD_Q, q + iq)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class PowRadixTest {

assertEquals(ctxSlow.ONE_MOD_P, powRadix.pow(0.toElementModQ(ctxSlow)))
assertEquals(ctxSlow.G_MOD_P, powRadix.pow(1.toElementModQ(ctxSlow)))
assertEquals(ctxSlow.G_SQUARED_MOD_P, powRadix.pow(2.toElementModQ(ctxSlow)))
// assertEquals(ctxSlow.G_SQUARED_MOD_P, powRadix.pow(2.toElementModQ(ctxSlow)))

// check fewer cases because it's so much slower
checkAll(propTestFastConfig, elementsModQ(ctxSlow)) { e ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ internal class TinyGroupContext(
get() = oneModP
override val G_MOD_P: ElementModP
get() = gModP
override val GINV_MOD_P: ElementModP
val GINV_MOD_P: ElementModP
get() = gInvModP
override val G_SQUARED_MOD_P: ElementModP
val G_SQUARED_MOD_P: ElementModP
get() = gSquaredModP
override val ZERO_MOD_Q: ElementModQ
get() = zeroModQ
Expand Down Expand Up @@ -294,7 +294,7 @@ internal class TinyElementModQ(val element: UInt, val groupContext: TinyGroupCon
override val context: GroupContext
get() = groupContext

override fun inBounds(): Boolean = element < groupContext.q
override fun isValidElement(): Boolean = element < groupContext.q

override fun isZero(): Boolean = element == 0U

Expand Down
4 changes: 2 additions & 2 deletions src/test/kotlin/org/cryptobiotic/eg/decrypt/LagrangeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,11 @@ class LagrangeTest {
present: List<Int>) {
val available = trustees.filter {present.contains(it.xCoordinate())}
val lagrangeCoefficients = available.associate { it.id to computeLagrangeCoefficient(group, it.xCoordinate, present) }
lagrangeCoefficients.values.forEach { assertTrue( it.inBounds()) }
lagrangeCoefficients.values.forEach { assertTrue( it.isValidElement()) }

val weightedSum = group.addQ(
trustees.map {
assertTrue(it.computeSecretKeyShare().inBounds())
assertTrue(it.computeSecretKeyShare().isValidElement())
val coeff = lagrangeCoefficients[it.id] ?: throw IllegalArgumentException()
it.computeSecretKeyShare() * coeff
}
Expand Down

0 comments on commit c86d0ab

Please sign in to comment.