173 lines
6.6 KiB
OCaml
173 lines
6.6 KiB
OCaml
|
|
(** Transport layer security
|
||
|
|
|
||
|
|
[TLS] is an implementation of
|
||
|
|
{{:https://en.wikipedia.org/wiki/Transport_Layer_Security}transport
|
||
|
|
layer security} in OCaml. TLS is a widely used security protocol
|
||
|
|
which establishes an end-to-end secure channel (with optional
|
||
|
|
(mutual) authentication) between two endpoints. It uses TCP/IP as
|
||
|
|
transport. This library supports all four versions of TLS:
|
||
|
|
{{:https://tools.ietf.org/html/rfc8446}1.3, RFC8446},
|
||
|
|
{{:https://tools.ietf.org/html/rfc5246}1.2, RFC5246},
|
||
|
|
{{:https://tools.ietf.org/html/rfc4346}1.1, RFC4346}, and
|
||
|
|
{{:https://tools.ietf.org/html/rfc2246}1.0, RFC2246}. SSL, the
|
||
|
|
previous protocol definition, is not supported.
|
||
|
|
|
||
|
|
TLS is algorithmically agile: protocol version, key exchange
|
||
|
|
algorithm, symmetric cipher, and message authentication code are
|
||
|
|
negotiated upon connection.
|
||
|
|
|
||
|
|
This library implements several extensions of TLS,
|
||
|
|
{{:https://tools.ietf.org/html/rfc3268}AES ciphers},
|
||
|
|
{{:https://tools.ietf.org/html/rfc4366}TLS extensions} (such as
|
||
|
|
server name indication, SNI),
|
||
|
|
{{:https://tools.ietf.org/html/rfc5746}Renegotiation extension},
|
||
|
|
{{:https://tools.ietf.org/html/rfc7627}Session Hash and Extended
|
||
|
|
Master Secret Extension}.
|
||
|
|
|
||
|
|
This library does not contain insecure cipher suites (such as
|
||
|
|
single DES, export ciphers, ...). It does not expose the server
|
||
|
|
time in the server random, requires secure renegotiation.
|
||
|
|
|
||
|
|
This library consists of a core, implemented in a purely
|
||
|
|
functional matter ({!Engine}, this module), and effectful parts:
|
||
|
|
{!Tls_lwt} and {!Tls_mirage}.
|
||
|
|
|
||
|
|
{e v2.0.3} *)
|
||
|
|
|
||
|
|
|
||
|
|
(** {1 Abstract state type} *)
|
||
|
|
|
||
|
|
(** The abstract type of a TLS state. *)
|
||
|
|
type state
|
||
|
|
|
||
|
|
(** {1 Constructors} *)
|
||
|
|
|
||
|
|
(** [client client] is [tls * out] where [tls] is the initial state,
|
||
|
|
and [out] the initial client hello *)
|
||
|
|
val client : Config.client -> (state * string)
|
||
|
|
|
||
|
|
(** [server server] is [tls] where [tls] is the initial server
|
||
|
|
state *)
|
||
|
|
val server : Config.server -> state
|
||
|
|
|
||
|
|
(** {1 Protocol failures} *)
|
||
|
|
|
||
|
|
(** failures which can be mitigated by reconfiguration *)
|
||
|
|
type error = [
|
||
|
|
| `AuthenticationFailure of X509.Validation.validation_error
|
||
|
|
| `NoConfiguredCiphersuite of Ciphersuite.ciphersuite list
|
||
|
|
| `NoConfiguredVersions of Core.tls_version list
|
||
|
|
| `NoConfiguredSignatureAlgorithm of Core.signature_algorithm list
|
||
|
|
| `NoMatchingCertificateFound of string
|
||
|
|
| `CouldntSelectCertificate
|
||
|
|
]
|
||
|
|
|
||
|
|
(** failures from received garbage or lack of features *)
|
||
|
|
type fatal = [
|
||
|
|
| `Protocol_version of [
|
||
|
|
| `None_supported of Core.tls_any_version list
|
||
|
|
| `Unknown_record of int * int
|
||
|
|
| `Bad_record of Core.tls_any_version
|
||
|
|
]
|
||
|
|
| `Unexpected of [
|
||
|
|
| `Content_type of int
|
||
|
|
| `Message of string
|
||
|
|
| `Handshake of Core.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
|
||
|
|
]
|
||
|
|
|
||
|
|
(** type of failures *)
|
||
|
|
type failure = [
|
||
|
|
| `Error of error
|
||
|
|
| `Fatal of fatal
|
||
|
|
| `Alert of Packet.alert_type
|
||
|
|
]
|
||
|
|
|
||
|
|
(** [alert_of_failure failure] is [alert], the TLS alert type for this failure. *)
|
||
|
|
val alert_of_failure : failure -> Packet.alert_level * Packet.alert_type
|
||
|
|
|
||
|
|
(** [string_of_failure failure] is [string], the string representation of the [failure]. *)
|
||
|
|
val string_of_failure : failure -> string
|
||
|
|
|
||
|
|
(** [pp_failure failure] pretty-prints failure. *)
|
||
|
|
val pp_failure : failure Fmt.t
|
||
|
|
|
||
|
|
(** {1 Protocol handling} *)
|
||
|
|
|
||
|
|
(** result type of {!handle_tls}: either failed to handle the incoming
|
||
|
|
buffer ([`Fail]) with {!failure} and potentially a message to send
|
||
|
|
to the other endpoint, or sucessful operation ([`Ok]) with a new
|
||
|
|
{!state}, an end of file ([`Eof]), or an incoming ([`Alert]).
|
||
|
|
Possibly some [`Response] to the other endpoint is needed, and
|
||
|
|
potentially some [`Data] for the application was received. *)
|
||
|
|
type ret =
|
||
|
|
(state * [ `Eof ] option
|
||
|
|
* [ `Response of string option ]
|
||
|
|
* [ `Data of string option ],
|
||
|
|
failure * [ `Response of string ]) result
|
||
|
|
|
||
|
|
(** [handle_tls state buffer] is [ret], depending on incoming [state]
|
||
|
|
and [buffer], the result is the appropriate {!ret} *)
|
||
|
|
val handle_tls : state -> string -> ret
|
||
|
|
|
||
|
|
(** [handshake_in_progrss state] is a predicate which indicates whether there
|
||
|
|
is a handshake in progress or scheduled. *)
|
||
|
|
val handshake_in_progress : state -> bool
|
||
|
|
|
||
|
|
(** [send_application_data tls outs] is [Some (tls', out)] where
|
||
|
|
[tls'] is the new tls state, and [out] the cstruct to send over the
|
||
|
|
wire (encrypted [outs]) when the TLS session is ready. When the TLS
|
||
|
|
session is not ready it is [None]. *)
|
||
|
|
val send_application_data : state -> string list -> (state * string) option
|
||
|
|
|
||
|
|
(** [send_close_notify tls] is [tls' * out] where [tls'] is the new
|
||
|
|
tls state, and out the (possible encrypted) close notify alert. *)
|
||
|
|
val send_close_notify : state -> state * string
|
||
|
|
|
||
|
|
(** [reneg ~authenticator ~acceptable_cas ~cert tls] initiates a renegotation on
|
||
|
|
[tls], using the provided [authenticator]. It is [tls' * out] where [tls']
|
||
|
|
is the new tls state, and [out] either a client hello or hello request
|
||
|
|
(depending on which communication endpoint [tls] is). *)
|
||
|
|
val reneg : ?authenticator:X509.Authenticator.t ->
|
||
|
|
?acceptable_cas:X509.Distinguished_name.t list -> ?cert:Config.own_cert ->
|
||
|
|
state -> (state * string) option
|
||
|
|
|
||
|
|
(** [key_update ~request state] initiates a KeyUpdate (TLS 1.3 only). If
|
||
|
|
[request] is provided and [true] (the default), the KeyUpdate message
|
||
|
|
contains a request that the peer should update their traffic key as well. *)
|
||
|
|
val key_update : ?request:bool -> state -> (state * string, failure) result
|
||
|
|
|
||
|
|
(** {1 Session information} *)
|
||
|
|
|
||
|
|
(** [epoch state] is [epoch], which contains the session
|
||
|
|
information. If there's no established session yet, an error is returned. *)
|
||
|
|
val epoch : state -> (Core.epoch_data, unit) result
|
||
|
|
|
||
|
|
(** [export_key_material epoch_data ?context label length] is the RFC 5705
|
||
|
|
exported key material of [length] bytes using [label] and, if provided,
|
||
|
|
[context]. *)
|
||
|
|
val export_key_material : Core.epoch_data -> ?context:string -> string -> int ->
|
||
|
|
string
|
||
|
|
|
||
|
|
(** [channel_binding epoch_data mode] is the RFC 5929 and RFC 9266 specified
|
||
|
|
channel binding. Please note that [`Tls_unique] will error for TLS 1.3
|
||
|
|
sessions, and [`Tls_exporter] is not recommended for TLS < 1.3 sessions
|
||
|
|
(unless the uniqueness is ensured via another path). *)
|
||
|
|
val channel_binding : Core.epoch_data ->
|
||
|
|
[ `Tls_exporter | `Tls_unique | `Tls_server_endpoint ] ->
|
||
|
|
(string, [ `Msg of string ]) result
|