mte/unikernel/duniverse/ocaml-tls/lib/handshake_crypto13.ml
2025-11-11 02:07:51 +01:00

183 lines
6.1 KiB
OCaml

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 =
(* RFC 8556, Section 7.4.1 - we need zero-padding on the left *)
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* () =
(* truncated share, better reject this *)
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 =
(* RFC 8556, Section 4.2.8.1 - we need zero-padding on the left *)
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))))