;;; Curve25519: the elliptic curve y^2 = x^3 + 486662 x^2 + x over the
;;; prime field Z/(2^255 - 19)Z
;;;
;;; X25519: the Diffie-Hellman function based on x-coordinate scalar
;;; multiplication in Curve25519.
;;;
;;; DO NOT USE THIS CODE IN PRACTICE! It is for experiments and for
;;; validating the results of other implementations. It does not
;;; defend against side channel attacks, and I am not totally sure it
;;; is correct.
;;;
;;; Public domain, or if you can't handle that then cc0
;;; .
(define (modexp a e n)
(if (zero? e)
1
(let loop ((r 1) (b (modulo a n)) (f e))
;; r b^f = a^e (mod n)
(let ((r (if (even? f) r (modulo (* r b) n)))
(f (quotient f 2)))
(if (zero? f)
r
(loop r (modulo (* b b) n) f))))))
(define p25519 (- (expt 2 255) 19))
(define (gf25519:zero? a) (zero? a))
(define (gf25519:one? a) (= a 1))
(define (gf25519:+ a b) (modulo (+ a b) p25519))
(define (gf25519:- a b) (modulo (- a b) p25519))
(define (gf25519:* a b) (modulo (* a b) p25519))
(define (gf25519:/ a b) (gf25519:* a (gf25519:recip b)))
(define (gf25519:negate a) (gf25519:- 0 a))
(define (gf25519:recip a) (gf25519:expt a (- p25519 2)))
(define (gf25519:double a) (gf25519:+ a a))
(define (gf25519:expt a n) (modexp a n p25519))
(define (gf25519:square a) (gf25519:* a a))
(define (gf25519:nonsquare? a)
(= (gf25519:negate 1) (gf25519:expt a (/ (- p25519 1) 2))))
(assert (not (gf25519:nonsquare? 9)))
(assert (= 5 (modulo p25519 8)))
(assert (= 0 (modulo (+ p25519 3) 8)))
(define gf25519:i (gf25519:expt 2 (/ (- p25519 1) 4)))
(define (gf25519:sqrt a)
(assert (not (gf25519:nonsquare? a)))
(let ((r (gf25519:expt a (/ (+ p25519 3) 8))))
(if (= (gf25519:square r) (gf25519:negate a))
(gf25519:* gf25519:i r)
r)))
(assert
(= (gf25519:sqrt
(gf25519:+ (gf25519:expt 9 3)
(gf25519:+ (gf25519:* 486662 (gf25519:square 9))
9)))
#x20ae19a1b8a086b4e01edd2c7748d14c923d4d7e6d7c61b229e9c5a27eced3d9))
(define (make-point x y) (cons x y))
(define (point-x p) (car p))
(define (point-y p) (cdr p))
(define infinity 'INFINITY)
(define (infinity? p) (eq? p infinity))
(define (curve25519? p)
(or (infinity? p)
(gf25519:zero?
(gf25519:-
(gf25519:square (point-y p))
(gf25519:+
(gf25519:expt (point-x p) 3)
(gf25519:+
(gf25519:* 486662 (gf25519:square (point-x p)))
(point-x p)))))))
(define (curve25519:+ p q)
(assert (curve25519? p))
(assert (curve25519? q))
(cond ((and (infinity? p) (infinity? q)) infinity)
((infinity? p) q)
((infinity? q) p)
(else
(let ((px (point-x p))
(py (point-y p))
(qx (point-x q))
(qy (point-y q)))
(assert (= px (modulo px p25519)))
(assert (= qx (modulo qx p25519)))
(assert (= py (modulo py p25519)))
(assert (= qy (modulo qy p25519)))
(cond ((= py (gf25519:negate qy)) infinity)
((and (= px qx) (= py qy))
(let ((x px) (y py))
(let* ((l
(gf25519:/
(gf25519:+
(gf25519:* 3 (gf25519:square x))
(gf25519:+
(gf25519:double (gf25519:* 486662 x))
1))
(gf25519:double y)))
(x*
(gf25519:- (gf25519:- (gf25519:square l) 486662)
(gf25519:double x))))
(make-point
x*
(gf25519:- (gf25519:* l (gf25519:- x x*)) y)))))
((= px qx)
(assert (not (= py (gf25519:negate qy))))
(error "Not on curve25519:" p q))
(else
(let* ((l (gf25519:/ (gf25519:- qy py)
(gf25519:- qx px)))
(x*
(gf25519:-
(gf25519:-
(gf25519:- (gf25519:square l) 486662)
px)
qx)))
(make-point x*
(gf25519:- (gf25519:* l (gf25519:- px x*))
py)))))))))
;;; Naive scalar multiplication using the horrible addition formula
;;; with add-and-double steps.
(define (curve25519:* n p)
(let loop ((r infinity) (q p) (m n))
;; r + m q = n p
(let ((r (if (even? m) r (curve25519:+ r q)))
(m (quotient m 2)))
(if (zero? m)
r
(loop r (curve25519:+ q q) m)))))
;;; Montgomery ladder as written in the Explicit Formulas Database
;;; and
;;; .
(define (curve25519:ladder-efd n x)
(define (cswap b x y)
(let ((d (gf25519:* b (gf25519:- x y))))
(values (gf25519:- x d)
(gf25519:+ y d))))
(let ((a24 (/ (- 486662 2) 4)))
(let loop ((t 254) (x1 x) (x2 1) (z2 0) (x3 x) (z3 1))
(let ((bit (extract-bit t n)))
(receive (x2 x3) (cswap bit x2 x3)
(receive (z2 z3) (cswap bit z2 z3)
(let* ((a (gf25519:+ x2 z2))
(aa (gf25519:square a))
(b (gf25519:- x2 z2))
(bb (gf25519:square b))
(e (gf25519:- aa bb))
(c (gf25519:+ x3 z3))
(d (gf25519:- x3 z3))
(da (gf25519:* d a))
(cb (gf25519:* c b))
(x3 (gf25519:square (gf25519:+ da cb)))
(z3 (gf25519:* x1 (gf25519:square (gf25519:- da cb))))
(x2 (gf25519:* aa bb))
(z2 (gf25519:* e (gf25519:+ aa (gf25519:* a24 e)))))
(receive (x2 x3) (cswap bit x2 x3)
(receive (z2 z3) (cswap bit z2 z3)
(if (zero? t)
(gf25519:* x2 (gf25519:expt z2 (- p25519 2)))
(loop (- t 1) x1 x2 z2 x3 z3)))))))))))
;;; Montgomery ladder as written in NaCl's crypto_scalarmult_curve25519
;;; reference implementation.
(define (curve25519:ladder-nacl n x)
(define (cswap b x y)
(let ((d (gf25519:* b (gf25519:- x y))))
(values (gf25519:- x d)
(gf25519:+ y d))))
(let ((a24 (/ (- 486662 2) 4)))
(let loop ((t 254) (xm1 x) (zm1 1) (xm 1) (zm 0))
(let ((bit (extract-bit t n)))
(receive (xmb xm1b) (cswap bit xm xm1)
(receive (zmb zm1b) (cswap bit zm zm1)
(let* ((a0x (gf25519:+ xmb zmb))
(a0z (gf25519:- xmb zmb))
(a1x (gf25519:+ xm1b zm1b))
(a1z (gf25519:- xm1b zm1b))
(b0x (gf25519:square a0x))
(b0z (gf25519:square a0z))
(b1x (gf25519:* a1x a0z))
(b1z (gf25519:* a1z a0x))
(c1x (gf25519:+ b1x b1z))
(c1z (gf25519:- b1x b1z))
(rx (gf25519:square c1z))
(sx (gf25519:- b0x b0z))
(tx (gf25519:* a24 sx))
(ux (gf25519:+ tx b0x))
(xnb (gf25519:* b0x b0z))
(znb (gf25519:* sx ux))
(xn1b (gf25519:square c1x))
(zn1b (gf25519:* rx x)))
(receive (xm xm1) (cswap bit xnb xn1b)
(receive (zm zm1) (cswap bit znb zn1b)
(if (zero? t)
(gf25519:* xm (gf25519:recip zm))
(loop (- t 1) xm1 zm1 xm zm)))))))))))
(define (mont25519:double x z)
(let ((a24 (/ (- 486662 2) 4)))
(let ((x+z^2 (gf25519:square (gf25519:+ x z)))
(x-z^2 (gf25519:square (gf25519:- x z))))
(values
(gf25519:* x+z^2 x-z^2)
(gf25519:*
(gf25519:- x+z^2 x-z^2)
(gf25519:+ x+z^2
(gf25519:* a24
(gf25519:- x+z^2 x-z^2))))))))
(define (mont25519:diff+ qx qz px pz q-px q-pz)
(let ((qx-qz (gf25519:- qx qz))
(qx+qz (gf25519:+ qx qz))
(px+pz (gf25519:+ px pz))
(px-pz (gf25519:- px pz)))
(let ((qx-qz*px+pz (gf25519:* qx-qz px+pz))
(qx+qz*px-pz (gf25519:* qx+qz px-pz)))
(values
(gf25519:*
q-pz
(gf25519:square (gf25519:+ qx-qz*px+pz qx+qz*px-pz)))
(gf25519:*
q-px
(gf25519:square (gf25519:- qx-qz*px+pz qx+qz*px-pz)))))))
(define (curve25519-decompress x sign)
(make-point x
(gf25519:* sign
(gf25519:sqrt
(gf25519:+
(gf25519:expt x 3)
(gf25519:+
(gf25519:* 486662 (gf25519:square x))
x))))))
(assert
(equal?
(curve25519-decompress 9 1)
(make-point
9
#x20ae19a1b8a086b4e01edd2c7748d14c923d4d7e6d7c61b229e9c5a27eced3d9)))
(define (le256-decode v)
(assert (= 32 (vector-length v)))
(let loop ((i 0) (x 0))
(if (< i 32)
(loop (+ i 1)
(+ x (* (vector-ref v i) (expt 2 (* 8 i)))))
x)))
(define (le256-encode x)
(let ((v (make-vector 32)))
(let loop ((i 0) (x x))
(if (< i 32)
(begin
(vector-set! v i (modulo x 256))
(loop (+ i 1) (quotient x 256)))))
v))
(define (x25519-clampc v)
(assert (= 32 (vector-length v)))
(define (vector-modify! v i f)
(vector-set! v i (f (vector-ref v i))))
(let ((v (let ((v0 (make-vector 32)))
(do ((i 0 (+ i 1))) ((>= i 32))
(vector-set! v0 i (vector-ref v i)))
v0)))
(vector-modify! v 0 (lambda (a0) (- a0 (modulo a0 8))))
(vector-modify! v 31 (lambda (a31) (+ 64 (modulo a31 64))))
v))
(define x25519-base
(vector 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0))
(define (x25519 n p)
(le256-encode
(point-x
(curve25519:* (le256-decode (x25519-clampc n))
(curve25519-decompress (le256-decode p) 1)))))
(define (x25519-nacl n p)
(le256-encode
(curve25519:ladder-nacl (le256-decode (x25519-clampc n))
(le256-decode p))))
(define (x25519-efd n p)
(le256-encode
(curve25519:ladder-efd (le256-decode (x25519-clampc n))
(le256-decode p))))
(let ((secret
'#(#x77 #x07 #x6d #x0a #x73 #x18 #xa5 #x7d
#x3c #x16 #xc1 #x72 #x51 #xb2 #x66 #x45
#xdf #x4c #x2f #x87 #xeb #xc0 #x99 #x2a
#xb1 #x77 #xfb #xa5 #x1d #xb9 #x2c #x2a))
(public
'#(#x85 #x20 #xf0 #x09 #x89 #x30 #xa7 #x54
#x74 #x8b #x7d #xdc #xb4 #x3e #xf7 #x5a
#x0d #xbf #x3a #x0d #x26 #x38 #x1a #xf4
#xeb #xa4 #xa9 #x8e #xaa #x9b #x4e #x6a)))
(assert (equal? public (x25519 secret x25519-base)))
(assert (equal? public (x25519-nacl secret x25519-base)))
(assert (equal? public (x25519-efd secret x25519-base))))