From 385f2fa3adf8d09454ba637390b50ac0450cbbe5 Mon Sep 17 00:00:00 2001 From: Jakob Heher Date: Fri, 25 Oct 2024 14:47:52 +0200 Subject: [PATCH] eccurve refactor draft --- .../at/asitplus/signum/ecmath/ECMath.kt | 351 +++++++++++------- .../asitplus/signum/indispensable/ECCurve.kt | 116 +++--- .../asitplus/signum/indispensable/ECPoint.kt | 3 + .../src/jvmTest/kotlin/ECCurveTest.kt | 33 +- .../at/asitplus/signum/ecmath/ECMathTest.kt | 14 +- 5 files changed, 324 insertions(+), 193 deletions(-) diff --git a/indispensable/src/commonMain/kotlin/at/asitplus/signum/ecmath/ECMath.kt b/indispensable/src/commonMain/kotlin/at/asitplus/signum/ecmath/ECMath.kt index a9ad538a..57c0382b 100644 --- a/indispensable/src/commonMain/kotlin/at/asitplus/signum/ecmath/ECMath.kt +++ b/indispensable/src/commonMain/kotlin/at/asitplus/signum/ecmath/ECMath.kt @@ -1,124 +1,204 @@ package at.asitplus.signum.ecmath -import at.asitplus.signum.indispensable.ECPoint +import at.asitplus.signum.indispensable.* import com.ionspin.kotlin.bignum.integer.BigInteger import com.ionspin.kotlin.bignum.modular.ModularBigInteger import kotlin.math.max +interface ECMathImpl { + /** checked in ECMathTest.kt */ + fun checkRequirements(curve: NewECCurve) + + /** addition (not necessarily constant time) */ + fun plus(v: ECPoint, w: ECPoint): ECPoint = ct_plus(v,w) + /** constant-time point addition */ + fun ct_plus(v: ECPoint, w: ECPoint): ECPoint + + /** adding a point to itself */ + fun double(p: ECPoint): ECPoint = ct_double(p) + /** constant-time doubling */ + fun ct_double(p: ECPoint): ECPoint + + /** scalar multiplication (not necessarily constant time) */ + fun mul(p: BigInteger, Q: ECPoint) = ct_mul(p,Q) + /** constant-time scalar multiplication */ + fun ct_mul(p: BigInteger, Q: ECPoint): ECPoint +} + +val NewECCurve.math: ECMathImpl inline get() = when (this) { + ECCurve.SECP_256_R_1, ECCurve.SECP_384_R_1, ECCurve.SECP_521_R_1 -> WeierstrassArithmeticForAEqualsMinus3 +} + +object WeierstrassArithmeticForAEqualsMinus3: ECMathImpl { + override fun checkRequirements(curve: NewECCurve) { + check(curve is WeierstrassCurve) + check(curve.a == BigInteger(-3).toModularBigInteger(curve.modulus)) + } + + /* Algorithm 4 from https://eprint.iacr.org/2015/1060.pdf */ + override fun ct_plus(v: ECPoint, w: ECPoint): ECPoint { + val b = v.curve.b + val X1 = v.homX + val Y1 = v.homY + val Z1 = v.homZ + val X2 = w.homX + val Y2 = w.homY + val Z2 = w.homZ + var t0: ModularBigInteger + var t1: ModularBigInteger + var t2: ModularBigInteger + var t3: ModularBigInteger + var t4: ModularBigInteger + var X3: ModularBigInteger + var Y3: ModularBigInteger + var Z3: ModularBigInteger + /* 1. */ t0 = X1 * X2 + /* 2. */ t1 = Y1 * Y2 + /* 3. */ t2 = Z1 * Z2 + /* 4. */ t3 = X1 + Y1 + /* 5. */ t4 = X2 + Y2 + /* 6. */ t3 = t3 * t4 + /* 7. */ t4 = t0 + t1 + /* 8. */ t3 = t3 - t4 + /* 9. */ t4 = Y1 + Z1 + /* 10. */ X3 = Y2 + Z2 + /* 11. */ t4 = t4 * X3 + /* 12. */ X3 = t1 + t2 + /* 13. */ t4 = t4 - X3 + /* 14. */ X3 = X1 + Z1 + /* 15. */ Y3 = X2 + Z2 + /* 16. */ X3 = X3 * Y3 + /* 17. */ Y3 = t0 + t2 + /* 18. */ Y3 = X3 - Y3 + /* 19. */ Z3 = b * t2 + /* 20. */ X3 = Y3 - Z3 + /* 21. */ Z3 = X3 + X3 + /* 22. */ X3 = X3 + Z3 + /* 23. */ Z3 = t1 - X3 + /* 24. */ X3 = t1 + X3 + /* 25. */ Y3 = b * Y3 + /* 26. */ t1 = t2 + t2 + /* 27. */ t2 = t1 + t2 + /* 28. */ Y3 = Y3 - t2 + /* 29. */ Y3 = Y3 - t0 + /* 30. */ t1 = Y3 + Y3 + /* 31. */ Y3 = t1 + Y3 + /* 32. */ t1 = t0 + t0 + /* 33. */ t0 = t1 + t0 + /* 34. */ t0 = t0 - t2 + /* 35. */ t1 = t4 * Y3 + /* 36. */ t2 = t0 * Y3 + /* 37. */ Y3 = X3 * Z3 + /* 38. */ Y3 = Y3 + t2 + /* 39. */ X3 = t3 * X3 + /* 40. */ X3 = X3 - t1 + /* 41. */ Z3 = t4 * Z3 + /* 42. */ t1 = t3 * t0 + /* 43. */ Z3 = Z3 + t1 + return ECPoint.General.unsafeFromXYZ(v.curve, X3, Y3, Z3) + } + + /* Algorithm 6 from https://eprint.iacr.org/2015/1060.pdf */ + override fun ct_double(p: ECPoint): ECPoint { + val b = p.curve.b + val X = p.homX + val Y = p.homY + val Z = p.homZ + var t0: ModularBigInteger + var t1: ModularBigInteger + var t2: ModularBigInteger + var t3: ModularBigInteger + var X3: ModularBigInteger + var Y3: ModularBigInteger + var Z3: ModularBigInteger + /* 1. */ t0 = X * X + /* 2. */ t1 = Y * Y + /* 3. */ t2 = Z * Z + /* 4. */ t3 = X * Y + /* 5. */ t3 = t3 + t3 + /* 6. */ Z3 = X * Z + /* 7. */ Z3 = Z3 + Z3 + /* 8. */ Y3 = b * t2 + /* 9. */ Y3 = Y3 - Z3 + /* 10. */ X3 = Y3 + Y3 + /* 11. */ Y3 = X3 + Y3 + /* 12. */ X3 = t1 - Y3 + /* 13. */ Y3 = t1 + Y3 + /* 14. */ Y3 = X3 * Y3 + /* 15. */ X3 = X3 * t3 + /* 16. */ t3 = t2 + t2 + /* 17. */ t2 = t2 + t3 + /* 18. */ Z3 = b * Z3 + /* 19. */ Z3 = Z3 - t2 + /* 20. */ Z3 = Z3 - t0 + /* 21. */ t3 = Z3 + Z3 + /* 22. */ Z3 = Z3 + t3 + /* 23. */ t3 = t0 + t0 + /* 24. */ t0 = t3 + t0 + /* 25. */ t0 = t0 - t2 + /* 26. */ t0 = t0 * Z3 + /* 27. */ Y3 = Y3 + t0 + /* 28. */ t0 = Y * Z + /* 29. */ t0 = t0 + t0 + /* 30. */ Z3 = t0 * Z3 + /* 31. */ X3 = X3 - Z3 + /* 32. */ Z3 = t0 * t1 + /* 33. */ Z3 = Z3 + Z3 + /* 34. */ Z3 = Z3 + Z3 + return ECPoint.General.unsafeFromXYZ(p.curve, X3, Y3, Z3) + } + + // TODO: i'm sure this could be smarter (keyword: "comb") + override fun mul(p: BigInteger, Q: ECPoint): ECPoint { + var o = Q + var sum = if (p.bitAt(0)) Q else Q.curve.IDENTITY + /* double-and-add */ + for (i in 1L..= 0) { + if (p.bitAt(i)) { + R0 = (R0+R1) + R1 = R1.double() + } else { + R1 = (R0+R1) + R0 = R0.double() + } + } + return R0 + } +} + /** adds `other` to `this` and returns the result */ -/* Algorithm 4 from https://eprint.iacr.org/2015/1060.pdf */ -operator fun ECPoint.plus(other: ECPoint): ECPoint { +inline operator fun ECPoint.plus(other: ECPoint): ECPoint { require(this.curve == other.curve) - val b = this.curve.b - val X1 = this.homX - val Y1 = this.homY - val Z1 = this.homZ - val X2 = other.homX - val Y2 = other.homY - val Z2 = other.homZ - var t0: ModularBigInteger - var t1: ModularBigInteger - var t2: ModularBigInteger - var t3: ModularBigInteger - var t4: ModularBigInteger - var X3: ModularBigInteger - var Y3: ModularBigInteger - var Z3: ModularBigInteger - /* 1. */ t0 = X1 * X2 - /* 2. */ t1 = Y1 * Y2 - /* 3. */ t2 = Z1 * Z2 - /* 4. */ t3 = X1 + Y1 - /* 5. */ t4 = X2 + Y2 - /* 6. */ t3 = t3 * t4 - /* 7. */ t4 = t0 + t1 - /* 8. */ t3 = t3 - t4 - /* 9. */ t4 = Y1 + Z1 - /* 10. */ X3 = Y2 + Z2 - /* 11. */ t4 = t4 * X3 - /* 12. */ X3 = t1 + t2 - /* 13. */ t4 = t4 - X3 - /* 14. */ X3 = X1 + Z1 - /* 15. */ Y3 = X2 + Z2 - /* 16. */ X3 = X3 * Y3 - /* 17. */ Y3 = t0 + t2 - /* 18. */ Y3 = X3 - Y3 - /* 19. */ Z3 = b * t2 - /* 20. */ X3 = Y3 - Z3 - /* 21. */ Z3 = X3 + X3 - /* 22. */ X3 = X3 + Z3 - /* 23. */ Z3 = t1 - X3 - /* 24. */ X3 = t1 + X3 - /* 25. */ Y3 = b * Y3 - /* 26. */ t1 = t2 + t2 - /* 27. */ t2 = t1 + t2 - /* 28. */ Y3 = Y3 - t2 - /* 29. */ Y3 = Y3 - t0 - /* 30. */ t1 = Y3 + Y3 - /* 31. */ Y3 = t1 + Y3 - /* 32. */ t1 = t0 + t0 - /* 33. */ t0 = t1 + t0 - /* 34. */ t0 = t0 - t2 - /* 35. */ t1 = t4 * Y3 - /* 36. */ t2 = t0 * Y3 - /* 37. */ Y3 = X3 * Z3 - /* 38. */ Y3 = Y3 + t2 - /* 39. */ X3 = t3 * X3 - /* 40. */ X3 = X3 - t1 - /* 41. */ Z3 = t4 * Z3 - /* 42. */ t1 = t3 * t0 - /* 43. */ Z3 = Z3 + t1 - return ECPoint.General.unsafeFromXYZ(curve, X3, Y3, Z3) + return this.curve.math.plus(this, other) } +inline infix fun ECPoint.ct_plus(other: ECPoint): ECPoint { + require(this.curve == other.curve) + return this.curve.math.ct_plus(this, other) +} + + /** adds `this` to `this` and returns the result */ -/* Algorithm 6 from https://eprint.iacr.org/2015/1060.pdf */ -fun ECPoint.double(): ECPoint { - val b = this.curve.b - val X = this.homX - val Y = this.homY - val Z = this.homZ - var t0: ModularBigInteger - var t1: ModularBigInteger - var t2: ModularBigInteger - var t3: ModularBigInteger - var X3: ModularBigInteger - var Y3: ModularBigInteger - var Z3: ModularBigInteger - /* 1. */ t0 = X * X - /* 2. */ t1 = Y * Y - /* 3. */ t2 = Z * Z - /* 4. */ t3 = X * Y - /* 5. */ t3 = t3 + t3 - /* 6. */ Z3 = X * Z - /* 7. */ Z3 = Z3 + Z3 - /* 8. */ Y3 = b * t2 - /* 9. */ Y3 = Y3 - Z3 - /* 10. */ X3 = Y3 + Y3 - /* 11. */ Y3 = X3 + Y3 - /* 12. */ X3 = t1 - Y3 - /* 13. */ Y3 = t1 + Y3 - /* 14. */ Y3 = X3 * Y3 - /* 15. */ X3 = X3 * t3 - /* 16. */ t3 = t2 + t2 - /* 17. */ t2 = t2 + t3 - /* 18. */ Z3 = b * Z3 - /* 19. */ Z3 = Z3 - t2 - /* 20. */ Z3 = Z3 - t0 - /* 21. */ t3 = Z3 + Z3 - /* 22. */ Z3 = Z3 + t3 - /* 23. */ t3 = t0 + t0 - /* 24. */ t0 = t3 + t0 - /* 25. */ t0 = t0 - t2 - /* 26. */ t0 = t0 * Z3 - /* 27. */ Y3 = Y3 + t0 - /* 28. */ t0 = Y * Z - /* 29. */ t0 = t0 + t0 - /* 30. */ Z3 = t0 * Z3 - /* 31. */ X3 = X3 - Z3 - /* 32. */ Z3 = t0 * t1 - /* 33. */ Z3 = Z3 + Z3 - /* 34. */ Z3 = Z3 + Z3 - return ECPoint.General.unsafeFromXYZ(curve, X3, Y3, Z3) +inline fun ECPoint.double(): ECPoint { + return this.curve.math.double(this) +} + +/** adds `this` to `this` in constant time and returns the result */ +inline fun ECPoint.ct_double(): ECPoint { + return this.curve.math.ct_double(this) } @Suppress("NOTHING_TO_INLINE") @@ -135,20 +215,13 @@ inline operator fun ECPoint.Normalized.unaryMinus() = @Suppress("NOTHING_TO_INLINE") inline operator fun ECPoint.minus(other: ECPoint) = this + (-other) -// TODO: i'm sure this could be smarter (keyword: "comb") -// i'm also sure this isn't resistant to timing side channels if that is something you care about -operator fun BigInteger.times(point: ECPoint): ECPoint { - var o = point - var sum = if (this.bitAt(0)) point else point.curve.IDENTITY - /* double-and-add */ - for (i in 1L..= 0) { - if (k.bitAt(i)) { - R0 = (R0+R1) - R1 = R1.double() - } else { - R1 = (R0+R1) - R0 = R0.double() - } - } - return R0 -} diff --git a/indispensable/src/commonMain/kotlin/at/asitplus/signum/indispensable/ECCurve.kt b/indispensable/src/commonMain/kotlin/at/asitplus/signum/indispensable/ECCurve.kt index 0b154c0d..d457b7c7 100644 --- a/indispensable/src/commonMain/kotlin/at/asitplus/signum/indispensable/ECCurve.kt +++ b/indispensable/src/commonMain/kotlin/at/asitplus/signum/indispensable/ECCurve.kt @@ -17,10 +17,57 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -@Suppress("NOTHING_TO_INLINE") inline fun UInt.ceilDiv(other: UInt) = (floorDiv(other)) + (if (rem(other) != 0u) 1u else 0u) +sealed interface NewECCurve { + /** Identity element of the curve */ + val IDENTITY: ECPoint + /** G: Generator of cyclic curve subgroup */ + val generator: ECPoint.Normalized + /** r: Order of (the cyclic subgroup generated by) G */ + val order: BigInteger + /** h: Cofactor of the cyclic subgroup generated by G */ + val cofactor: BigInteger + /** n: Order of the complete elliptic curve group */ + val n: BigInteger + /** p: Prime characteristic of the underlying field */ + val modulus: BigInteger + /** m: Extension degree of the underlying field */ + val extensionDegree: BigInteger + /** q: Order of the underlying field */ + val fieldOrder: BigInteger + + companion object { + val entries: Iterable = listOf( + ECCurve.SECP_256_R_1, ECCurve.SECP_384_R_1, ECCurve.SECP_521_R_1) + } +} +// convenience aliases: +/** G: Generator of cyclic curve subgroup */ +val NewECCurve.G inline get() = this.generator +/** r: Order of (the cyclic subgroup generated by) G */ +val NewECCurve.r inline get() = this.order +/** h: Cofactor of the cyclic subgroup generated by G */ +val NewECCurve.h inline get() = this.cofactor +/** p: Prime characteristic of the underlying prime field */ +val NewECCurve.p inline get() = this.modulus +/** m: Extension degree of the underlying field */ +val NewECCurve.m inline get() = this.extensionDegree +/** q: Order of the underlying field */ +val NewECCurve.q inline get() = this.fieldOrder +/** the number of bits/bytes needed to store scalar multipliers (such as private keys) in unsigned form */ +val NewECCurve.scalarLength inline get() = BitLength.of(order) +/** the number of bits/bytes needed to store point coordinates (such as public key coordinates) in unsigned form */ +val NewECCurve.coordinateLength inline get() = BitLength.of(modulus*extensionDegree) + +sealed interface WeierstrassCurve : NewECCurve { + /** a: Curve equation coefficient */ + val a: ModularBigInteger + /** b: Curve equation coefficient */ + val b: ModularBigInteger +} + /** * EC Curve Class [jwkName] really does use established JWK curve names */ @@ -28,7 +75,7 @@ inline fun UInt.ceilDiv(other: UInt) = enum class ECCurve( val jwkName: String, override val oid: ObjectIdentifier, -) : Identifiable { +) : Identifiable, WeierstrassCurve { /** NIST curve [secp256r1](https://neuromancer.sk/std/nist/P-256) */ SECP_256_R_1("P-256", KnownOIDs.prime256v1), /** NIST curve [secp384r1](https://neuromancer.sk/std/nist/P-384) */ @@ -36,16 +83,10 @@ enum class ECCurve( /** NIST curve [secp521r1](https://neuromancer.sk/std/nist/P-521) */ SECP_521_R_1("P-521", KnownOIDs.secp521r1); - val IDENTITY: ECPoint by lazy { + override val IDENTITY: ECPoint by lazy { ECPoint.General.unsafeFromXYZ(this, coordinateCreator.ZERO, coordinateCreator.ONE, coordinateCreator.ZERO) } - /** the number of bits/bytes needed to store scalar multipliers (such as private keys) in unsigned form */ - inline val scalarLength get() = BitLength.of(order) - - /** the number of bits/bytes needed to store point coordinates (such as public key coordinates) in unsigned form */ - inline val coordinateLength get() = BitLength.of(modulus) - @Deprecated("Use scalarLength to express private key lengths", ReplaceWith("scalarLength.bits")) /** the number of bits needed to store a private key in unsigned form */ inline val keyLengthBits: UInt get() = scalarLength.bits @@ -61,11 +102,8 @@ enum class ECCurve( internal val coordinateCreator by lazy { ModularBigInteger.creatorForModulo(this.modulus) } internal val scalarCreator by lazy { ModularBigInteger.creatorForModulo(this.order) } - /** - * p: Prime modulus of the underlying prime field - * See https://www.secg.org/sec2-v2.pdf - */ - val modulus: BigInteger by lazy { + /* See https://www.secg.org/sec2-v2.pdf */ + override val modulus: BigInteger by lazy { when (this) { SECP_256_R_1 -> "FFFFFFFF 00000001 00000000 00000000 00000000 FFFFFFFF FFFFFFFF FFFFFFFF" @@ -82,11 +120,8 @@ enum class ECCurve( }.replace(" ", "").toBigInteger(16) } - /** - * a: Curve equation coefficient - * See https://www.secg.org/sec2-v2.pdf - */ - val a: ModularBigInteger by lazy { + /* See https://www.secg.org/sec2-v2.pdf */ + override val a: ModularBigInteger by lazy { when (this) { SECP_256_R_1 -> "FFFFFFFF 00000001 00000000 00000000 00000000 FFFFFFFF FFFFFFFF FFFFFFFC" @@ -104,11 +139,8 @@ enum class ECCurve( } } - /** - * b: Curve equation coefficient - * See https://www.secg.org/sec2-v2.pdf - */ - val b: ModularBigInteger by lazy { + /* See https://www.secg.org/sec2-v2.pdf */ + override val b: ModularBigInteger by lazy { when (this) { SECP_256_R_1 -> "5AC635D8 AA3A93E7 B3EBBD55 769886BC 651D06B0 CC53B0F6 3BCE3C3E 27D2604B" @@ -126,11 +158,8 @@ enum class ECCurve( } } - /** - * G: Generator of cyclic curve subgroup - * See https://www.secg.org/sec2-v2.pdf - */ - val generator: ECPoint.Normalized by lazy { + /* See https://www.secg.org/sec2-v2.pdf */ + override val generator: ECPoint.Normalized by lazy { when (this) { SECP_256_R_1 -> "04 6B17D1F2 E12C4247 F8BCE6E5 63A440F2 77037D81 2DEB33A0" + @@ -153,11 +182,8 @@ enum class ECCurve( .let { CryptoPublicKey.EC.fromAnsiX963Bytes(this, it).publicPoint } } - /** - * n: Order of (the cyclic subgroup generated by) G - * See https://www.secg.org/sec2-v2.pdf - */ - val order: BigInteger by lazy { + /* See https://www.secg.org/sec2-v2.pdf */ + override val order: BigInteger by lazy { when (this) { SECP_256_R_1 -> "FFFFFFFF 00000000 FFFFFFFF FFFFFFFF BCE6FAAD A7179E84 F3B9CAC2" + @@ -174,18 +200,24 @@ enum class ECCurve( }.replace(" ", "").toBigInteger(16) } - /** - * h: Cofactor of the cyclic subgroup generated by G - * See https://www.secg.org/sec2-v2.pdf - */ - val cofactor: Int + /* See https://www.secg.org/sec2-v2.pdf */ + override val cofactor: BigInteger get() = when (this) { - SECP_256_R_1 -> 1 - SECP_384_R_1 -> 1 - SECP_521_R_1 -> 1 + SECP_256_R_1 -> BigInteger.ONE + SECP_384_R_1 -> BigInteger.ONE + SECP_521_R_1 -> BigInteger.ONE } + override val fieldOrder: BigInteger + get() = modulus + + override val extensionDegree: BigInteger + get() = BigInteger.ONE + + override val n: BigInteger + get() = order + companion object { fun of(bits: UInt) = entries.find { it.scalarLength.bits == bits } } diff --git a/indispensable/src/commonMain/kotlin/at/asitplus/signum/indispensable/ECPoint.kt b/indispensable/src/commonMain/kotlin/at/asitplus/signum/indispensable/ECPoint.kt index e9e43cf5..0fdc2f5c 100644 --- a/indispensable/src/commonMain/kotlin/at/asitplus/signum/indispensable/ECPoint.kt +++ b/indispensable/src/commonMain/kotlin/at/asitplus/signum/indispensable/ECPoint.kt @@ -70,6 +70,9 @@ sealed class ECPoint private constructor( /** y coordinate of the point (x,y) */ val y inline get() = homY + inline operator fun component1() = x + inline operator fun component2() = y + val xBytes inline get() = x.toByteArray().ensureSize(curve.coordinateLength.bytes) val yBytes inline get() = y.toByteArray().ensureSize(curve.coordinateLength.bytes) val yCompressed get() = compressY(curve, x, y) diff --git a/indispensable/src/jvmTest/kotlin/ECCurveTest.kt b/indispensable/src/jvmTest/kotlin/ECCurveTest.kt index 9a0afe9c..2d7724bd 100644 --- a/indispensable/src/jvmTest/kotlin/ECCurveTest.kt +++ b/indispensable/src/jvmTest/kotlin/ECCurveTest.kt @@ -1,7 +1,11 @@ -import at.asitplus.signum.indispensable.ECCurve +import at.asitplus.signum.indispensable.* import com.ionspin.kotlin.bignum.integer.toBigInteger import io.kotest.core.spec.style.FreeSpec +import io.kotest.datatest.withData +import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe +import java.util.Stack +import kotlin.reflect.KClass /** @@ -46,4 +50,31 @@ class ECCurveTest : FreeSpec({ ECCurve.SECP_384_R_1.coordinateLength.bytes shouldBe 48u ECCurve.SECP_521_R_1.coordinateLength.bytes shouldBe 66u } + + "EC interface entries completeness" - { + val discovered = mutableSetOf() + val queue = Stack>() + queue.push(NewECCurve::class) + while (!queue.empty()) { + queue.pop().sealedSubclasses.shouldNotBeNull().forEach { + when (val o = it.objectInstance) { + null -> queue.push(it) + else -> discovered.add(o) + } + } + } + NewECCurve.entries.toSet() shouldBe discovered + } + + "EC parameter relationships" - { + withData(NewECCurve.entries) { curve -> + curve.modulus * curve.extensionDegree shouldBe curve.fieldOrder + curve.order * curve.cofactor shouldBe curve.n + if (curve is WeierstrassCurve) { + val (x, y) = curve.generator + // weierstrass form curve equation: y² = x³+ax+b + x.pow(3) + (curve.a * x) + curve.b shouldBe y.pow(2) + } + } + } }) diff --git a/indispensable/src/jvmTest/kotlin/at/asitplus/signum/ecmath/ECMathTest.kt b/indispensable/src/jvmTest/kotlin/at/asitplus/signum/ecmath/ECMathTest.kt index 8ca30f5a..f8a7f185 100644 --- a/indispensable/src/jvmTest/kotlin/at/asitplus/signum/ecmath/ECMathTest.kt +++ b/indispensable/src/jvmTest/kotlin/at/asitplus/signum/ecmath/ECMathTest.kt @@ -1,7 +1,6 @@ package at.asitplus.signum.ecmath -import at.asitplus.signum.indispensable.ECCurve -import at.asitplus.signum.indispensable.ECPoint +import at.asitplus.signum.indispensable.* import com.ionspin.kotlin.bignum.integer.BigInteger import com.ionspin.kotlin.bignum.integer.Quadruple import com.ionspin.kotlin.bignum.integer.Sign @@ -28,12 +27,9 @@ private fun ECCurve.randomPoint(): ECPoint = }}.firstNotNullOf { it.getOrNull() } class ECMathTest : FreeSpec({ - "Assumption: All implemented curves are prime order Weierstrass curves with a = -3" - { - withData(ECCurve.entries) { curve -> - // if new curves are ever added that violate this assumption, - // the algorithms in ECMath must be revisited! - // the current algorithm only works on this particular class of curve - curve.a == curve.coordinateCreator.fromInt(-3) + "Curves have appropriate math objects assigned" - { + withData(NewECCurve.entries) { curve -> + curve.math.checkRequirements(curve) } } "Addition: group axioms" - { @@ -747,7 +743,7 @@ class ECMathTest : FreeSpec({ withData(ECCurve.entries) { curve -> withData(generateSequence { Pair(curve.randomScalar(), curve.randomPoint())}.take(10)) { (k,P) -> - montgomeryMul(k.residue,P) shouldBe (k*P) + (k ct_mul P) shouldBe (k * P) } } }