(* Defines all high-level datatypes for the TLS library. It is opaque to clients of this library, and only used from within the library. *) open Core open Mirage_crypto type hmac_key = string (* initialisation vector style, depending on TLS version *) type iv_mode = | Iv of string (* traditional CBC (reusing last cipherblock) *) | Random_iv (* TLS 1.1 and higher explicit IV (we use random) *) type 'k cbc_cipher = (module Block.CBC with type key = 'k) type 'k cbc_state = { cipher : 'k cbc_cipher ; cipher_secret : 'k ; iv_mode : iv_mode ; hmac : Digestif.hash' ; hmac_secret : hmac_key } type nonce = string type 'k aead_cipher = (module AEAD with type key = 'k) type 'k aead_state = { cipher : 'k aead_cipher ; cipher_secret : 'k ; nonce : nonce ; explicit_nonce : bool ; (* RFC 7905: no explicit nonce, instead TLS 1.3 construction is adapted *) } (* state of a symmetric cipher *) type cipher_st = | CBC : 'k cbc_state -> cipher_st | AEAD : 'k aead_state -> cipher_st (* context of a TLS connection (both in and out has each one of these) *) type crypto_context = { sequence : int64 ; (* sequence number *) cipher_st : cipher_st ; (* cipher state *) } (* the raw handshake log we need to carry around *) type hs_log = string list type dh_secret = [ | `Finite_field of Mirage_crypto_pk.Dh.secret | `P256 of Mirage_crypto_ec.P256.Dh.secret | `P384 of Mirage_crypto_ec.P384.Dh.secret | `P521 of Mirage_crypto_ec.P521.Dh.secret | `X25519 of Mirage_crypto_ec.X25519.secret ] (* a collection of client and server verify bytes for renegotiation *) type reneg_params = string * string type common_session_data = { server_random : string ; (* 32 bytes random from the server hello *) client_random : string ; (* 32 bytes random from the client hello *) peer_certificate_chain : X509.Certificate.t list ; peer_certificate : X509.Certificate.t option ; trust_anchor : X509.Certificate.t option ; received_certificates : X509.Certificate.t list ; own_certificate : X509.Certificate.t list ; own_private_key : X509.Private_key.t option ; own_name : [`host] Domain_name.t option ; client_auth : bool ; master_secret : master_secret ; alpn_protocol : string option ; (* selected alpn protocol after handshake *) } type session_data = { common_session_data : common_session_data ; client_version : tls_any_version ; (* version in client hello (needed in RSA client key exchange) *) ciphersuite : Ciphersuite.ciphersuite ; group : group option ; renegotiation : reneg_params ; (* renegotiation data *) session_id : string ; extended_ms : bool ; tls_unique : string ; } (* state machine of the server *) type server_handshake_state = | AwaitClientHello (* initial state *) | AwaitClientHelloRenegotiate | AwaitClientCertificate_RSA of session_data * hs_log | AwaitClientCertificate_DHE of session_data * dh_secret * hs_log | AwaitClientKeyExchange_RSA of session_data * hs_log (* server hello done is sent, and RSA key exchange used, waiting for a client key exchange message *) | AwaitClientKeyExchange_DHE of session_data * dh_secret * hs_log (* server hello done is sent, and DHE_RSA key exchange used, waiting for client key exchange *) | AwaitClientCertificateVerify of session_data * crypto_context * crypto_context * hs_log | AwaitClientChangeCipherSpec of session_data * crypto_context * crypto_context * hs_log (* client key exchange received, next should be change cipher spec *) | AwaitClientChangeCipherSpecResume of session_data * crypto_context * string * hs_log (* resumption: next should be change cipher spec *) | AwaitClientFinished of session_data * hs_log (* change cipher spec received, next should be the finished including a hmac over all handshake packets *) | AwaitClientFinishedResume of session_data * string * hs_log (* change cipher spec received, next should be the finished including a hmac over all handshake packets *) | Established (* handshake successfully completed *) (* state machine of the client *) type client_handshake_state = | ClientInitial (* initial state *) | AwaitServerHello of client_hello * (group * dh_secret) list * hs_log (* client hello is sent, handshake_params are half-filled *) | AwaitServerHelloRenegotiate of session_data * client_hello * hs_log (* client hello is sent, handshake_params are half-filled *) | AwaitCertificate_RSA of session_data * hs_log (* certificate expected with RSA key exchange *) | AwaitCertificate_DHE of session_data * hs_log (* certificate expected with DHE key exchange *) | AwaitServerKeyExchange_DHE of session_data * hs_log (* server key exchange expected with DHE *) | AwaitCertificateRequestOrServerHelloDone of session_data * string * string * hs_log (* server hello done expected, client key exchange and premastersecret are ready *) | AwaitServerHelloDone of session_data * signature_algorithm list option * string * string * hs_log (* server hello done expected, client key exchange and premastersecret are ready *) | AwaitServerChangeCipherSpec of session_data * crypto_context * string * hs_log (* change cipher spec expected *) | AwaitServerChangeCipherSpecResume of session_data * crypto_context * crypto_context * hs_log (* change cipher spec expected *) | AwaitServerFinished of session_data * string * hs_log (* finished expected with a hmac over all handshake packets *) | AwaitServerFinishedResume of session_data * hs_log (* finished expected with a hmac over all handshake packets *) | Established (* handshake successfully completed *) type kdf = { secret : string ; cipher : Ciphersuite.ciphersuite13 ; hash : Digestif.hash' ; } (* TODO needs log of CH..CF for post-handshake auth *) (* TODO drop master_secret!? *) type session_data13 = { common_session_data13 : common_session_data ; ciphersuite13 : Ciphersuite.ciphersuite13 ; master_secret : kdf ; exporter_master_secret : string ; resumption_secret : string ; state : epoch_state ; resumed : bool ; client_app_secret : string ; server_app_secret : string ; } type client13_handshake_state = | AwaitServerHello13 of client_hello * (group * dh_secret) list * string (* this is for CH1 ~> HRR ~> CH2 <~ WAIT SH *) | AwaitServerEncryptedExtensions13 of session_data13 * string * string * string | AwaitServerCertificateRequestOrCertificate13 of session_data13 * string * string * string | AwaitServerCertificate13 of session_data13 * string * string * signature_algorithm list option * string | AwaitServerCertificateVerify13 of session_data13 * string * string * signature_algorithm list option * string | AwaitServerFinished13 of session_data13 * string * string * signature_algorithm list option * string | Established13 type server13_handshake_state = | AwaitClientHelloHRR13 (* if we sent out HRR (also to-be-used for tls13-only) *) | AwaitClientCertificate13 of session_data13 * string * crypto_context * session_ticket option * string | AwaitClientCertificateVerify13 of session_data13 * string * crypto_context * session_ticket option * string | AwaitClientFinished13 of string * crypto_context * session_ticket option * string | AwaitEndOfEarlyData13 of string * crypto_context * crypto_context * session_ticket option * string | Established13 type handshake_machina_state = | Client of client_handshake_state | Server of server_handshake_state | Client13 of client13_handshake_state | Server13 of server13_handshake_state (* state during a handshake, used in the handlers *) type handshake_state = { session : [ `TLS of session_data | `TLS13 of session_data13 ] list ; protocol_version : tls_version ; early_data_left : int32 ; machina : handshake_machina_state ; (* state machine state *) config : Config.config ; (* given config *) hs_fragment : string ; (* handshake messages can be fragmented, leftover from before *) } (* connection state: initially None, after handshake a crypto context *) type crypto_state = crypto_context option (* record consisting of a content type and a byte vector *) type record = Packet.content_type * string (* response returned by a handler *) type rec_resp = [ | `Change_enc of crypto_context (* either instruction to change the encryptor to the given one *) | `Change_dec of crypto_context (* either change the decryptor to the given one *) | `Record of record (* or a record which should be sent out *) ] (* return type of handshake handlers *) type handshake_return = handshake_state * rec_resp list (* Top level state, encapsulating the entire session. *) type state = { handshake : handshake_state ; (* the current handshake state *) decryptor : crypto_state ; (* the current decryption state *) encryptor : crypto_state ; (* the current encryption state *) fragment : string ; (* the leftover fragment from TCP fragmentation *) read_closed : bool ; write_closed : bool ; } type error = [ | `AuthenticationFailure of X509.Validation.validation_error | `NoConfiguredCiphersuite of Ciphersuite.ciphersuite list | `NoConfiguredVersions of tls_version list | `NoConfiguredSignatureAlgorithm of signature_algorithm list | `NoMatchingCertificateFound of string | `CouldntSelectCertificate ] let pp_error ppf = function | `AuthenticationFailure v -> Fmt.pf ppf "authentication failure: %a" X509.Validation.pp_validation_error v | `NoConfiguredCiphersuite cs -> Fmt.pf ppf "no configured ciphersuite: %a" Fmt.(list ~sep:(any ", ") Ciphersuite.pp_ciphersuite) cs | `NoConfiguredVersions vs -> Fmt.pf ppf "no configured version: %a" Fmt.(list ~sep:(any ", ") pp_tls_version) vs | `NoConfiguredSignatureAlgorithm sas -> Fmt.pf ppf "no configure signature algorithm: %a" Fmt.(list ~sep:(any ", ") pp_signature_algorithm) sas | `NoMatchingCertificateFound host -> Fmt.pf ppf "no matching certificate found for %s" host | `CouldntSelectCertificate -> Fmt.string ppf "couldn't select certificate" type fatal = [ | `Protocol_version of [ | `None_supported of tls_any_version list | `Unknown_record of int * int | `Bad_record of tls_any_version ] | `Unexpected of [ | `Content_type of int | `Message of string | `Handshake of tls_handshake ] | `Decode of string | `Handshake of [ | `Message of string | `Fragments | `BadDH of string | `BadECDH of Mirage_crypto_ec.error ] | `Bad_certificate of string | `Missing_extension of string | `Bad_mac | `Record_overflow of int | `Unsupported_extension | `Inappropriate_fallback | `No_application_protocol ] let pp_protocol_version ppf = function | `None_supported vs -> Fmt.pf ppf "none supported, client provided %a" Fmt.(list ~sep:(any ", ") pp_tls_any_version) vs | `Unknown_record (maj, min) -> Fmt.pf ppf "unknown record version %u.%u" maj min | `Bad_record v -> Fmt.pf ppf "bad record version %a" pp_tls_any_version v let pp_unexpected ppf = function | `Content_type c -> Fmt.pf ppf "content type %u" c | `Message msg -> Fmt.string ppf msg | `Handshake hs -> Fmt.pf ppf "handshake %a" pp_handshake hs let pp_handshake_error ppf = function | `Message msg -> Fmt.string ppf msg | `Fragments -> Fmt.string ppf "fragments are not empty" | `BadDH msg -> Fmt.pf ppf "bad DH %s" msg | `BadECDH e -> Fmt.pf ppf "bad ECDH %a" Mirage_crypto_ec.pp_error e let pp_fatal ppf = function | `Protocol_version e -> Fmt.pf ppf "version error: %a" pp_protocol_version e | `Unexpected p -> Fmt.pf ppf "unexpected: %a" pp_unexpected p | `Decode msg -> Fmt.pf ppf "decode error: %s" msg | `Handshake h -> Fmt.pf ppf "handshake error: %a" pp_handshake_error h | `Bad_certificate msg -> Fmt.pf ppf "bad certificate: %s" msg | `Missing_extension msg -> Fmt.pf ppf "missing extension: %s" msg | `Bad_mac -> Fmt.string ppf "MAC mismatch" | `Record_overflow n -> Fmt.pf ppf "record overflow %u" n | `Unsupported_extension -> Fmt.string ppf "unsupported extension" | `Inappropriate_fallback -> Fmt.string ppf "inappropriate fallback" | `No_application_protocol -> Fmt.string ppf "no application protocol" type failure = [ | `Error of error | `Fatal of fatal | `Alert of Packet.alert_type ] let pp_failure ppf = function | `Error e -> pp_error ppf e | `Fatal f -> pp_fatal ppf f | `Alert a -> Fmt.pf ppf "alert %s" (Packet.alert_type_to_string a) let common_data_to_epoch common is_server peer_name = let own_random, peer_random = if is_server then common.server_random, common.client_random else common.client_random, common.server_random in let epoch : epoch_data = { side = if is_server then `Server else `Client ; state = `Established ; protocol_version = `TLS_1_0 ; ciphersuite = `DHE_RSA_WITH_AES_256_CBC_SHA ; peer_random ; peer_certificate = common.peer_certificate ; peer_certificate_chain = common.peer_certificate_chain ; peer_name ; trust_anchor = common.trust_anchor ; own_random ; own_certificate = common.own_certificate ; own_private_key = common.own_private_key ; own_name = common.own_name ; received_certificates = common.received_certificates ; master_secret = common.master_secret ; exporter_master_secret = "" ; alpn_protocol = common.alpn_protocol ; session_id = "" ; extended_ms = false ; tls_unique = None ; } in epoch let epoch_of_session server peer_name protocol_version = function | `TLS (session : session_data) -> let epoch = common_data_to_epoch session.common_session_data server peer_name in { epoch with protocol_version = protocol_version ; ciphersuite = session.ciphersuite ; session_id = session.session_id ; extended_ms = session.extended_ms ; tls_unique = Some session.tls_unique ; } | `TLS13 (session : session_data13) -> let epoch : epoch_data = common_data_to_epoch session.common_session_data13 server peer_name in { epoch with protocol_version = protocol_version ; ciphersuite = (session.ciphersuite13 :> Ciphersuite.ciphersuite) ; extended_ms = true ; (* RFC 8446, Appendix D, last paragraph *) state = session.state ; exporter_master_secret = session.exporter_master_secret ; } let epoch_of_hs hs = let server = match hs.machina with | Client _ | Client13 _ -> false | Server _ | Server13 _ -> true and peer_name = Config.(hs.config.peer_name) in match hs.session with | [] -> None | session :: _ -> Some (epoch_of_session server peer_name hs.protocol_version session)