mte/unikernel/duniverse/ocaml-tls/lib/reader.ml

816 lines
28 KiB
OCaml
Raw Normal View History

2025-11-11 02:07:51 +01:00
open Packet
open Core
type error =
| TrailingBytes of string
| WrongLength of string
| Unknown of string
exception Reader_error of error
let raise_unknown msg = raise (Reader_error (Unknown msg))
and raise_wrong_length msg = raise (Reader_error (WrongLength msg))
and raise_trailing_bytes msg = raise (Reader_error (TrailingBytes msg))
let shift str amount = String.sub str amount (String.length str - amount)
let catch f x =
try Ok (f x) with
| Reader_error TrailingBytes msg -> Error (`Decode ("trailing bytes: " ^ msg))
| Reader_error WrongLength msg -> Error (`Decode ("wrong length: " ^ msg))
| Reader_error Unknown msg -> Error (`Decode msg)
| Invalid_argument msg -> Error (`Decode msg)
let parse_version_int buf =
let major = String.get_uint8 buf 0 in
let minor = String.get_uint8 buf 1 in
(major, minor)
let parse_version_exn buf =
let version = parse_version_int buf in
match tls_version_of_pair version with
| Some x -> x
| None -> raise_unknown "version"
let parse_any_version_opt buf =
let version = parse_version_int buf in
tls_any_version_of_pair version, shift buf 2
let parse_any_version_exn buf =
match parse_any_version_opt buf with
| Some x, _ -> x
| None, _ -> raise_unknown "version"
let parse_version = catch parse_version_exn
let parse_any_version = catch parse_any_version_exn
let parse_record buf =
if String.length buf < 5 then
Ok (`Fragment buf)
else
let typ = String.get_uint8 buf 0
and version = parse_version_int (shift buf 1)
in
match String.get_uint16_be buf 3 with
| x when x > (1 lsl 14 + 2048) ->
(* 2 ^ 14 + 2048 for TLSCiphertext
2 ^ 14 + 1024 for TLSCompressed
2 ^ 14 for TLSPlaintext *)
Error (`Record_overflow x)
| x when 5 + x > String.length buf -> Ok (`Fragment buf)
| x ->
match
tls_any_version_of_pair version,
int_to_content_type typ
with
| None, _ -> Error (`Protocol_version (`Unknown_record version))
| _, None -> Error (`Unexpected (`Content_type typ))
| Some version, Some content_type ->
let payload, rest = split_str ~start:5 buf x in
Ok (`Record (({ content_type ; version }, payload), rest))
let validate_alert (lvl, typ) =
let open Packet in
match lvl, typ with
(* from RFC, find out which ones must be always FATAL
and report if this does not meet the expectations *)
| WARNING, (UNEXPECTED_MESSAGE | BAD_RECORD_MAC | RECORD_OVERFLOW
| HANDSHAKE_FAILURE | BAD_CERTIFICATE | DECODE_ERROR
| PROTOCOL_VERSION | INAPPROPRIATE_FALLBACK | MISSING_EXTENSION
| UNSUPPORTED_EXTENSION | UNRECOGNIZED_NAME |
NO_APPLICATION_PROTOCOL as x) ->
raise_unknown (alert_type_to_string x ^ " must always be fatal")
(* those are always warnings *)
| FATAL, (USER_CANCELED | NO_RENEGOTIATION as x) ->
raise_unknown (alert_type_to_string x ^ " must always be a warning")
| lvl, typ -> (lvl, typ)
let parse_alert = catch @@ fun buf ->
if String.length buf <> 2 then
raise_trailing_bytes "after alert"
else
let level = String.get_uint8 buf 0 in
let typ = String.get_uint8 buf 1 in
match int_to_alert_level level, int_to_alert_type typ with
| (Some lvl, msg) -> validate_alert (lvl, msg)
| _ -> raise_unknown @@ "alert level " ^ string_of_int level
let parse_change_cipher_spec buf =
match String.length buf, String.get_uint8 buf 0 with
| 1, 1 -> Ok ()
| _ -> Error (`Decode "bad change cipher spec message")
let rec parse_count_list parsef buf acc = function
| 0 -> (List.rev acc, buf)
| n ->
match parsef buf with
| Some elem, buf' -> parse_count_list parsef buf' (elem :: acc) (pred n)
| None , buf' -> parse_count_list parsef buf' acc (pred n)
let rec parse_list parsef buf acc =
match String.length buf with
| 0 -> List.rev acc
| _ ->
match parsef buf with
| Some elem, buf' -> parse_list parsef buf' (elem :: acc)
| None , buf' -> parse_list parsef buf' acc
let parse_compression_method buf =
let cm = String.get_uint8 buf 0 in
(int_to_compression_method cm, shift buf 1)
let parse_compression_methods buf =
let count = String.get_uint8 buf 0 in
parse_count_list parse_compression_method (shift buf 1) [] count
let parse_any_ciphersuite buf =
let typ = String.get_uint16_be buf 0 in
(int_to_any_ciphersuite typ, shift buf 2)
let parse_any_ciphersuites buf =
let count = String.get_uint16_be buf 0 in
if count mod 2 <> 0 then
raise_wrong_length "ciphersuite list"
else
parse_count_list parse_any_ciphersuite (shift buf 2) [] (count / 2)
let parse_ciphersuite buf =
match parse_any_ciphersuite buf with
| None , buf' -> (None, buf')
| Some cs, buf' -> match Ciphersuite.any_ciphersuite_to_ciphersuite cs with
| None -> (None, buf')
| Some cs' -> (Some cs', buf')
let parse_hostnames buf =
match String.length buf with
| 0 -> []
| n ->
let parsef buf =
let typ = String.get_uint8 buf 0 in
let entrylen = String.get_uint16_be buf 1 in
let rt = shift buf (3 + entrylen) in
match typ with
| 0 -> let hostname = String.sub buf 3 entrylen in
(Some hostname, rt)
| _ -> (None, rt)
in
let list_length = String.get_uint16_be buf 0 in
if list_length + 2 <> n then
raise_trailing_bytes "hostname"
else
parse_list parsef (String.sub buf 2 list_length) []
let parse_fragment_length buf =
if String.length buf <> 1 then
raise_trailing_bytes "fragment length"
else
int_to_max_fragment_length (String.get_uint8 buf 0)
let parse_supported_version buf =
parse_any_version_opt buf
let parse_supported_versions buf =
let len = String.get_uint8 buf 0 in
if len mod 2 <> 0 then
raise_wrong_length "supported versions"
else
parse_count_list parse_supported_version (shift buf 1) [] (len / 2)
let parse_named_group buf =
let typ = String.get_uint16_be buf 0 in
(int_to_named_group typ, shift buf 2)
let parse_group buf =
match parse_named_group buf with
| Some x, buf -> (named_group_to_group x, buf)
| None, buf -> (None, buf)
let parse_supported_groups buf =
let count = String.get_uint16_be buf 0 in
if count mod 2 <> 0 then
raise_wrong_length "elliptic curve list"
else
let cs, rt = parse_count_list parse_named_group (shift buf 2) [] (count / 2) in
if String.length rt <> 0 then
raise_trailing_bytes "elliptic curves"
else
cs
let parse_signature_algorithm buf =
match int_to_signature_alg (String.get_uint16_be buf 0) with
| Some sig_alg -> of_signature_alg sig_alg
| _ -> None
let parse_signature_algorithms buf =
let parsef buf = parse_signature_algorithm buf, shift buf 2 in
let count = String.get_uint16_be buf 0 in
if count mod 2 <> 0 then
raise_wrong_length "signature hash"
else
parse_count_list parsef (shift buf 2) [] (count / 2)
let parse_alpn_protocol raw =
let length = String.get_uint8 raw 0 in
let protocol = String.sub raw 1 length in
(Some protocol, shift raw (1 + length))
let parse_alpn_protocols buf =
let len = String.get_uint16_be buf 0 in
if String.length buf <> len + 2 then
raise_trailing_bytes "alpn"
else
parse_list parse_alpn_protocol (String.sub buf 2 len) []
let parse_ec_point_format buf =
(* this is deprecated, we only check that uncompressed (typ 0) is present *)
let data = String.get_uint8 buf 0 in
Some (data = 0), shift buf 1
let parse_ec_point_formats buf =
let count = String.get_uint8 buf 0 in
parse_count_list parse_ec_point_format (shift buf 1) [] count
let parse_extension buf = function
| MAX_FRAGMENT_LENGTH ->
(match parse_fragment_length buf with
| Some mfl -> `MaxFragmentLength mfl
| None -> raise_unknown "maximum fragment length")
| RENEGOTIATION_INFO ->
let len' = String.get_uint8 buf 0 in
if String.length buf <> len' + 1 then
raise_trailing_bytes "renegotiation"
else
`SecureRenegotiation (String.sub buf 1 len')
| EXTENDED_MASTER_SECRET ->
if String.length buf > 0 then
raise_trailing_bytes "extended master secret"
else
`ExtendedMasterSecret
| EC_POINT_FORMATS ->
let formats, rt = parse_ec_point_formats buf in
if String.length rt <> 0 then
raise_trailing_bytes "ec point formats"
else if List.mem true formats then
`ECPointFormats
else
raise_unknown "EC Point Formats without uncompressed"
| x -> `UnknownExtension (extension_type_to_int x, buf)
let parse_keyshare_entry buf =
let parse_share data =
let size = String.get_uint16_be data 0 in
split_str ~start:2 data size
in
let g, rest = parse_named_group buf in
let share, left = parse_share rest in
match g with
| None -> None, left
| Some g -> Some (g, share), left
let parse_id buf =
let id_len = String.get_uint16_be buf 0 in
if id_len = 0 then (* id must be non-empty! *)
raise_wrong_length "PSK id is empty"
else
let age = String.get_int32_be buf (id_len + 2) in
(Some (String.sub buf 2 id_len, age), shift buf (id_len + 6))
let parse_binder buf =
let l = String.get_uint8 buf 0 in
Some (String.sub buf 1 l), shift buf (l + 1)
let parse_client_presharedkeys buf =
let id_len = String.get_uint16_be buf 0 in
let identities = parse_list parse_id (String.sub buf 2 id_len) [] in
let binders_len = String.get_uint16_be buf (id_len + 2) in
let binders = parse_list parse_binder (String.sub buf (4 + id_len) binders_len) [] in
let id_binder = List.combine identities binders in
if String.length buf <> 4 + binders_len + id_len then
raise_trailing_bytes "psk"
else
id_binder
let parse_cookie buf =
let len = String.get_uint16_be buf 0 in
(String.sub buf 2 len, shift buf (2 + len))
let parse_psk_key_exchange_mode buf =
let data = String.get_uint8 buf 0 in
(int_to_psk_key_exchange_mode data, shift buf 1)
let parse_psk_key_exchange_modes buf =
let count = String.get_uint8 buf 0 in
parse_count_list parse_psk_key_exchange_mode (shift buf 1) [] count
let parse_ext raw =
let etype = String.get_uint16_be raw 0
and length = String.get_uint16_be raw 2
in
(etype, length, String.sub raw 4 length)
let parse_client_extension raw =
let etype, len, buf = parse_ext raw in
let data =
match int_to_extension_type etype with
| Some SERVER_NAME ->
(match parse_hostnames buf with
| [name] ->
(match Domain_name.of_string name with
| Error (`Msg err) ->
raise_unknown ("unable to canonicalize " ^ name ^ "into a domain name: " ^ err)
| Ok domain_name ->
(match Domain_name.host domain_name with
| Error (`Msg err) ->
raise_unknown ("unable to build a hostname from " ^ name ^ ": " ^ err)
| Ok hostname -> `Hostname hostname))
| _ -> raise_unknown "bad server name indication (multiple names)")
| Some SUPPORTED_GROUPS ->
let gs = parse_supported_groups buf in
`SupportedGroups gs
| Some PADDING ->
let rec check = function
| 0 -> `Padding len
| n -> let idx = pred n in
if String.get_uint8 buf idx <> 0 then
raise_unknown "bad padding in padding extension"
else
check idx
in
check len
| Some SIGNATURE_ALGORITHMS ->
let algos, rt = parse_signature_algorithms buf in
if String.length rt <> 0 then
raise_trailing_bytes "signature algorithms"
else
`SignatureAlgorithms algos
| Some APPLICATION_LAYER_PROTOCOL_NEGOTIATION ->
let protocols = parse_alpn_protocols buf in
`ALPN protocols
| Some KEY_SHARE ->
let ll = String.get_uint16_be buf 0 in
if ll + 2 <> String.length buf then
raise_unknown "bad key share extension"
else
let shares = parse_list parse_keyshare_entry (String.sub buf 2 ll) [] in
`KeyShare shares
| Some PRE_SHARED_KEY ->
let ids = parse_client_presharedkeys buf in
`PreSharedKeys ids
| Some EARLY_DATA ->
if String.length buf <> 0 then
raise_trailing_bytes "early data"
else
`EarlyDataIndication
| Some SUPPORTED_VERSIONS ->
let versions, rt = parse_supported_versions buf in
if String.length rt <> 0 then
raise_trailing_bytes "supported versions"
else
`SupportedVersions versions
| Some POST_HANDSHAKE_AUTH ->
if String.length buf = 0 then
`PostHandshakeAuthentication
else
raise_unknown "non-empty post handshake authentication"
| Some COOKIE ->
let c, rt = parse_cookie buf in
if String.length rt <> 0 then
raise_trailing_bytes "cookie"
else
`Cookie c
| Some PSK_KEY_EXCHANGE_MODES ->
let modes, rt = parse_psk_key_exchange_modes buf in
if String.length rt <> 0 then
raise_trailing_bytes "psk key exchange modes"
else
`PskKeyExchangeModes modes
| Some x -> parse_extension buf x
| None -> `UnknownExtension (etype, buf)
in
(Some data, shift raw (4 + len))
let parse_server_extension raw =
let etype, len, buf = parse_ext raw in
let data =
match int_to_extension_type etype with
| Some SERVER_NAME ->
(match parse_hostnames buf with
| [] -> `Hostname
| _ -> raise_unknown "bad server name indication (multiple names)")
| Some KEY_SHARE ->
(match parse_keyshare_entry buf with
| _, xs when String.length xs <> 0 -> raise_trailing_bytes "server keyshare"
| None, _ -> raise_unknown "keyshare entry"
| Some (g, ks), _ ->
match named_group_to_group g with
| Some g -> `KeyShare (g, ks)
| None -> raise_unknown "keyshare entry")
| Some PRE_SHARED_KEY ->
if String.length buf <> 2 then
raise_trailing_bytes "server pre_shared_key"
else
`PreSharedKey (String.get_uint16_be buf 0)
| Some SUPPORTED_GROUPS | Some SIGNATURE_ALGORITHMS | Some PADDING ->
raise_unknown "invalid extension in server hello!"
| Some APPLICATION_LAYER_PROTOCOL_NEGOTIATION ->
(match parse_alpn_protocols buf with
| [protocol] -> `ALPN protocol
| _ -> raise_unknown "bad ALPN (none or multiple names)")
| Some SUPPORTED_VERSIONS ->
let version = parse_version_exn buf in
`SelectedVersion version
| Some x -> parse_extension buf x
| None -> `UnknownExtension (etype, buf)
in
(Some data, shift raw (4 + len))
let parse_encrypted_extension raw =
let etype, len, buf = parse_ext raw in
let data =
match int_to_extension_type etype with
| Some SERVER_NAME ->
(match parse_hostnames buf with
| [] -> `Hostname
| _ -> raise_unknown "bad server name indication (multiple names)")
| Some SUPPORTED_GROUPS ->
let gs = parse_supported_groups buf in
let supported = List.filter_map named_group_to_group gs in
`SupportedGroups supported
| Some APPLICATION_LAYER_PROTOCOL_NEGOTIATION ->
(match parse_alpn_protocols buf with
| [protocol] -> `ALPN protocol
| _ -> raise_unknown "bad ALPN (none or multiple names)")
| Some EARLY_DATA ->
if String.length buf <> 0 then
raise_trailing_bytes "server early_data"
else
`EarlyDataIndication
| Some x -> raise_unknown ("bad encrypted extension " ^ (extension_type_to_string x)) (* TODO maybe unknown instead? *)
| None -> `UnknownExtension (etype, buf)
in
(Some data, shift raw (4 + len))
let parse_retry_extension raw =
let etype, len, buf = parse_ext raw in
let data =
match int_to_extension_type etype with
| Some KEY_SHARE ->
begin
let group, rt = parse_group buf in
if String.length rt <> 0 then
raise_trailing_bytes "key share"
else
match group with
| None -> raise_unknown "unknown group in key share"
| Some g -> `SelectedGroup g
end
| Some SUPPORTED_VERSIONS ->
let version = parse_version_exn buf in
`SelectedVersion version
| Some COOKIE ->
let c, rt = parse_cookie buf in
if String.length rt <> 0 then
raise_trailing_bytes "cookie"
else
`Cookie c
| _ -> `UnknownExtension (etype, buf)
in
(Some data, shift raw (4 + len))
let parse_extensions parse_ext buf =
let len = String.get_uint16_be buf 0 in
if String.length buf <> len + 2 then
raise_trailing_bytes "extensions"
else
parse_list parse_ext (String.sub buf 2 len) []
let parse_client_hello buf =
let client_version = parse_any_version_exn buf in
let client_random = String.sub buf 2 32 in
let slen = String.get_uint8 buf 34 in
let sessionid = if slen = 0 then None else Some (String.sub buf 35 slen) in
let ciphersuites, rt = parse_any_ciphersuites (shift buf (35 + slen)) in
let _, rt' = parse_compression_methods rt in
let extensions =
if String.length rt' = 0 then [] else parse_extensions parse_client_extension rt'
in
(* TLS 1.3 mandates PreSharedKeys to be the last extension *)
(if List.exists (function `PreSharedKeys _ -> true | _ -> false) extensions then
match List.rev extensions with
| `PreSharedKeys _::_ -> ()
| _ -> raise_unknown "Pre-shared key extension exists, but is not the last");
ClientHello { client_version ; client_random ; sessionid ; ciphersuites ; extensions }
let parse_server_hello buf =
let server_version = parse_version_exn buf in
let server_random = String.sub buf 2 32 in
let slen = String.get_uint8 buf 34 in
let sessionid = if slen = 0 then None else Some (String.sub buf 35 slen) in
let ciphersuite, rt = match parse_ciphersuite (shift buf (35 + slen)) with
| Some x, buf' -> (x, buf')
| None , _ -> raise_unknown "ciphersuite"
in
let rt' = match parse_compression_method rt with
| Some NULL, buf' -> buf'
| None , _ -> raise_unknown "compression method"
in
(* depending on the content of the server_random we have to diverge in behaviour *)
if String.equal server_random helloretryrequest then begin
(* hello retry request, TODO: verify compression=empty *)
match Ciphersuite.ciphersuite_to_ciphersuite13 ciphersuite with
| None -> raise_unknown "unsupported ciphersuite in hello retry request"
| Some ciphersuite ->
let extensions =
if String.length rt' = 0 then [] else parse_extensions parse_retry_extension rt'
in
let retry_version =
match Utils.map_find ~f:(function `SelectedVersion v -> Some v | _ -> None) extensions with
| None -> server_version
| Some v -> v
in
let selected_group =
match Utils.map_find ~f:(function `SelectedGroup g -> Some g | _ -> None) extensions with
| None -> raise_unknown "unknown selected group"
| Some g -> g
in
HelloRetryRequest { retry_version ; sessionid ; ciphersuite ; selected_group ; extensions }
end else begin
let extensions =
if String.length rt' = 0 then [] else parse_extensions parse_server_extension rt'
in
let server_version =
match Utils.map_find ~f:(function `SelectedVersion v -> Some v | _ -> None) extensions with
| None -> server_version
| Some v -> v
in
ServerHello { server_version ; server_random ; sessionid ; ciphersuite ; extensions }
end
let parse_certificates_exn buf =
let parsef buf =
let len = get_uint24_len ~off:0 buf in
(Some (String.sub buf 3 len), shift buf (len + 3))
in
let len = get_uint24_len ~off:0 buf in
if String.length buf <> len + 3 then
raise_trailing_bytes "certificates"
else
parse_list parsef (String.sub buf 3 len) []
let parse_certificates = catch @@ parse_certificates_exn
(* TODO finish implementation of certificate extensions *)
let parse_certificate_ext _ = None, ""
let parse_certificate_ext_1_3_exn buf =
let certlen = get_uint24_len ~off:0 buf in
let cert, extbuf, rest =
let cert, rt = split_str ~start:3 buf certlen in
let ext_len = String.get_uint16_be rt 0 in
let extbuf, rt = split_str ~start:2 rt ext_len in
cert, extbuf, rt
in
let exts = parse_list parse_certificate_ext extbuf [] in
(Some (cert, exts), rest)
let parse_certificate_ext_list_1_3_exn buf =
let len = get_uint24_len ~off:0 buf in
if String.length buf <> len + 3 then
raise_trailing_bytes "certificates"
else
parse_list parse_certificate_ext_1_3_exn (shift buf 3) []
let parse_certificates_1_3_exn buf =
let clen = String.get_uint8 buf 0 in
let context, rt = split_str ~start:1 buf clen in
let certs = parse_certificate_ext_list_1_3_exn rt in
(context, certs)
let parse_certificates_1_3 = catch @@ parse_certificates_1_3_exn
let parse_certificate_types buf =
let parsef buf =
let byte = String.get_uint8 buf 0 in
(int_to_client_certificate_type byte, shift buf 1)
in
let count = String.get_uint8 buf 0 in
parse_count_list parsef (shift buf 1) [] count
let parse_cas buf =
let parsef buf =
let length = String.get_uint16_be buf 0 in
let name = String.sub buf 2 length in
(Some name, shift buf (2 + length))
in
let calength = String.get_uint16_be buf 0 in
let cas, rt = split_str ~start:2 buf calength in
(parse_list parsef cas [], rt)
let parse_certificate_request_exn buf =
let certificate_types, buf' = parse_certificate_types buf in
let certificate_authorities, buf' = parse_cas buf' in
if String.length buf' <> 0 then
raise_trailing_bytes "certificate request"
else
(certificate_types, certificate_authorities)
let parse_certificate_request =
catch parse_certificate_request_exn
let parse_certificate_request_1_2_exn buf =
let certificate_types, buf' = parse_certificate_types buf in
let sigs, buf' = parse_signature_algorithms buf' in
let cas, buf' = parse_cas buf' in
if String.length buf' <> 0 then
raise_trailing_bytes "certificate request"
else
(certificate_types, sigs, cas)
let parse_certificate_request_1_2 =
catch parse_certificate_request_1_2_exn
let parse_certificate_request_extension raw =
let etype, len, buf = parse_ext raw in
let data = match int_to_extension_type etype with
| Some SIGNATURE_ALGORITHMS ->
let algos, rt = parse_signature_algorithms buf in
if String.length rt <> 0 then
raise_trailing_bytes "signature algorithms"
else
`SignatureAlgorithms algos
| Some CERTIFICATE_AUTHORITIES ->
let cas, rt = parse_cas buf in
if String.length rt <> 0 then
raise_trailing_bytes "certificate authorities"
else
let cas = List.fold_left (fun cas buf ->
match X509.Distinguished_name.decode_der buf with
| Ok ca -> ca :: cas
| Error _ -> cas)
[] cas
in
`CertificateAuthorities (List.rev cas)
| _ -> `UnknownExtension (etype, buf)
in
(Some data, shift raw (4 + len))
let parse_certificate_request_1_3_exn buf =
let contextlen = String.get_uint8 buf 0 in
let context, rt =
if contextlen = 0 then
None, shift buf 1
else
let ctx, rest = split_str ~start:1 buf contextlen in
Some ctx, rest
in
let exts = parse_extensions parse_certificate_request_extension rt in
(context, exts)
let parse_certificate_request_1_3 =
catch parse_certificate_request_1_3_exn
let parse_dh_parameters = catch @@ fun raw ->
let plength = String.get_uint16_be raw 0 in
let dh_p = String.sub raw 2 plength in
let buf = shift raw (2 + plength) in
let glength = String.get_uint16_be buf 0 in
let dh_g = String.sub buf 2 glength in
let buf = shift buf (2 + glength) in
let yslength = String.get_uint16_be buf 0 in
let dh_Ys = String.sub buf 2 yslength in
let buf = shift buf (2 + yslength) in
let rawparams = String.sub raw 0 (plength + glength + yslength + 6) in
({ dh_p ; dh_g ; dh_Ys }, rawparams, buf)
let parse_ec_parameters = catch @@ fun raw ->
if String.get_uint8 raw 0 <> ec_curve_type_to_int NAMED_CURVE then
raise_unknown "EC curve type"
else
match int_to_named_group (String.get_uint16_be raw 1) with
| Some g ->
begin match named_group_to_group g with
| Some ((`X25519 | `P256 | `P384 | `P521) as g) ->
let data_len = String.get_uint8 raw 3 in
let d, rest = split_str ~start:4 raw data_len in
g, d, String.sub raw 0 (data_len + 4), rest
| _ -> raise_unknown "EC group"
end
| None -> raise_unknown "EC named group"
let parse_digitally_signed_exn buf =
let siglen = String.get_uint16_be buf 0 in
if String.length buf <> siglen + 2 then
raise_trailing_bytes "digitally signed"
else
String.sub buf 2 siglen
let parse_digitally_signed =
catch parse_digitally_signed_exn
let parse_digitally_signed_1_2 = catch @@ fun buf ->
match parse_signature_algorithm buf with
| Some sig_alg ->
let signature = parse_digitally_signed_exn (shift buf 2) in
(sig_alg, signature)
| None -> raise_unknown "hash or signature algorithm"
let parse_session_ticket_extension raw =
let etype, len, buf = parse_ext raw in
let data = match int_to_extension_type etype with
| Some EARLY_DATA ->
if String.length buf <> 4 then
raise_unknown "bad early_data extension in session ticket"
else
let size = String.get_int32_be buf 0 in
`EarlyDataIndication size
| _ -> `UnknownExtension (etype, buf)
in
(Some data, shift raw (4 + len))
let parse_session_ticket buf =
let lifetime = String.get_int32_be buf 0
and age_add = String.get_int32_be buf 4
and nonce_len = String.get_uint8 buf 8
in
let nonce = String.sub buf 9 nonce_len in
let ticket_len = String.get_uint16_be buf (9 + nonce_len) in
let ticket, exts_buf = split_str ~start:(11 + nonce_len) buf ticket_len in
let extensions = parse_extensions parse_session_ticket_extension exts_buf in
{ lifetime ; age_add ; nonce ; ticket ; extensions }
let parse_client_dh_key_exchange_exn buf =
let len = String.get_uint16_be buf 0 in
if String.length buf <> len + 2 then
raise_trailing_bytes "client key exchange"
else
String.sub buf 2 len
let parse_client_dh_key_exchange = catch parse_client_dh_key_exchange_exn
let parse_client_ec_key_exchange_exn buf =
let len = String.get_uint8 buf 0 in
if String.length buf <> len + 1 then
raise_trailing_bytes "client key exchange"
else
String.sub buf 1 len
let parse_client_ec_key_exchange = catch parse_client_ec_key_exchange_exn
let parse_keyupdate buf =
if String.length buf <> 1 then
raise_trailing_bytes "key update"
else
match int_to_key_update_request_type (String.get_uint8 buf 0) with
| Some y -> y
| None -> raise_unknown "key update content"
let parse_handshake_frame buf =
if String.length buf < 4 then
(None, buf)
else
let l = get_uint24_len ~off:1 buf in
let hslen = l + 4 in
if String.length buf >= hslen then
let hs, rest = split_str buf hslen in
(Some hs, rest)
else
(None, buf)
let parse_handshake = catch @@ fun buf ->
let typ = String.get_uint8 buf 0 in
let handshake_type = int_to_handshake_type typ in
let len = get_uint24_len ~off:1 buf in
if String.length buf <> len + 4 then
raise_trailing_bytes "handshake"
else
let payload = String.sub buf 4 len in
match handshake_type with
| Some HELLO_REQUEST ->
if String.length payload = 0 then HelloRequest else raise_trailing_bytes "hello request"
| Some CLIENT_HELLO -> parse_client_hello payload
| Some SERVER_HELLO -> parse_server_hello payload
| Some CERTIFICATE -> Certificate payload
| Some CERTIFICATE_VERIFY -> CertificateVerify payload
| Some SERVER_KEY_EXCHANGE -> ServerKeyExchange payload
| Some SERVER_HELLO_DONE ->
if String.length payload = 0 then ServerHelloDone else raise_trailing_bytes "server hello done"
| Some CERTIFICATE_REQUEST -> CertificateRequest payload
| Some CLIENT_KEY_EXCHANGE -> ClientKeyExchange payload
| Some FINISHED -> Finished payload
| Some ENCRYPTED_EXTENSIONS ->
let ee = parse_extensions parse_encrypted_extension payload in
EncryptedExtensions ee
| Some KEY_UPDATE ->
let ku = parse_keyupdate payload in
KeyUpdate ku
| Some SESSION_TICKET ->
let ticket = parse_session_ticket payload in
SessionTicket ticket
| Some END_OF_EARLY_DATA ->
EndOfEarlyData
| Some _
| None -> raise_unknown @@ "handshake type" ^ string_of_int typ