Replacing the Rusty Core of the Internet with FP

OpenSSL Usage

* Does not include IMAP and other apps that also use OpenSSL

OpenSSL Coverage

OpenSSL Coverage

OpenSSL Vulnerabilities

NOPE

Agenda

Elliptic Curves

 

Laws and properties of Elliptic Curves

 

Discrete Logarithm Problem

 

Scala Implementation of Elliptic Curve

Elliptic Curve Cryptography

y^2 = x^3 + ax + b
y2=x3+ax+by^2 = x^3 + ax + b

Point Addition

R = P + Q
R=P+QR = P + Q

Commutative

P + Q = Q + P
P+Q=Q+PP + Q = Q + P

Inverse

-Q + Q = O
Q+Q=O-Q + Q = O

Associative

( P + Q ) + R = P + ( Q + R )
(P+Q)+R=P+(Q+R)( P + Q ) + R = P + ( Q + R )

Identity

P + O = P
P+O=PP + O = P

Point Multiplication

Point Multiplication

Point Multiplication

Point Multiplication

Elliptic Curve Diffie Hellman

Elliptic Curve Diffie Hellman

nP * m = mP * n
nPm=mPnnP * m = mP * n

Shared Key:

Evaluated Mod Prime

Elliptic Point Multiplication




    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

What's "large"?

115792089237316195423570985008687907853269984665640564039457584007908834671663

Atoms in Universe

10000000000000000000000000000000000000000000000000000000000000000000000000000000

Secp256k1 prime

Discrete Log Problem

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

For educational purposes only!

Finite Field




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

Finite Field

Points




    sealed abstract class AffinePoint[+A]

    case class APoint[A](x:A, y:A) extends AffinePoint[A]

    case object Infinity extends AffinePoint[Nothing]

Points




    sealed abstract class HomogeneousPoint[+A]

    case class HPoint[A](x:A, y:A, z:A) extends HomogeneousPoint[A]

    case object Infinity extends HomogeneousPoint[Nothing]

Point Add



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

Point Double

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

Point Times

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

Point Group

  

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

So what?

  • 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.

What about SPEED?!?

  • BigInt is slow

  • Radix 51 trick using CPU pipelining

  • Use real newtypes to save allocations

  • Abandon the JVM

    • Cryptonite in Haskell (using GMP)

What now?

  • Don't be scared of Crypto.
  • Don't invent your own algorithms.
  • Choose implementations which are well tested.
  • Old Crypto is not good Crypto.

Replacing the Rusty Core with FP

By Colt Frederickson

Replacing the Rusty Core with FP

  • 1,928