Montgomery Ladder in ECC

This is a technique to efficiently calculate the x coordinate x(nP)\bold{x}(nP) given the x coordinate of a point on a Montgomery curve x(P)\bold{x}(P). This method is also easily implemented in a constant-time way.

This works for curves:

By2=x3+Ax2+xBy^2 = x^3 + Ax^2 + x

Satisfying B(A24)0B(A^2 - 4) \neq 0.

In Summary

The ladder itself

func ScalarMul(n Scalar, x Field) Field {
  nP := Projective{1, 0}
  n1P := Projective{x, 1}

  swap := 0
  for b := range n.msb .. n.lsb {
    swap ^= b
    condSwap(swap, nP, n1P)
    swap = b

    nP, n1P = step0(x, nP, n1P)
  }
  condSwap(swap, nP, n1P)
  return nP.X / nP.Z
}

Implementing the individual step:

// In field elements of course
const A24 = (A - 2) / 4

func step0(x Field, nP, n1P Projective) (Projective, Projective) {
  double_X := nP.X + nP.Z
  double_Z := nP.X - nP.Z
  // Addition
  add_X := n1P.X + n1P.Z
  add_Z := nP.X - nP.Z
  add_X *= double_Z
  add_Z *= double_X
  tmp := add_X - add_Z
  add_X += add_Z
  add_Z = tmp
  add_X.square()
  add_Z.square()
  add_Z *= x
  // Now we can do doubling
  double_X.square()
  double_Z.square()
  // Store for later
  Xn_plus_Zn_squared := double_X
  double_X *= double_Z
  tmp = Xn_plus_Zn_squared - double_Z
  double_Z = A24 * tmp
  double_Z += Xn_plus_Zn_squared
  double_Z *= tmp
  return (
    Projective{double_X, double_Z},
    Projective{add_X, add_Z}
  )
}

This could be optimized to reuse nP and n1P's buffers better as well

Doubling Formula

Given a point P=(x,y)P = (x, y), the formula for 2P2P is given by:

x2=Bλ2A2xy2=λ(xx2)y\begin{aligned} &x_2 = B \lambda^2 - A - 2x \cr &y_2 = \lambda (x - x_2) - y \end{aligned}

where:

λ=3x2+2Ax+12By\lambda = \frac{3x^2 + 2Ax + 1}{2By}

(This formula is derived geometrically. λ\lambda is the slope of the tangent line to the curve at the point PP.)

Because λ2\lambda^2 features By2By^2 in the denominator, we can replace that with x3+Ax2+xx^3 + Ax^2 + x, and then calculate out the following identity:

x2=(x21)24(x3+Ax2+x)x_2 = \frac{(x^2 - 1)^2}{4(x^3 + Ax^2 + x)}

This formula calculates x(2P)\bold{x}(2P) given only x(P)\bold{x}(P), and can be shown to be valid in other edge cases. See: 1.

Projective Form

If we use projective coordinates, then we have x=X/Zx = X / Z. Plugging this into the formula we had above, we get:

x2=(X2Z2)24ZX(X+AXZ+Z2)x_2 = \frac{(X^2 - Z^2)^2}{4ZX(X + AXZ + Z^2)}

In other terms, this means we have:

X2=(X2Z2)2Z2=4XZ(X+AXZ+Z2)\begin{aligned} &X_2 = (X^2 - Z^2)^2 \cr &Z_2 = 4XZ(X + AXZ + Z^2) \end{aligned}

If we calculate this directly, a point doubling takes:

2M+3S+2C2 \bold{M} + 3 \bold{S} + 2 \bold{C}

We can re-arrange things to allow more sharing:

X2=(X+Z)2(XZ)2Z2=((X+Z)2(XZ)2)((X+Z)2+A24((X+Z)2(XZ)2))\begin{aligned} &X_2 = (X + Z)^2(X - Z)^2 \cr &Z_2 = ((X + Z)^2 - (X - Z)^2)\left( (X + Z)^2 + \frac{A - 2}{4}((X + Z)^2 - (X - Z)^2) \right) \end{aligned}

If you note that:

(X+Z)2(XZ)2=4XZ(X + Z)^2 - (X - Z)^2 = 4XZ

The correctness of this formula becomes relatively clear.

This adjusted formula takes:

2M+2S+C2 \bold{M} + 2 \bold{S} + \bold{C}

Addition Formula

In can be shown that given PP and QQ, we have:

x(P+Q)x(QP)=(x(P)x(Q)1)2(x(Q)x(P))2\bold{x}(P + Q)\bold{x}(Q - P) = \frac{ (\bold{x}(P)\bold{x}(Q) - 1)^2 }{ (\bold{x}(Q) - \bold{x}(P))^2 }

(This is a monstrous PITA, see 1.)

In projective coordinates [XP,ZP], [XQ,ZQ][X_P, Z_P],\ [X_Q, Z_Q], we get:

x(P+Q)XQPZQP=(XPXQZPZQ)2(XQZPXPZQ)2\bold{x}(P + Q) \frac{X_{Q - P}}{Z_{Q - P}} = \frac{(X_P X_Q - Z_P Z_Q)^2}{(X_Q Z_P - X_P Z_Q)^2}

This gives us the following formulas for addition:

XP+Q=ZQP(XPXQZPZQ)2ZP+Q=XQP(XQZPXPZQ)2\begin{aligned} &X_{P + Q} = Z_{Q - P}(X_P X_Q - Z_P Z_Q)^2 \cr &Z_{P + Q} = X_{Q - P}(X_Q Z_P - X_P Z_Q)^2 \end{aligned}

Unlike with doubling, we need to have calculated PQP - Q prior. In practice, this constraint is easily accomodated.

This formula requires:

6M+2S6 \bold{M} + 2 \bold{S}

To simplify it, first note that because of the properties of projective coordinates, multiplying both XX and ZZ by 44 gives us the same point:

XP+Q=4ZQP(XPXQZPZQ)2ZP+Q=4XQP(XQZPXPZQ)2\begin{aligned} &X_{P + Q} = 4Z_{Q - P}(X_P X_Q - Z_P Z_Q)^2 \cr &Z_{P + Q} = 4X_{Q - P}(X_Q Z_P - X_P Z_Q)^2 \end{aligned}

A bit of toil gives us the following formula:

XP+Q=ZQP((XPZP)(XQ+ZQ)+(XP+ZP)(XQZQ))2ZP+Q=XQP((XPZP)(XQ+ZQ)(XP+ZP)(XQZQ))2\begin{aligned} &X_{P + Q} = Z_{Q - P}( (X_P - Z_P)(X_Q + Z_Q) + (X_P + Z_P)(X_Q - Z_Q) )^2 \cr &Z_{P + Q} = X_{Q - P}( (X_P - Z_P)(X_Q + Z_Q) - (X_P + Z_P)(X_Q - Z_Q) )^2 \cr \end{aligned}

This gives us an improved:

4M+2S4 \bold{M} + 2 \bold{S}

In practice, we'll always have ZQPZ_{Q - P} as 11, and XQPX_{Q - P} as a small constant, giving us:

2M+2S+C2 \bold{M} + 2 \bold{S} + \bold{C}

Combined Formula

In practice, you have (X2,Z2)(X_2, Z_2) and (X3,Z3)(X_3, Z_3), and calculate (X4,Z4)(X_4, Z_4) as well as (X5,Z5)(X_5, Z_5) together, giving you the following combined formula:

/

(See: 1)

Ladder

One simple way to calculate kPkP is as follows. Iterate over the bits of kk from most to least significant. If a bit b=0b = 0, set:

P2PP \leftarrow 2P

If b=1b = 1, then set:

P2P+PP \leftarrow 2P + P

Our addition formula works well when we have PnP_n and Pn+1P_{n + 1}, and want to calculate P2n+1P_{2n + 1}, since the difference is P1=PP_1 = P, a point we already know.

At each iteration, as we rise up to PkP_k, our final value, we want to keep this property, and keep the successive values around.

This, when b=0b = 0, we do:

(Pn,Pn+1)(P2n,P2n+1)(P_n, P_{n + 1}) \mapsto (P_{2n}, P_{2n + 1})

The first value comes from point doubling PnP_n, and the second from differential point addition, using the fact that the difference is P1P_1.

When b=1b = 1, we want to do:

(Pn,Pn+1)(P2n+1,P2n+2)(P_n, P_{n + 1}) \mapsto (P_{2n + 1}, P_{2n + 2})

We can calculate the first value through point addition, and the second through point doubling.

In fact, if we define these two functions as step0\text{step}_0, and step1\text{step}_1 respectively, we notice that step1\text{step}_1 comes from swapping the input, and then the output. With:

swap(P,Q):=(Q,P)\text{swap}(P, Q) := (Q, P)

We have:

step1=swap;step0;swap\text{step}_1 = \text{swap};\text{step}_0;\text{swap}

Thus, an algorithm for doing our ladder would be:

for b in bits:
  if b == 1:
    swap(P, Q)
  (P, Q) = step0(P, Q)
  if b == 1:
    swap(P, Q)
return P

We can make this "constant-time", by replacing our branching with a conditional swap:

for b in bits:
  cswap(b, P, Q)
  (P, Q) = step0(P, Q)
  cswap(b, P, Q)
return P

If we have two conditional swaps chained together

cswap(bits[i], P, Q)
cswap(bits[i - 1], P, Q)

Which happens between iterations, notice that this is effectively:

cswap(bits[i] ^ bits[i - 1], P, Q)

because of this, we can effectively do:

cswap(bits[N - 1], P, Q)
step0(P, Q)
cswap(bits[N - 1] ^ bits[N - 1], P, Q)
step0(P, Q)
...
cswap(bits[1] ^ bits[0], P, Q)
step0(P, Q)
cswap(bits[0], P, Q)

Noting that 0 ^ bits[N - 1] = bits[N - 1], we can effectively do:

swap := 0
for b in bits:
  swap ^= b
  cswap(swap, P, Q)
  swap = b
  step0(P, Q)
cswap(swap, P, Q)
return P

This allows us to efficiently compute whether or not we need to swap.

This is the gist of things, see the summary up top for more useable pseudo-code.

References

1

  1. [1] Bernstein, Daniel J., Lange, Tanja - Montgomery Curves and the Montgomery Ladder (2017)