(** Core type definitions *) open Packet open Ciphersuite let ( let* ) = Result.bind let guard p e = if p then Ok () else Error e let split_str ?(start = 0) str off = String.sub str start off, String.sub str (start + off) (String.length str - off - start) let map_reader_error r = Result.map_error (fun e -> `Fatal e) r type tls13 = [ `TLS_1_3 ] let pp_tls13 ppf `TLS_1_3 = Fmt.string ppf "TLS 1.3" type tls_before_13 = [ | `TLS_1_0 | `TLS_1_1 | `TLS_1_2 ] let pp_tls_before_13 ppf = function | `TLS_1_0 -> Fmt.string ppf "TLS 1.0" | `TLS_1_1 -> Fmt.string ppf "TLS 1.1" | `TLS_1_2 -> Fmt.string ppf "TLS 1.2" type tls_version = [ tls13 | tls_before_13 ] let pp_tls_version ppf = function | #tls13 as v -> pp_tls13 ppf v | #tls_before_13 as v -> pp_tls_before_13 ppf v let pair_of_tls_version = function | `TLS_1_0 -> (3, 1) | `TLS_1_1 -> (3, 2) | `TLS_1_2 -> (3, 3) | `TLS_1_3 -> (3, 4) let compare_tls_version a b = match a, b with | `TLS_1_0, `TLS_1_0 -> 0 | `TLS_1_0, _ -> -1 | _, `TLS_1_0 -> 1 | `TLS_1_1, `TLS_1_1 -> 0 | `TLS_1_1, _ -> -1 | _, `TLS_1_1 -> 1 | `TLS_1_2, `TLS_1_2 -> 0 | `TLS_1_2, _ -> -1 | _, `TLS_1_2 -> 1 | `TLS_1_3, `TLS_1_3 -> 0 let next = function | `TLS_1_0 -> Some `TLS_1_1 | `TLS_1_1 -> Some `TLS_1_2 | `TLS_1_2 -> Some `TLS_1_3 | `TLS_1_3 -> None let all_versions (min, max) = let rec gen curr = if compare_tls_version max curr >= 0 then match next curr with | None -> [curr] | Some c -> curr :: gen c else [] in List.rev (gen min) let tls_version_of_pair = function | (3, 1) -> Some `TLS_1_0 | (3, 2) -> Some `TLS_1_1 | (3, 3) -> Some `TLS_1_2 | (3, 4) -> Some `TLS_1_3 | _ -> None type tls_any_version = [ | tls_version | `SSL_3 | `TLS_1_X of int ] let pp_tls_any_version ppf = function | #tls_version as v -> pp_tls_version ppf v | `SSL_3 -> Fmt.string ppf "SSL3" | `TLS_1_X x -> Fmt.pf ppf "TLS1.%u" x let any_version_to_version = function | #tls_version as v -> Some v | _ -> None let version_eq a b = match a with | #tls_version as x -> compare_tls_version x b = 0 | _ -> false let version_ge a b = match a with | #tls_version as x -> compare_tls_version x b >= 0 | `SSL_3 -> false | `TLS_1_X _ -> true let tls_any_version_of_pair x = match tls_version_of_pair x with | Some v -> Some v | None -> match x with | (3, 0) -> Some `SSL_3 | (3, x) -> Some (`TLS_1_X x) | _ -> None let pair_of_tls_any_version = function | #tls_version as x -> pair_of_tls_version x | `SSL_3 -> (3, 0) | `TLS_1_X m -> (3, m) let max_protocol_version (_, hi) = hi let min_protocol_version (lo, _) = lo type tls_hdr = { content_type : content_type; version : tls_any_version; } let pp_tls_hdr ppf { content_type ; version } = Fmt.pf ppf "content type: %a version: %a" pp_content_type content_type pp_tls_any_version version module SessionID = struct type t = string let compare = String.compare let hash t = Hashtbl.hash t let equal = String.equal end module PreSharedKeyID = struct type t = string let compare = String.compare let hash t = Hashtbl.hash t let equal = String.equal end type psk_identity = (string * int32) * string let binders_len psks = let binder_len (_, binder) = String.length binder + 1 (* binder len *) in 2 (* binder len *) + List.fold_left (+) 0 (List.map binder_len psks) type group = [ | `FFDHE2048 | `FFDHE3072 | `FFDHE4096 | `FFDHE6144 | `FFDHE8192 | `X25519 | `P256 | `P384 | `P521 ] let pp_group ppf = function | `FFDHE2048 -> Fmt.string ppf "FFDHE2048" | `FFDHE3072 -> Fmt.string ppf "FFDHE3072" | `FFDHE4096 -> Fmt.string ppf "FFDHE4096" | `FFDHE6144 -> Fmt.string ppf "FFDHE6144" | `FFDHE8192 -> Fmt.string ppf "FFDHE8192" | `X25519 -> Fmt.string ppf "X25519" | `P256 -> Fmt.string ppf "P256" | `P384 -> Fmt.string ppf "P384" | `P521 -> Fmt.string ppf "P521" let named_group_to_group = function | FFDHE2048 -> Some `FFDHE2048 | FFDHE3072 -> Some `FFDHE3072 | FFDHE4096 -> Some `FFDHE4096 | FFDHE6144 -> Some `FFDHE6144 | FFDHE8192 -> Some `FFDHE8192 | X25519 -> Some `X25519 | SECP256R1 -> Some `P256 | SECP384R1 -> Some `P384 | SECP521R1 -> Some `P521 | _ -> None let group_to_named_group = function | `FFDHE2048 -> FFDHE2048 | `FFDHE3072 -> FFDHE3072 | `FFDHE4096 -> FFDHE4096 | `FFDHE6144 -> FFDHE6144 | `FFDHE8192 -> FFDHE8192 | `X25519 -> X25519 | `P256 -> SECP256R1 | `P384 -> SECP384R1 | `P521 -> SECP521R1 let group_to_impl = function | `FFDHE2048 -> `Finite_field Mirage_crypto_pk.Dh.Group.ffdhe2048 | `FFDHE3072 -> `Finite_field Mirage_crypto_pk.Dh.Group.ffdhe3072 | `FFDHE4096 -> `Finite_field Mirage_crypto_pk.Dh.Group.ffdhe4096 | `FFDHE6144 -> `Finite_field Mirage_crypto_pk.Dh.Group.ffdhe6144 | `FFDHE8192 -> `Finite_field Mirage_crypto_pk.Dh.Group.ffdhe8192 | `X25519 -> `X25519 | `P256 -> `P256 | `P384 -> `P384 | `P521 -> `P521 type signature_algorithm = [ | `RSA_PKCS1_MD5 | `RSA_PKCS1_SHA1 | `RSA_PKCS1_SHA224 | `RSA_PKCS1_SHA256 | `RSA_PKCS1_SHA384 | `RSA_PKCS1_SHA512 | `ECDSA_SECP256R1_SHA1 | `ECDSA_SECP256R1_SHA256 | `ECDSA_SECP384R1_SHA384 | `ECDSA_SECP521R1_SHA512 | `RSA_PSS_RSAENC_SHA256 | `RSA_PSS_RSAENC_SHA384 | `RSA_PSS_RSAENC_SHA512 | `ED25519 (* | `ED448 | `RSA_PSS_PSS_SHA256 | `RSA_PSS_PSS_SHA384 | `RSA_PSS_PSS_SHA512 *) ] let hash_of_signature_algorithm = function | `RSA_PKCS1_MD5 -> `MD5 | `RSA_PKCS1_SHA1 -> `SHA1 | `RSA_PKCS1_SHA224 -> `SHA224 | `RSA_PKCS1_SHA256 -> `SHA256 | `RSA_PKCS1_SHA384 -> `SHA384 | `RSA_PKCS1_SHA512 -> `SHA512 | `RSA_PSS_RSAENC_SHA256 -> `SHA256 | `RSA_PSS_RSAENC_SHA384 -> `SHA384 | `RSA_PSS_RSAENC_SHA512 -> `SHA512 | `ECDSA_SECP256R1_SHA1 -> `SHA1 | `ECDSA_SECP256R1_SHA256 -> `SHA256 | `ECDSA_SECP384R1_SHA384 -> `SHA384 | `ECDSA_SECP521R1_SHA512 -> `SHA512 | `ED25519 -> `SHA512 let signature_scheme_of_signature_algorithm = function | `RSA_PKCS1_MD5 -> `RSA_PKCS1 | `RSA_PKCS1_SHA1 -> `RSA_PKCS1 | `RSA_PKCS1_SHA224 -> `RSA_PKCS1 | `RSA_PKCS1_SHA256 -> `RSA_PKCS1 | `RSA_PKCS1_SHA384 -> `RSA_PKCS1 | `RSA_PKCS1_SHA512 -> `RSA_PKCS1 | `RSA_PSS_RSAENC_SHA256 -> `RSA_PSS | `RSA_PSS_RSAENC_SHA384 -> `RSA_PSS | `RSA_PSS_RSAENC_SHA512 -> `RSA_PSS | `ECDSA_SECP256R1_SHA1 -> `ECDSA | `ECDSA_SECP256R1_SHA256 -> `ECDSA | `ECDSA_SECP384R1_SHA384 -> `ECDSA | `ECDSA_SECP521R1_SHA512 -> `ECDSA | `ED25519 -> `ED25519 let pp_signature_algorithm ppf sa = let h = hash_of_signature_algorithm sa and ss = signature_scheme_of_signature_algorithm sa in let pp_signature_scheme ppf = function | `RSA_PKCS1 -> Fmt.string ppf "RSA-PKCS1" | `RSA_PSS -> Fmt.string ppf "RSA-PSS" | `ECDSA -> Fmt.string ppf "ECDSA" | `ED25519 -> Fmt.string ppf "ED25519" in match ss with | `ED25519 -> Fmt.pf ppf "%a" pp_signature_scheme ss | `ECDSA -> let group_to_string = function | `ECDSA_SECP256R1_SHA1 -> "SECP256R1" | `ECDSA_SECP256R1_SHA256 -> "SECP256R1" | `ECDSA_SECP384R1_SHA384 -> "SECP384R1" | `ECDSA_SECP521R1_SHA512 -> "SECP521R1" | _ -> assert false in Fmt.pf ppf "%a %s %a" pp_signature_scheme ss (group_to_string sa) pp_hash h | _ -> Fmt.pf ppf "%a %a" pp_signature_scheme ss pp_hash h let rsa_sigalg = function | `RSA_PSS_RSAENC_SHA256 | `RSA_PSS_RSAENC_SHA384 | `RSA_PSS_RSAENC_SHA512 | `RSA_PKCS1_SHA256 | `RSA_PKCS1_SHA384 | `RSA_PKCS1_SHA512 | `RSA_PKCS1_SHA224 | `RSA_PKCS1_SHA1 | `RSA_PKCS1_MD5 -> true | `ECDSA_SECP256R1_SHA1 | `ECDSA_SECP256R1_SHA256 | `ECDSA_SECP384R1_SHA384 | `ECDSA_SECP521R1_SHA512 | `ED25519 -> false let tls13_sigalg = function | `RSA_PSS_RSAENC_SHA256 | `RSA_PSS_RSAENC_SHA384 | `RSA_PSS_RSAENC_SHA512 | `ECDSA_SECP256R1_SHA256 | `ECDSA_SECP384R1_SHA384 | `ECDSA_SECP521R1_SHA512 | `ED25519 -> true | `RSA_PKCS1_SHA256 | `RSA_PKCS1_SHA384 | `RSA_PKCS1_SHA512 | `RSA_PKCS1_SHA224 | `RSA_PKCS1_SHA1 | `RSA_PKCS1_MD5 | `ECDSA_SECP256R1_SHA1 -> false let pk_matches_sa pk sa = match pk, sa with | `RSA _, _ -> rsa_sigalg sa | `ED25519 _, `ED25519 | `P256 _, (`ECDSA_SECP256R1_SHA1 | `ECDSA_SECP256R1_SHA256) | `P384 _, `ECDSA_SECP384R1_SHA384 | `P521 _, `ECDSA_SECP521R1_SHA512 -> true | _ -> false type client_extension = [ | `Hostname of [`host] Domain_name.t | `MaxFragmentLength of max_fragment_length | `SupportedGroups of Packet.named_group list | `SecureRenegotiation of string | `Padding of int | `SignatureAlgorithms of signature_algorithm list | `ExtendedMasterSecret | `ALPN of string list | `KeyShare of (Packet.named_group * string) list | `EarlyDataIndication | `PreSharedKeys of psk_identity list | `SupportedVersions of tls_any_version list | `PostHandshakeAuthentication | `Cookie of string | `PskKeyExchangeModes of psk_key_exchange_mode list | `ECPointFormats | `UnknownExtension of (int * string) ] type server13_extension = [ | `KeyShare of (group * string) | `PreSharedKey of int | `SelectedVersion of tls_version (* only used internally in writer!! *) ] type server_extension = [ server13_extension | `Hostname | `MaxFragmentLength of max_fragment_length | `SecureRenegotiation of string | `ExtendedMasterSecret | `ALPN of string | `ECPointFormats | `UnknownExtension of (int * string) ] type encrypted_extension = [ | `Hostname | `MaxFragmentLength of max_fragment_length | `SupportedGroups of group list | `ALPN of string | `EarlyDataIndication | `UnknownExtension of (int * string) ] type hello_retry_extension = [ | `SelectedGroup of group (* only used internally in writer!! *) | `Cookie of string | `SelectedVersion of tls_version (* only used internally in writer!! *) | `UnknownExtension of (int * string) ] type client_hello = { client_version : tls_any_version; client_random : string; sessionid : SessionID.t option; ciphersuites : any_ciphersuite list; extensions : client_extension list } type server_hello = { server_version : tls_version; server_random : string; sessionid : SessionID.t option; ciphersuite : ciphersuite; extensions : server_extension list } type dh_parameters = { dh_p : string; dh_g : string; dh_Ys : string; } type hello_retry = { retry_version : tls_version ; ciphersuite : ciphersuite13 ; sessionid : SessionID.t option ; selected_group : group ; extensions : hello_retry_extension list } type session_ticket_extension = [ | `EarlyDataIndication of int32 | `UnknownExtension of int * string ] type session_ticket = { lifetime : int32 ; age_add : int32 ; nonce : string ; ticket : string ; extensions : session_ticket_extension list } type certificate_request_extension = [ (* | `StatusRequest *) | `SignatureAlgorithms of signature_algorithm list (* | `SignedCertificateTimestamp *) | `CertificateAuthorities of X509.Distinguished_name.t list (* | `OidFilters *) (* | `SignatureAlgorithmsCert *) | `UnknownExtension of (int * string) ] type tls_handshake = | HelloRequest | HelloRetryRequest of hello_retry | EncryptedExtensions of encrypted_extension list | ServerHelloDone | ClientHello of client_hello | ServerHello of server_hello | Certificate of string | ServerKeyExchange of string | CertificateRequest of string | ClientKeyExchange of string | CertificateVerify of string | Finished of string | SessionTicket of session_ticket | KeyUpdate of key_update_request_type | EndOfEarlyData let pp_handshake ppf = function | HelloRequest -> Fmt.string ppf "HelloRequest" | HelloRetryRequest _ -> Fmt.string ppf "HelloRetryRequest" | EncryptedExtensions _ -> Fmt.string ppf "EncryptedExtensions" | ServerHelloDone -> Fmt.string ppf "ServerHelloDone" | ClientHello _ -> Fmt.string ppf "ClientHello" | ServerHello _ -> Fmt.string ppf "ServerHello" | Certificate _ -> Fmt.string ppf "Certificate" | ServerKeyExchange _ -> Fmt.string ppf "ServerKeyExchange" | CertificateRequest _ -> Fmt.string ppf "CertificateRequest" | ClientKeyExchange _ -> Fmt.string ppf "ClientKeyExchange" | CertificateVerify _ -> Fmt.string ppf "CertificateVerify" | Finished _ -> Fmt.string ppf "Finished" | SessionTicket _ -> Fmt.string ppf "SessionTicket" | KeyUpdate _ -> Fmt.string ppf "KeyUpdate" | EndOfEarlyData -> Fmt.string ppf "EndOfEarlyData" let src = Logs.Src.create "tls.tracing" ~doc:"TLS tracing" module Tracing = struct include (val Logs.src_log src : Logs.LOG) let cs ~tag buf = debug (fun m -> m "%s@.%a" tag (Ohex.pp_hexdump ()) buf) let hs ~tag hs = debug (fun m -> m "%s %a" tag pp_handshake hs) end type tls_alert = alert_level * alert_type (** the master secret of a TLS connection *) type master_secret = string type psk13 = { identifier : string ; obfuscation : int32 ; secret : string ; lifetime : int32 ; early_data : int32 ; issued_at : Ptime.t ; (* origin : [ `Resumption | `External ] (* using different labels for binder_key *) *) } type epoch_state = [ `ZeroRTT | `Established ] (** information about an open session *) type epoch_data = { side : [ `Client | `Server ] ; state : epoch_state ; protocol_version : tls_version ; ciphersuite : Ciphersuite.ciphersuite ; peer_random : string ; peer_certificate_chain : X509.Certificate.t list ; peer_certificate : X509.Certificate.t option ; peer_name : [`host] Domain_name.t option ; trust_anchor : X509.Certificate.t option ; received_certificates : X509.Certificate.t list ; own_random : string ; own_certificate : X509.Certificate.t list ; own_private_key : X509.Private_key.t option ; own_name : [`host] Domain_name.t option ; master_secret : master_secret ; exporter_master_secret : master_secret ; session_id : SessionID.t ; extended_ms : bool ; alpn_protocol : string option ; tls_unique : string option ; } let supports_key_usage ?(not_present = false) usage cert = match X509.Extension.(find Key_usage (X509.Certificate.extensions cert)) with | None -> not_present | Some (_, kus) -> List.mem usage kus let supports_extended_key_usage ?(not_present = false) usage cert = match X509.Extension.(find Ext_key_usage (X509.Certificate.extensions cert)) with | None -> not_present | Some (_, kus) -> List.mem usage kus