Source file handshake_crypto13.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
open Core
let cdiv (x : int) (y : int) =
if x > 0 && y > 0 then (x + y - 1) / y
else if x < 0 && y < 0 then (x + y + 1) / y
else x / y
let left_pad_dh group msg =
let bytes = cdiv (Mirage_crypto_pk.Dh.modulus_size group) 8 in
let padding = String.make (bytes - String.length msg) '\x00' in
padding ^ msg
let not_all_zero r =
let* str = r in
try
for i = 0 to String.length str - 1 do
if String.unsafe_get str i != '\x00' then raise_notrace Not_found;
done;
Error (`Fatal (`Handshake (`BadDH "all zero")))
with Not_found -> Ok str
let dh_shared secret share =
let map_ecdh_error =
Result.map_error (fun e -> `Fatal (`Handshake (`BadECDH e)))
in
let open Mirage_crypto_ec in
not_all_zero
(match secret with
| `Finite_field secret ->
let group = secret.Mirage_crypto_pk.Dh.group in
let bits = Mirage_crypto_pk.Dh.modulus_size group in
let* () =
guard (String.length share = cdiv bits 8)
(`Fatal (`Handshake (`BadDH "truncated")))
in
let* shared =
Option.to_result
~none:(`Fatal (`Handshake (`BadDH "invalid FF")))
(Mirage_crypto_pk.Dh.shared secret share)
in
Ok (left_pad_dh group shared)
| `P256 priv -> map_ecdh_error (P256.Dh.key_exchange priv share)
| `P384 priv -> map_ecdh_error (P384.Dh.key_exchange priv share)
| `P521 priv -> map_ecdh_error (P521.Dh.key_exchange priv share)
| `X25519 priv -> map_ecdh_error (X25519.key_exchange priv share))
let dh_gen_key group =
match Core.group_to_impl group with
| `Finite_field mc_group ->
let sec, shared = Mirage_crypto_pk.Dh.gen_key mc_group in
`Finite_field sec, left_pad_dh mc_group shared
| `P256 ->
let secret, shared = Mirage_crypto_ec.P256.Dh.gen_key () in
`P256 secret, shared
| `P384 ->
let secret, shared = Mirage_crypto_ec.P384.Dh.gen_key () in
`P384 secret, shared
| `P521 ->
let secret, shared = Mirage_crypto_ec.P521.Dh.gen_key () in
`P521 secret, shared
| `X25519 ->
let secret, shared = Mirage_crypto_ec.X25519.gen_key () in
`X25519 secret, shared
let trace tag cs = Tracing.cs ~tag:("crypto " ^ tag) cs
let pp_hash_k_n ciphersuite =
let open Ciphersuite in
let pp = privprot13 ciphersuite
and hash = hash13 ciphersuite
in
let k, n = kn_13 pp in
(pp, hash, k, n)
let hkdflabel label context length =
let lbl = "tls13 " ^ label in
let len_llen = Bytes.create 3 in
Bytes.set_uint16_be len_llen 0 length;
Bytes.set_uint8 len_llen 2 (String.length lbl);
let clen = String.make 1 (Char.unsafe_chr (String.length context)) in
let lbl = String.concat ""
[ Bytes.unsafe_to_string len_llen ;
lbl ;
clen ;
context ]
in
trace "hkdflabel" lbl ;
lbl
let derive_secret_no_hash hash prk ?length ?(ctx = "") label =
let length = match length with
| None ->
let module H = (val Digestif.module_of_hash' hash) in
H.digest_size
| Some x -> x
in
let info = hkdflabel label ctx length in
trace "prk" prk ;
let key = Hkdf.expand ~hash ~prk ~info length in
trace ("derive_secret: " ^ label) key ;
key
let derive_secret t label log =
let module H = (val Digestif.module_of_hash' t.State.hash) in
let ctx = H.(to_raw_string (digest_string log)) in
trace "derive secret ctx" ctx ;
derive_secret_no_hash t.State.hash t.State.secret ~ctx label
let empty cipher = {
State.secret = "" ;
cipher ;
hash = Ciphersuite.hash13 cipher
}
let derive t secret_ikm =
let salt =
if String.equal t.State.secret "" then
""
else
derive_secret t "derived" ""
in
trace "derive: secret_ikm" secret_ikm ;
trace "derive: salt" salt ;
let secret = Hkdf.extract ~hash:t.State.hash ~salt secret_ikm in
trace "derive (extracted secret)" secret ;
{ t with State.secret }
let traffic_key cipher prk =
let _, hash, key_len, iv_len = pp_hash_k_n cipher in
let key_info = hkdflabel "key" "" key_len in
let key = Hkdf.expand ~hash ~prk ~info:key_info key_len in
let iv_info = hkdflabel "iv" "" iv_len in
let iv = Hkdf.expand ~hash ~prk ~info:iv_info iv_len in
(key, iv)
let ctx t label secret =
let secret, nonce = traffic_key t.State.cipher secret in
trace (label ^ " secret") secret ;
trace (label ^ " nonce") nonce ;
let pp = Ciphersuite.privprot13 t.State.cipher in
{ State.sequence = 0L ; cipher_st = Crypto.Ciphers.get_aead_cipher ~secret ~nonce pp }
let early_traffic t log =
let secret = derive_secret t "c e traffic" log in
(secret, ctx t "client early traffic" secret)
let hs_ctx t log =
Tracing.cs ~tag:"hs ctx with sec" t.State.secret ;
Tracing.cs ~tag:"log is" log ;
let server_handshake_traffic_secret = derive_secret t "s hs traffic" log
and client_handshake_traffic_secret = derive_secret t "c hs traffic" log
in
(server_handshake_traffic_secret,
ctx t "server handshake traffic" server_handshake_traffic_secret,
client_handshake_traffic_secret,
ctx t "client handshake traffic" client_handshake_traffic_secret)
let app_ctx t log =
let server_application_traffic_secret = derive_secret t "s ap traffic" log
and client_application_traffic_secret = derive_secret t "c ap traffic" log
in
(server_application_traffic_secret,
ctx t "server application traffic" server_application_traffic_secret,
client_application_traffic_secret,
ctx t "client application traffic" client_application_traffic_secret)
let app_secret_n_1 t app_secret =
let secret = derive_secret_no_hash t.State.hash app_secret "traffic upd" in
secret, ctx t "traffic update" secret
let exporter t log = derive_secret t "exp master" log
let resumption t log = derive_secret t "res master" log
let res_secret hash secret nonce =
derive_secret_no_hash hash secret ~ctx:nonce "resumption"
let finished hash secret data =
let module H = (val Digestif.module_of_hash' hash) in
let key = derive_secret_no_hash hash secret "finished" in
H.(to_raw_string (hmac_string ~key (to_raw_string (digest_string data))))