SPAKE2/SPAKE2+ password-authenticated key exchange for OCaml
1# spake2
2
3SPAKE2 and SPAKE2+ Password-Authenticated Key Exchange for OCaml.
4
5## Overview
6
7This library implements the SPAKE2 (RFC 9382) and SPAKE2+ protocols for
8password-authenticated key exchange. These protocols allow two parties who
9share a password to derive a strong shared secret key without revealing the
10password to eavesdroppers or allowing offline dictionary attacks.
11
12- **SPAKE2**: Both parties derive the same values from the password
13- **SPAKE2+**: Augmented PAKE where the server stores only a verifier, not password-equivalent data
14
15## Security Notice
16
17This implementation uses mirage-crypto-ec for P-256 elliptic curve operations,
18which provides **constant-time** arithmetic via code generated by fiat-crypto.
19Scalar operations use Zarith but only for protocol-level math (not secret-dependent
20branching). The P-256 point operations are constant-time.
21
22## Installation
23
24Install with opam:
25
26<!-- $MDX skip -->
27```sh
28$ opam install spake2
29```
30
31If opam cannot find the package, it may not yet be released in the public
32`opam-repository`. Add the overlay repository, then install it:
33
34<!-- $MDX skip -->
35```sh
36$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
37$ opam update
38$ opam install spake2
39```
40
41## Usage
42
43### SPAKE2
44
45```ocaml
46let () = Crypto_rng_unix.use_default ()
47
48let password = "secret"
49
50(* Party A sends msg_a to B, receives msg_b from B. *)
51let state_a, msg_a = Spake2.init ~password `A
52
53(* Party B sends msg_b to A, receives msg_a from A. *)
54let state_b, msg_b = Spake2.init ~password `B
55
56let () =
57 match
58 ( Spake2.finish ~context:"myapp" state_a msg_b,
59 Spake2.finish ~context:"myapp" state_b msg_a )
60 with
61 | Ok key_a, Ok key_b -> assert (String.equal key_a key_b)
62 | _ -> failwith "finish failed"
63```
64
65### SPAKE2+
66
67```ocaml
68let password = "secret"
69
70(* Setup: derive verifier data from password. *)
71let salt = Spake2.Plus.generate_salt ()
72let iterations = 1000
73let w0, w1 = Spake2.Plus.derive_w ~password ~salt ~iterations
74let l = Spake2.Plus.compute_l ~w1
75
76(* Protocol run: exchange pa and pb between prover and verifier. *)
77let context = "myapp"
78let prover_state, pa = Spake2.Plus.prover_init ~w0 ~w1 ~context
79let verifier_state, pb = Spake2.Plus.verifier_init ~w0 ~l ~context
80
81let () =
82 match
83 ( Spake2.Plus.prover_finish prover_state pb,
84 Spake2.Plus.verifier_finish verifier_state pa )
85 with
86 | Ok (ke_prover, _ca, _), Ok (ke_verifier, _cb, _) ->
87 assert (String.equal ke_prover ke_verifier)
88 | _ -> failwith "spake2+ exchange failed"
89```
90
91## API
92
93### SPAKE2
94
95- `Spake2.init ~password role` - Initialize protocol for `A or `B
96- `Spake2.finish ?context ?id_a ?id_b state peer_msg` - Complete protocol
97
98### SPAKE2+
99
100- `Spake2.Plus.derive_w ~password ~salt ~iterations` - Derive w0, w1 from password
101- `Spake2.Plus.compute_l ~w1` - Compute L for server storage
102- `Spake2.Plus.prover_init ~w0 ~w1 ~context` - Initialize as prover (client)
103- `Spake2.Plus.verifier_init ~w0 ~l ~context` - Initialize as verifier (server)
104- `Spake2.Plus.prover_finish state pb` - Complete as prover
105- `Spake2.Plus.verifier_finish state pa` - Complete as verifier
106
107### P-256 Curve
108
109- `Spake2.P256.add p q` - Point addition
110- `Spake2.P256.negate p` - Point negation
111- `Spake2.P256.to_bytes p` - Encode point (SEC1 uncompressed)
112- `Spake2.P256.of_bytes s` - Decode and validate point
113
114## References
115
116- [RFC 9382 - SPAKE2](https://www.rfc-editor.org/rfc/rfc9382.html)
117- [SPAKE2+ Draft](https://datatracker.ietf.org/doc/draft-bar-cfrg-spake2plus/)
118
119## Licence
120
121MIT License. See [LICENSE.md](LICENSE.md) for details.