Elliptic Curves
Laws and properties of Elliptic Curves
Discrete Logarithm Problem
Scala Implementation of Elliptic Curve
mul pt 0 = InfinityPoint
mul pt 1 = pt
mul pt n
| even n = mul (double pt) (n `div` 2)
| otherwise = add (mul (double pt) (n `div` 2)) pt
115792089237316195423570985008687907853269984665640564039457584007908834671663
10000000000000000000000000000000000000000000000000000000000000000000000000000000
Given a point \(Q\) which is a multiple of \(P\) find the \(n\) that was used to get to \(Q\)
$$nP = Q$$
Solve for n
case class Fp private(i:BigInt) extends AnyVal
object Fp {
final val Prime = ??? //Huge prime, around 256 bits long
def create(i:BigInt): Fp = Fp(mod(i, Prime))
}
implicit val fieldForFp: Field[Fp] = new Field.WithDefaultGCD[Fp] {
def negate(x: Fp): Fp = create(-x.value)
val zero: Fp = create(0)
def plus(x: Fp, y: Fp): Fp = create(x.value + y.value)
def times(x: Fp, y: Fp): Fp = create(x.value * y.value)
def div(x: Fp, y: Fp): Fp = create(x.value * multInverse(y.value, Prime))
val one: Fp = create(1)
}
sealed abstract class AffinePoint[+A]
case class APoint[A](x:A, y:A) extends AffinePoint[A]
case object Infinity extends AffinePoint[Nothing]
sealed abstract class HomogeneousPoint[+A]
case class HPoint[A](x:A, y:A, z:A) extends HomogeneousPoint[A]
case object Infinity extends HomogeneousPoint[Nothing]
def add[A: Field: Eq](p1: AffinePoint[A], p2: AffinePoint[A]) =
(p1,p2) match {
case (Infinity, p) => p
case (p, Infinity) => p
case (p1, p2) if p1 === p2 => double(p1)
case (APoint(_, y1), APoint(_, y2)) if (y1 == -y2) => Infinity
case (APoint(x1, y1), APoint(x2, y2)) =>
val m = (y1 - y2) * (Field[A].one / (x1 - x2))
val x3 = -x1 - x2 + (m.pow(2))
val y3 = -y1 + m * (x1 - x3)
APoint(x3, y3)
}
def double[A: Field: Eq](p: AffinePoint[A]): AffinePoint[A] = p match {
case Infinity => Infinity
case APoint(_, y) if y === Field[A].zero => Infinity
case APoint(x, y) =>
val twoYInverse = Field[A].one / (y * 2)
val m = 3 * x.pow(2) * twoYInverse
val x2 = -2 * x + m.pow(2)
val y2 = -y + m * (x - x2)
APoint(x2, y2)
}
def sumN[A: AdditiveMonoid](a: A, n: BigInt): A = {
@annotation.tailrec
def loop(a: A, k: BigInt, extra: A): A =
if (k == 1) a + extra else {
val x = if ((k & 1) == 1) a + extra else extra
loop(a + a, k >> 1, x)
}
if (n == 0) AdditiveMonoid[A].zero else if (n == 1) a else loop(a, n - 1, a)
}
implicit def aGroup[A: Field: Eq] = new AdditiveCommutativeGroup[AffinePoint[A]] {
def plus(x: AffinePoint[A], y: AffinePoint[A]): AffinePoint[A] = add(x,y)
val zero: AffinePoint[A] = Infinity
def negate(p: AffinePoint[A]): AffinePoint[A] = p match {
case Infinity => Infinity
case APoint(x, y) => APoint(x, -y)
}
}
This is all just algebra. Nothing complex!
We can now test our points for any Field!
Parametricy ensures we're not doing crazy hacks.
Laws on fields and groups keep us safe.
Property based testing.
BigInt is slow
Radix 51 trick using CPU pipelining
Use real newtypes to save allocations
Abandon the JVM
Cryptonite in Haskell (using GMP)