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