SPAKE2/SPAKE2+ password-authenticated key exchange for OCaml
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

OCaml 90.4%
Dune 2.9%
Other 6.7%
43 1 0

Clone this repository

https://tangled.org/gazagnaire.org/ocaml-spake2 https://tangled.org/did:plc:jhift2vwcxhou52p3sewcrpx/ocaml-spake2
git@git.recoil.org:gazagnaire.org/ocaml-spake2 git@git.recoil.org:did:plc:jhift2vwcxhou52p3sewcrpx/ocaml-spake2

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

spake2#

SPAKE2 and SPAKE2+ Password-Authenticated Key Exchange for OCaml.

Overview#

This library implements the SPAKE2 (RFC 9382) and SPAKE2+ protocols for password-authenticated key exchange. These protocols allow two parties who share a password to derive a strong shared secret key without revealing the password to eavesdroppers or allowing offline dictionary attacks.

  • SPAKE2: Both parties derive the same values from the password
  • SPAKE2+: Augmented PAKE where the server stores only a verifier, not password-equivalent data

Security Notice#

This implementation uses mirage-crypto-ec for P-256 elliptic curve operations, which provides constant-time arithmetic via code generated by fiat-crypto. Scalar operations use Zarith but only for protocol-level math (not secret-dependent branching). The P-256 point operations are constant-time.

Installation#

Install with opam:

$ opam install spake2

If opam cannot find the package, it may not yet be released in the public opam-repository. Add the overlay repository, then install it:

$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install spake2

Usage#

SPAKE2#

let () = Crypto_rng_unix.use_default ()

let password = "secret"

(* Party A sends msg_a to B, receives msg_b from B. *)
let state_a, msg_a = Spake2.init ~password `A

(* Party B sends msg_b to A, receives msg_a from A. *)
let state_b, msg_b = Spake2.init ~password `B

let () =
  match
    ( Spake2.finish ~context:"myapp" state_a msg_b,
      Spake2.finish ~context:"myapp" state_b msg_a )
  with
  | Ok key_a, Ok key_b -> assert (String.equal key_a key_b)
  | _ -> failwith "finish failed"

SPAKE2+#

let password = "secret"

(* Setup: derive verifier data from password. *)
let salt = Spake2.Plus.generate_salt ()
let iterations = 1000
let w0, w1 = Spake2.Plus.derive_w ~password ~salt ~iterations
let l = Spake2.Plus.compute_l ~w1

(* Protocol run: exchange pa and pb between prover and verifier. *)
let context = "myapp"
let prover_state, pa = Spake2.Plus.prover_init ~w0 ~w1 ~context
let verifier_state, pb = Spake2.Plus.verifier_init ~w0 ~l ~context

let () =
  match
    ( Spake2.Plus.prover_finish prover_state pb,
      Spake2.Plus.verifier_finish verifier_state pa )
  with
  | Ok (ke_prover, _ca, _), Ok (ke_verifier, _cb, _) ->
      assert (String.equal ke_prover ke_verifier)
  | _ -> failwith "spake2+ exchange failed"

API#

SPAKE2#

  • Spake2.init ~password role - Initialize protocol for A or B
  • Spake2.finish ?context ?id_a ?id_b state peer_msg - Complete protocol

SPAKE2+#

  • Spake2.Plus.derive_w ~password ~salt ~iterations - Derive w0, w1 from password
  • Spake2.Plus.compute_l ~w1 - Compute L for server storage
  • Spake2.Plus.prover_init ~w0 ~w1 ~context - Initialize as prover (client)
  • Spake2.Plus.verifier_init ~w0 ~l ~context - Initialize as verifier (server)
  • Spake2.Plus.prover_finish state pb - Complete as prover
  • Spake2.Plus.verifier_finish state pa - Complete as verifier

P-256 Curve#

  • Spake2.P256.add p q - Point addition
  • Spake2.P256.negate p - Point negation
  • Spake2.P256.to_bytes p - Encode point (SEC1 uncompressed)
  • Spake2.P256.of_bytes s - Decode and validate point

References#

Licence#

MIT License. See LICENSE.md for details.