Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is valid element #78

Merged
merged 2 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading