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 forA orBSpake2.finish ?context ?id_a ?id_b state peer_msg- Complete protocol
SPAKE2+#
Spake2.Plus.derive_w ~password ~salt ~iterations- Derive w0, w1 from passwordSpake2.Plus.compute_l ~w1- Compute L for server storageSpake2.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 proverSpake2.Plus.verifier_finish state pa- Complete as verifier
P-256 Curve#
Spake2.P256.add p q- Point additionSpake2.P256.negate p- Point negationSpake2.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.