···11+## v2.0.3 (2025-09-26)
22+33+* Provide an implementation which uses the Unix module (distributed with OCaml),
44+ based on the miou-unix implementation
55+ (#518 #443 @kit-ty-kate @dinosaure, fixes #517)
66+77+## v2.0.2 (2025-08-22)
88+99+* put CERTIFICATE_UNKNOWN alert back (#516 @copy)
1010+* improve README (#514 @dinosaure)
1111+1212+## v2.0.1 (2025-04-14)
1313+1414+* core, send_application_data: allocate much fewer data. previously, we
1515+ allocated a lot to split the data into chunks of 2^14 - and did not have
1616+ tail calls. the memory footprint, esp. for big amounts of data passed to
1717+ send_application_data has changed now (#515 @hannesm @reynir)
1818+1919+## v2.0.0 (2025-02-05)
2020+2121+* Use dune variants (mirage-ptime) instead of functorising over PCLOCK
2222+ (#513 @hannesm)
2323+2424+## v1.0.4 (2024-10-28)
2525+2626+* Removed links to nqsb (we're no longer in charge of that domain)
2727+ (23c0deef586df4a1732efbf8d8da006111ca76e0,
2828+ f6455e37513a74da63dc8819dfc8dd1aaebafb9a, fixes #512 reported by @msub2)
2929+3030+## v1.0.3 (2024-09-29)
3131+3232+* tls-miou-unix: fix recursive call in read_in (#511 @dinosaure)
3333+3434+## v1.0.2 (2024-09-04)
3535+3636+* tls-lwt: `read` now has an optional `?off:int` argument (#510 @hannesm)
3737+* Use the kdf package instead of hkdf (#509 @hannesm)
3838+3939+## v1.0.1 (2024-08-25)
4040+4141+* tls-miou-unix: fix file descriptor leak (#508 @dinosaure)
4242+* tls-miou-unix: fix fuzz test (#507 @dinosaure)
4343+4444+## v1.0.0 (2024-08-21)
4545+4646+* API breaking change: remove usage of Cstruct.t inside of TLS, use bytes
4747+ and string instead (#497 by @art-w, @hannesm, @dinosaure, @reynir)
4848+ Performance is up to 3x improved (bandwidth), 2x improvement for handshake/s
4949+ on an Intel Core(TM) i7-5600U CPU @ 2.60GHz
5050+* FEATURE: add tls-miou-unix package, which adds miou support for TLS
5151+ (#494 #503 @dinosaure)
5252+* FEATURE: tls-lwt and tls-async: allow TLS over an existing connection
5353+ `Tls_lwt.client_of_channels : Tls.Config.client -> ?host:[`host] Domain_name.t -> Lwt_io.input_channel * Lwt_io.output_channel -> t Lwt.t`
5454+ and
5555+ `Tls_lwt.server_of_channels : Tls.Config.server -> Lwt_io.input_channel * Lwt_io.output_channel -> t Lwt.t`
5656+ (#499 @art-w @MisterDA)
5757+* API breaking changes: revise errors - reduce the polymorphic variant
5858+ in size, align it with RFC specified errors, be in parts more precise
5959+ about errors, in other parts skip data (#505, @hannesm - fixes #491)
6060+ NB: if you relied on a specific error constructor, please open an issue
6161+* Remove unused constructors from Packet.{alert_type, compression_methods,
6262+ client_certificate_type, extension_type} (#505, @hannesm)
6363+ NB: if you relied on specific constructors, please open an issue
6464+* API breaking change: Tls.Config.{server,client} now return a result
6565+ type instead of raising an exception (#502, @hannesm, fixes #411)
6666+* FEATURE: add bench/speed.exe, a benchmark for bandwidth (for different
6767+ ciphersuites) and handshakes (different key exchanges and private keys)
6868+ (#500 @hannesm @dinosaure @reynir)
6969+* BUGFIX: tests/feedback.exe update with TLS 1.3 semantics, run as test
7070+ (#501, @hannesm - reported by @dinosaure)
7171+7272+## v0.17.5 (2024-05-14)
7373+7474+* tls: documentation: clarify send_application_data (#492 @reynir)
7575+* BUGFIX: tls: export_key_material was wrong for the server side on TLS 1.3,
7676+ reported in robur-coop/miragevpn#181 by @reynir, fix in #495 @hannesm
7777+* FEATURE: tls: add channel_binding (RFC 5929, RFC 9266) support (tls_unique,
7878+ tls_exporter, tls_server_endpoint), requested by @Neustradamus in #484, added
7979+ in #496 by @hannesm
8080+8181+## v0.17.4 (2024-03-26)
8282+8383+* tls: handle half-closed connection properly: a received CLOSE_NOTIFY does not
8484+ lead to a CLOSE_NOTIFY to be sent (a `send_close_notify` sends it explicitly)
8585+ (#488 @hannesm)
8686+* tls: modify return type of `handle_tls` - the Alert is now in the right hand
8787+ side, and `` `Eof `` is explicit in the second part of the tuple
8888+ (#488 @hannesm)
8989+* tls: remove `can_handle_appdata`, the function `handshake_in_progress` is
9090+ available (#488 @hannesm)
9191+* tls-mirage: avoid exceptions in reneg and rekey (#487 @hannesm)
9292+* tls: remove HEARTBEAT decoding - HEARTBEAT was never supported in this
9393+ library, the decoder was superfluous (#487 @hannesm)
9494+* tls-mirage: provide `underlying : flow -> FLOW.flow` (#487 @hannesm,
9595+ fixes #425 @dinosaure)
9696+* tls-mirage: implement mirage-flow 4 API (`val shutdown`) (#488 @hannesm)
9797+* tls-eio: adapt to half-closed connections (#488 @talex5)
9898+* tls-eio: implement Eio.Resource.Close (#489 @paurkedal, reviewed by @talex5)
9999+100100+## v0.17.3 (2023-11-20)
101101+102102+* tls: provide Engine.export_key_material, which implements RFC 5705 (and 8446)
103103+ TLS EKM (#482 @hannesm)
104104+* tls: fix protocol_version in Engine.epoch (TLS 1.3 always pretended TLS 1.0)
105105+ (#482 @hannesm)
106106+* tls: add the side (`` `Client `` or `` `Server ``) to epoch_data
107107+ (#482 @hannesm)
108108+* BREAKING tls: Engine.epoch - return result instead of custom variant
109109+ (#483 @hannesm)
110110+111111+## v0.17.2 (2023-09-24)
112112+113113+* tls-eio: update to eio 0.12 (#479 @talex5)
114114+115115+## v0.17.1 (2023-07-03)
116116+117117+* tls-async: update to 0.16 series (#476 @tmcgilchrist, review by @torinnd)
118118+* avoid Cstruct.copy (@hannesm)
119119+120120+## v0.17.0 (2023-03-01)
121121+122122+* tls-async: remove ocaml < 5.0.0 constraint (#471 #474 @hannesm)
123123+* remove dependency on ppx, especially ppx_cstruct and ppx_sexp_conv across
124124+ the libraries (#473 @hannesm, discussion in #472)
125125+126126+## v0.16.0 (2023-02-14)
127127+128128+* BREAKING: new opam package tls-lwt (formerly tls.lwt), in dune:
129129+ (libraries tls.lwt) should now be libraries (tls-lwt)
130130+ (#468 @hannesm, reported #449 by @mbacarella)
131131+* tls: update to mirage-crypto 0.11 API (#468 @hannesm)
132132+* tls: relax SignatureAlgorithms extension handling to allow OpenSSL
133133+ interoperability tests with TLS 1.0 and TLS 1.1 (#469 @hannesm)
134134+* tls: remove Utils.filter_map and and Utils.option, use Stdlib instead (#455
135135+ @hannesm)
136136+* tls: do not globally open Utils (#455 @hannesm)
137137+* tls: export log source of Tracing module (#461 @bikallem)
138138+* tls: remove unused ciphersuites to reduce binary size (#467 @hannesm)
139139+* tls-lwt: do not catch out of memory exception (#469 @hannesm)
140140+* tls-eio: add fuzz testing using crowbar (#456 #463 @talex5)
141141+* tls-eio: update to eio 0.7 (#456 @talex5)
142142+* tls-eio: fix test for develop with vendoring (#462 @bikallem)
143143+144144+## v0.15.5 (2022-10-25)
145145+146146+* tls-eio release only: fix end-of-file handling (#454 @talex5), avoid
147147+ deprecation warnings (#454 @hannesm)
148148+149149+## v0.15.4 (2022-09-27)
150150+151151+* New package tls-eio (#451 @talex5)
152152+* Tls_async: expose tls_handler (#448 @mbacarella, reviewed by @torinnd)
153153+154154+## v0.15.3 (2022-03-29)
155155+156156+* Upgrade to v0.15 of Jane Street packages (#444 @bcc32)
157157+* Use cmdliner 1.1.0 in lwt/examples (#445 @hannesm)
158158+159159+## v0.15.2 (2021-11-14)
160160+161161+* Tls_async: drop dependency on async_find, now trust anchors in a directory
162162+ are not recursively read - aligns it with the lwt and mirage implementations
163163+ (#442 @torinnd)
164164+165165+## v0.15.1 (2021-10-29)
166166+167167+* Tls_lwt: avoid exception if connect is executed with a non-host name string
168168+ (e.g. an IP address) (#441 @hannesm)
169169+* Bugfix: log a warning if certificate decoding fails (#441 @hannesm)
170170+* Remove rresult dependency (#441 @hannesm)
171171+172172+## v0.15.0 (2021-10-07)
173173+174174+* Adapt to x509 0.15.0 changes (#440 @hannesm)
175175+176176+## v0.14.1 (2021-09-13)
177177+178178+* Bugfix: do not filter signature_algorithms based on server certificate. Since
179179+ signature_algorithms is also used for client authentication (as
180180+ SignatureAlgorithms extension in CertificateVerify), previously the client
181181+ needed the same key type as the server.
182182+ Discovered in https://github.com/roburio/albatross/commit/df434da0e531e1b0c8091a0fc82e8b37ed319e7a
183183+184184+## v0.14.0 (2021-08-02)
185185+186186+* Breaking: peer_name (in config and epoch data, also own_name) is now a
187187+ [`host] Domain_name.t instead of a string. (#434 #438 @torinnd @hannesm)
188188+* Add a X509_async module (#435 @torinnd)
189189+* Client and server constructor log messages are on the debug level (#436
190190+ reported by @talex5, fix by @hannesm)
191191+* Adapt to cstruct 6.0.0 API (Cstruct.len is deprecated) #439 @hannesm
192192+193193+## v0.13.2 (2021-06-04)
194194+195195+* New package tls-async that provides an effectful layer of TLS using async.
196196+ (#432, @torinnd, @dinosaure, @kit-ty-kate, reviews by @hannesm @avsm @seliopou)
197197+198198+## v0.13.1 (2021-04-22)
199199+200200+* Breaking: use deriving sexp_of instead of sexp. Constructing a state from
201201+ a sexp has not been supported (lead to exception), and is now removed
202202+ (#430 by @torinnd, continued in #431 by @hannesm)
203203+* Bugfix: TLS 1.3 client authentication with certificate, client side. This
204204+ used to work accidentally before 0.13.0 changed the signature algorithms
205205+ handling, now the right signature algorithm (as requested by server) is used.
206206+ (#431 @hannesm, @talex5 reported https://github.com/mirage/capnp-rpc/pull/228)
207207+* adapt to x509 0.13.0 and mirage-crypto-ec 0.10.0 changes (#431 @hannesm)
208208+209209+## v0.13.0 (2021-04-14)
210210+211211+* Remove static RSA and CBC ciphersuites from default configuration. The
212212+ default configuration now includes FFDHE and ECDHE key exchanges with RSA or
213213+ ECDSA/EdDSA certificates, and AEAD ciphers
214214+ (AES-GCM, AES-CCM, ChaCha20-Poly1305) (#429 by @hannesm)
215215+* Remove SHA1 from signature_algorithms in the default configuration
216216+ (#429 by @hannesm)
217217+* Support ECDSA and EdDSA certificates and private keys via x509 0.12.0 and
218218+ mirage-crypto-ec (#428 by @hannesm)
219219+ Breaking changes:
220220+ - the second part of type Tls.Config.certchain is now a X509.Private_key.t
221221+ (previously Mirage_crypto_pk.Rsa.priv)
222222+ - the type aliases X509_lwt.priv and X509_lwt.authenticator have been removed
223223+* Use mirage-crypto-ec instead of fiat-p256 and hacl_x25519 for elliptic curve
224224+ support - this adds P384 and P521 ECDH support (#428 by @hannesm)
225225+* Remove custom Monad implementation, use Result and Rresult instead
226226+ (#429 by @hannesm)
227227+* Remove Utils.Cs submodule, use Cstruct API instead (#429 by @hannesm)
228228+* Breaking: Tls.Engine.ret type is now a result instead of a custom variant type
229229+ (#429 by @hannesm)
230230+* Breaking: Tls_lwt.Unix.epoch results in (Tls.Core.epoch_data, unit) result -
231231+ it was a custom error type previously (#429 by @hannesm)
232232+233233+## v0.12.8 (2020-12-08)
234234+235235+* Re-add ECPointFormats hello extension (both client and server) to avoid
236236+ handshake failures with Go's TLS stack (RFC 8422 makes it optional, but go
237237+ (1.15.5) requires it) - reported by @jeffa5 at
238238+ https://discuss.ocaml.org/t/strange-prohibited-tls-1-2-cipher-suite-9d-issue/
239239+ fix by @hannesm #424
240240+241241+## v0.12.7 (2020-12-04)
242242+243243+* Tls.lwt: make the receive buffer connection-local to avoid potential data
244244+ races (#422 by @dinosaure)
245245+* Tls_mirage: remove unneeded type alias (@hannesm)
246246+* Add Tls.Config.Ciphers.http2 - a list of ciphersuites allowed to be negotiated
247247+ for HTTP2 sessions (#423 by @jeffa5)
248248+249249+## v0.12.6 (2020-11-06)
250250+251251+* OCaml 4.12 support (#421 @kit-ty-kate)
252252+253253+## v0.12.5 (2020-09-22)
254254+255255+* Rename length to v_length to be compatible with cstruct 6.0.0 (#419 @dinosaure)
256256+257257+## v0.12.4 (2020-08-08)
258258+259259+* handshake_server13: demote group and cipher log level (#417 by @xguerin)
260260+* tls_lwt: register printers for Tls_alert and Tls_failure (#418 by @hannesm)
261261+262262+## v0.12.3 (2020-07-04)
263263+264264+* Adapt to new GCM and CCM API of mirage-crypto (#416 by @hannesm)
265265+* Add support for ChaCha20/Poly1305 ciphersuite (#416 by @hannesm)
266266+267267+## v0.12.2 (2020-06-20)
268268+269269+* tls_lwt again calls Mirage_crypto_rng_lwt.initialize () -- which is since
270270+ mirage-crypto-rng 0.8 no longer inside the lwt monad, and safe to be called
271271+ multiple times and on top level (#415 by @hannesm)
272272+273273+## v0.12.1 (2020-06-12)
274274+275275+in #414 by @hannesm
276276+* Drop support for RC4 ciphersuite
277277+* Raise lower TLS version in default configuration to 1.2
278278+* tls_lwt no longer calls Mirage_crypto_rng_unix.initialize -- this needs to be
279279+ done in the application, inside Lwt_main.run:
280280+ `Mirage_crypto_rng_lwt.initialize () >>= fun () ->`
281281+* Support ECDHE ciphersuites in TLS 1.2 and below as specified in RFC 8422
282282+ (requested in #413 by @ryanakca, also in #362 by @orbitz @annubiz)
283283+* drop "TLS_" prefix from ciphersuite constructors
284284+* BUGFIX: TLS client (<= 1.2) assembling an empty Certificate message
285285+ (noticed in #413, present since 0.12.0 release)
286286+* Cleanup Packet.any_ciphersuite list (remove ARIA, CAMELLIA, KRB5, EXPORT)
287287+* Adapt interoperability test scripts with TLS 1.3 support
288288+289289+## v0.12.0 (2020-05-12)
290290+291291+in #405 by @hannesm
292292+* TLS 1.3 support
293293+* Tracing now uses the logs library (log source tls.tracing on debug level)
294294+* bugfix for padding in ClientHello, which computed wrong length
295295+* bugfix hs_fragments to be set before executing the protocol handling logic
296296+* bugfix guard RSA signature with an Insufficient_key handler, which may occur
297297+ when using an RSA key which size is too small for the used digest algorithm
298298+299299+## v0.11.1 (2020-04-09)
300300+301301+* Adapt to X509.0.11.0 API changes (#412)
302302+303303+## v0.11.0 (2020-03-12)
304304+305305+* use dune as build system (#407)
306306+* BREAKING split into tls and tls-mirage opam packages (#407)
307307+* BREAKING use mirage-crypto instead of nocrypto (#407)
308308+309309+## v0.10.6 (2020-01-23)
310310+311311+* adapt to x509 0.9.0 interface: certificate revocation lists can now be passed
312312+ to the authenticator in Tls_mirage and X509_lwt; also a list of hash
313313+ algorithms to be used for certificate signature verification can be passed to
314314+ the authenticator
315315+* adapt to lwt 5.0.0
316316+317317+## v0.10.5 (2019-11-01)
318318+319319+* adapt to mirage-flow 2.0.0, mirage-clock 3.0.0, mirage-kv 3.0.0 interfaces (#401 @hannesm)
320320+321321+## 0.10.4 (2019-08-15)
322322+323323+* tls_lwt: avoid double close by checking in the default `close` callback of
324324+ `Lwt_io.make` whether the underlying file descriptor has been closed already.
325325+ (reported and discussed by @hcarty in #395, merged #397)
326326+327327+## 0.10.3 (2019-07-26)
328328+329329+* support x509 0.7.0+
330330+* remove dependency on Astring (was only used in the lwt-starttls example)
331331+332332+## 0.10.2 (2019-04-02)
333333+334334+* support for cstruct 4.0.0+
335335+* remove support for < 4.04.2 (same as x509 in master)
336336+* remove result (part of 4.03.0)
337337+* enhance mirage/example2 to work on more platforms than unix
338338+339339+## 0.10.1 (2019-02-28)
340340+341341+* tls-mirage: fix compilation
342342+343343+## 0.10.0 (2019-02-28)
344344+345345+* tls: fix extensions length (used to include the 2 byte extension length field)
346346+ if padding is inserted (introduced on May 5, 2014 in #73)
347347+* tls-mirage: adapt to mirage-kv 2.0.0 API (#384, @samoht)
348348+349349+## 0.9.3 (2019-01-07)
350350+351351+* tls: do not require client sent ciphersuites to be a proper set
352352+ (interoperability with some android devices)
353353+* tls_lwt: delay error from writing to peer while reading, record errors only
354354+ if state is active (fixes #347)
355355+* migrate opam file to opam 2.0 format
356356+357357+## 0.9.2 (2018-08-24)
358358+359359+* compatibility with ppx_sexp_conv >v0.11.0 (#381), required for 4.07.0
360360+* support ALPN (#378, @bobbypriambodo)
361361+362362+## 0.9.1 (2018-02-26)
363363+364364+* Tls_lwt: use Tls.Config instead of Config directly to avoid polluting imported
365365+ names (#376, @rgrinberg)
366366+367367+## 0.9.0 (2017-12-23)
368368+369369+* renegotiation semantics (#375)
370370+ allow acceptable_ca, authenticator, and own_cert to be updated (Config.with_x)
371371+ semantics of reneg is blocking
372372+ `{Tls_lwt.Unix|Tls_mirage}.reneg ~drop:bool` drops data of earlier epoch
373373+* implement acceptable_ca (#332, @reynir)
374374+* fix client renegotiation with ExtendedMasterSecret (#373, broken since 0.7.0)
375375+* Config.client can get ~peer_name (#373)
376376+* Asn.Time.t is Ptime.t now (asn1-combinators.0.2.0, x509.0.6.0, #372)
377377+* cleanups (#360, #363, #369, @rgrinberg)
378378+* remove 3DES CBC SHA from default ciphers (#359)
379379+380380+## 0.8.0 (2017-02-01)
381381+382382+* lwt: in Unix.client_of_fd the named argument host is now optional (#336)
383383+* mirage: in client_of_flow the (positional) hostname argument is now optional (#336)
384384+* mirage: adapt to PCLOCK interface (@mattgray #329 #331)
385385+* build system migrated from oasis to topkg (#342)
386386+* mirage: adapt to MirageOS3 (@yomimono @samoht #338 #349 #350 #351 #353)
387387+* lwt: do not crash on double close (@vbmithr #345)
388388+* fixed docstring typos (@mor1 #340)
389389+390390+## 0.7.1 (2016-03-21)
391391+392392+* remove camlp4 dependency (use cstruct ppx and sexplib ppx instead)
393393+* sort client extensions, there are servers which dislike an extension without
394394+ data at the end, thus try to send extensions with data at the end (#319)
395395+* initial GCM support (#310)
396396+* fix `hs_can_handle_appdata` (#315):
397397+ Initially we allowed application data always after the first handshake.
398398+399399+ Turns out, between CCS and Finished there is new crypto_context in place
400400+ which has not yet been authenticated -- bad idea to accept application data
401401+ at that point (beginning of 2015 in OCaml TLS).
402402+403403+ The fix was to only allow application data in Established state (and block
404404+ in Tls_lwt/Tls_mirage when the user requested renegotiation) (December 2015
405405+ in OCaml-TLS).
406406+407407+ Renegotiation was also turned off by default when we introduced resumption
408408+ (mid October 2015): both features together (without mitigating via session
409409+ hash) allow the triple handshake.
410410+411411+ It turns out, the server side can happily accept application data from the
412412+ other side when it just sent a HelloRequest (and waits for the ClientHello;
413413+ same is true for the client side, waiting for the ServerHello in
414414+ renegotiation case might be interleaved with application data) to let the
415415+ client initiate a new handshake. By this commit, OCaml-TLS allows
416416+ application data then.
417417+418418+ In the end, it is a pretty academic thing anyways, since nobody uses
419419+ renegotiation with OCaml-TLS in the field.
420420+* during verification of a digitally signed: checked that the used hash
421421+ algorithm is one of the configured ones (#313)
422422+* unify return type of handshake and change cipher spec handler (#314)
423423+* separate client and server extensions (#317)
424424+* type equality (no longer generative error type), use result (#318)
425425+* removed Printer (was barely useful)
426426+427427+## 0.7.0 (2015-12-04)
428428+429429+* session resumption (via session ID) support (#283)
430430+ Config contains `session_cache : SessionID.t -> epoch_data option`
431431+ and `cached_session : epoch_data option`
432432+* session hash and extended master secret (RFC 7627) support (#287)
433433+434434+### semantic changes
435435+* disable renegotiation by default (#300)
436436+* blocking semantics (both Mirage and Lwt) while renegotiating (#304)
437437+* `Engine.handshake_in_progress` no longer exist
438438+* `Hex_fingerprint / `Fingerprint authenticators no longer exist
439439+* Mirage X509 does no longer prefix keys and trust anchors with "tls/" in the path
440440+441441+### minor fixes
442442+* fix concurrent read/write in tls_mirage (#303)
443443+* expose own_random and peer_random in epoch_data (@cfcs, #297)
444444+* public key pinning (X509_lwt) via `Hex_key_fingerprint / `Key_fingerprint (#301)
445445+* certificate chain and peer certificate are exposed via epoch_data (new path-building X.509 interface)
446446+447447+## 0.6.0 (2015-07-02)
448448+449449+* API: dropped 'perfect' from forward secrecy in Config.Ciphers:
450450+ fs instead of pfs, fs_of instead of pfs_of
451451+* API: type epoch_data moved from Engine to Core
452452+* removed Cstruct_s now that cstruct (since 1.6.0) provides
453453+ s-expression marshalling
454454+* require at least 1024 bit DH group, use FFDHE 2048 bit DH group
455455+ by default instead of oakley2 (logjam)
456456+* more specific alerts:
457457+ - UNRECOGNIZED_NAME: if hostname in SNI does not match
458458+ - UNSUPPORTED_EXTENSION: if server hello has an extension not present in
459459+ client hello
460460+ - ILLEGAL_PARAMETER: if a parse error occured
461461+* encrypt outgoing alerts
462462+* fix off-by-one in handling empty TLS records: if a record is less than 5
463463+ bytes, treat as a fragment. exactly 5 bytes might already be a valid
464464+ application data frame
465465+466466+## 0.5.0 (2015-05-02)
467467+468468+* updates to extension enum (contributed by Dave Garrett #264)
469469+* removed entropy feeding (done by nocrypto) #265
470470+* Tls_lwt file descriptor lifecycle: not eagerly close file descriptors #266
471471+472472+## 0.4.0 (2015-03-19)
473473+474474+* client authentication (both client and server side)
475475+* server side SNI configuration (see sni.md)
476476+* SCSV server-side downgrade prevention (by Gabriel de Perthuis @g2p #5)
477477+* remove RC4 ciphers from default config #8
478478+* support for AEAD ciphers, currently CCM #191
479479+* proper bounds checking of handshake fragments #255
480480+* disable application data between CCS and Finished #237
481481+* remove secure renegotiation configuration option #256
482482+* expose epoch in mirage interface, implement 2.3.0 API (error_message)
483483+* error reporting (type failure in engine.mli) #246
484484+* hook into Lwt event loop to feed RNG #254
485485+486486+## 0.3.0 (2014-12-21)
487487+488488+* X509_lwt provides `Fingerprints and `Hex_fingerprints constructor for
489489+ checking fingerprints of certificates instead of trusting trust
490490+ anchors #206 #207
491491+* client configuration requires an authenticator #202
492492+* server certificate must be at least Config.min_rsa_key_size bits
493493+* expose epoch via lwt interface #208
494494+* mirage-2.2.0 compatibility #212
495495+* cleanups of mirage interface #213
496496+* nocrypto-0.3.0 compatibility #194 #209 #210
497497+498498+## 0.2.0 (2014-10-30)
499499+500500+* distinguish between supported hash and mac algorithms (using Nocrypto.Hash)
501501+ and those which may occur on the wire #189
502502+* expose trust anchor when authenticating certificate (requires x509 >=0.2) #178
503503+* information about the active session is exposed via epoch : state -> epoch
504504+* distinguish between supported ciphersuites (type ciphersuite) and
505505+ known ciphersuites (type any_ciphersuite) #173
506506+* distinguish between supported versions by the stack (type tls_version)
507507+ and readable versions (tls_any_version), which might occur in a tls
508508+ record or client_hello read from the network #179 #172
509509+* support > TLS-1.2 client hellos (as reported by ssllabs.com #161)
510510+* support iOS 6 devices (who propose NULL ciphers - reported in #160)
511511+* send minimal protocol version in record layer of client hello
512512+ (maximum version is in the client hello itself) (RFC5246, E.1) #165
513513+514514+## 0.1.0 (2014-07-08)
515515+516516+* initial beta release
+23
vendor/opam/tls/LICENSE.md
···11+Copyright (c) 2014, David Kaloper and Hannes Mehnert
22+All rights reserved.
33+44+Redistribution and use in source and binary forms, with or without modification,
55+are permitted provided that the following conditions are met:
66+77+* Redistributions of source code must retain the above copyright notice, this
88+ list of conditions and the following disclaimer.
99+1010+* Redistributions in binary form must reproduce the above copyright notice, this
1111+ list of conditions and the following disclaimer in the documentation and/or
1212+ other materials provided with the distribution.
1313+1414+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
1515+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1616+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1717+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
1818+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1919+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2020+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
2121+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2222+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2323+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+89
vendor/opam/tls/README.md
···11+## TLS - Transport Layer Security purely in OCaml
22+33+%%VERSION%%
44+55+Transport Layer Security (TLS) is probably the most widely deployed security
66+protocol on the Internet. It provides communication privacy to prevent
77+eavesdropping, tampering, and message forgery. Furthermore, it optionally
88+provides authentication of the involved endpoints. TLS is commonly deployed for
99+securing web services ([HTTPS](http://tools.ietf.org/html/rfc2818)), emails,
1010+virtual private networks, and wireless networks.
1111+1212+TLS uses asymmetric cryptography to exchange a symmetric key, and optionally
1313+authenticate (using X.509) either or both endpoints. It provides algorithmic
1414+agility, which means that the key exchange method, symmetric encryption
1515+algorithm, and hash algorithm are negotiated.
1616+1717+Read our [Usenix Security 2015 paper](https://www.usenix.org/conference/usenixsecurity15/technical-sessions/presentation/kaloper-mersinjak) for further details.
1818+1919+## Documentation
2020+2121+[API documentation](https://mirleft.github.io/ocaml-tls/doc)
2222+2323+## Installation
2424+2525+`opam install tls` will install this library.
2626+2727+You can also build this locally by conducting the steps:
2828+2929+```bash
3030+opam install --deps-only -t . # or a named package instead of `.` - i.e. ./tls-lwt.opam
3131+dune build --profile=release # you can also put a package list here, i.e. tls,tls-lwt -- you can also use `@all` target to compile examples as well
3232+```
3333+3434+## Usage
3535+3636+The core of `ocaml-tls` (the opam package `tls`, available in the `lib`
3737+subdirectory) is an library independent of schedulers and does not perform any
3838+I/O operations. The library is designed so that a `Tls.Engine.state` state
3939+informs you of when to write and when to feed more data. It does not use
4040+mutation and is in a value-passing style (so, read data and state is the input,
4141+and data to be sent or presented to the upper layer, and state is the output).
4242+4343+There are therefore `ocaml-tls` derivations with different schedulers that
4444+perform read and write operations. These derivations offer an interface similar
4545+to what an SSL socket (like [ssl][ssl]) can offer.
4646+- [lwt](https://ocsigen.org/lwt/latest/manual/manual): `tls-lwt` proposes to
4747+ initiate a TLS flow with `Lwt_io.{input,output}_channel` from a Unix socket.
4848+ It can also propose an abstract type `Tls_lwt.Unix.t` (which can be created
4949+ from a Unix socket) associated with a `Tls_lwt.Unix` interface similar to a
5050+ Unix socket.
5151+- [miou](https://github.com/robur-coop/miou): `tls-miou-unix` proposes a TLS
5252+ flow via an abstract type `Tls_miou_unix.t` and an interface similar to a Unix
5353+ socket from a `Miou_unix.file_descr` socket.
5454+- [MirageOS](https://mirageos.org): `tls-mirage` proposes a composition
5555+ of a [`Mirage_flow.S`](https://github.com/mirage/mirage-flow/) module to
5656+ obtain a new `Mirage_flow.S` (corresponding to the TLS layer) which uses the
5757+ lwt scheduler.
5858+- [eio](https://github.com/ocaml-multicore/eio): `tls-eio` proposes the creation
5959+ of an _eio flow_ from another _eio flow_.
6060+- [async](https://github.com/janestreet/async): `tls-async` proposes a TLS flow
6161+ via `Async.{Reader,Writer}.t` from a `Async.Socket`.
6262+6363+Depending on the scheduler you choose, you should choose one of these
6464+`ocaml-tls` derivations, distributed in the mentioned opam packages (tls-lwt,
6565+tls-mirage, tls-eio, tls-async). Each one takes advantage of what the scheduler
6666+used has to offer.
6767+6868+### Composability
6969+7070+`ocaml-tls` can also be used as it is in order to be able to compose with other
7171+protocols without choosing a scheduler. This is the case, for example, with
7272+[sendmail.starttls][sendmail], which composes the SMTP and TLS protocols. The
7373+user can also be more selective about the use of certificates involved in a TLS
7474+connection, as [albatross][albatross] can offer in its transactions between
7575+clients and the server.
7676+7777+When seen as OCaml values, the critical elements that enable instantiation of a
7878+TLS connection can be very finely controlled.
7979+8080+### Portability
8181+8282+ocaml-tls is currently used for [MirageOS unikernels](https://mirageos.org),
8383+which makes it portable and available on many systems (even the most restricted
8484+ones such as [Solo5](https://github.com/solo5/solo5) as long as OCaml is
8585+available on them.
8686+8787+[sendmail]: https://github.com/mirage/colombe
8888+[albatross]: https://github.com/robur-coop/albatross
8989+[ssl]: https://github.com/savonet/ocaml-ssl
···11+open! Core
22+open! Async
33+open Deferred.Or_error.Let_syntax
44+55+let config = match Tls.Config.client ~authenticator:(fun ?ip:_ ~host:_ _ -> Ok None) () with
66+ | Ok cfg -> cfg
77+ | Error `Msg msg -> invalid_arg msg
88+99+let test_client () =
1010+ let host = "127.0.0.1" in
1111+ let port = 8443 in
1212+ let hnp = Host_and_port.create ~host ~port in
1313+ let%bind (_ : Tls_async.Session.t), rd, wr =
1414+ (* we can't build a [[ `host ] Domain_name.t] from an IP address *)
1515+ let host = None in
1616+ Tls_async.connect config (Tcp.Where_to_connect.of_host_and_port hnp) ~host
1717+ in
1818+ let req =
1919+ String.concat
2020+ ~sep:"\r\n"
2121+ [ "GET / HTTP/1.1"; "Host: " ^ host; "Connection: close"; ""; "" ]
2222+ in
2323+ Writer.write wr req;
2424+ let%bind () = Writer.flushed wr |> Deferred.ok in
2525+ let%bind () =
2626+ match%map Reader.read_line rd |> Deferred.ok with
2727+ | `Ok str -> print_endline str
2828+ | `Eof -> print_endline "Eof reached"
2929+ in
3030+ Writer.close wr |> Deferred.ok
3131+;;
3232+3333+let cmd = Command.async_or_error ~summary:"test client" (Command.Param.return test_client)
3434+let () = Command_unix.run cmd
+64
vendor/opam/tls/async/examples/test_server.ml
···11+open! Core
22+open! Async
33+44+let server_cert = "./certificates/server.pem"
55+let server_key = "./certificates/server.key"
66+77+let serve_tls ~low_level port handler =
88+ let%bind certificate =
99+ Tls_async.X509_async.Certificate.of_pem_file server_cert |> Deferred.Or_error.ok_exn
1010+ in
1111+ let%bind priv_key =
1212+ Tls_async.X509_async.Private_key.of_pem_file server_key |> Deferred.Or_error.ok_exn
1313+ in
1414+ let config =
1515+ match Tls.Config.(
1616+ server
1717+ ~version:(`TLS_1_0, `TLS_1_2)
1818+ ~certificates:(`Single (certificate, priv_key))
1919+ ~ciphers:Ciphers.supported
2020+ ())
2121+ with
2222+ | Ok cfg -> cfg
2323+ | Error `Msg msg -> invalid_arg msg
2424+ in
2525+ let where_to_listen = Tcp.Where_to_listen.of_port port in
2626+ let on_handler_error = `Ignore in
2727+ if low_level then
2828+ Tcp.Server.create
2929+ ~on_handler_error
3030+ where_to_listen
3131+ (fun sa ->
3232+ printf !"connection establised from %{Socket.Address.Inet} starting TLS\n" sa;
3333+ Tls_async.upgrade_server_handler ~config (handler sa))
3434+ else
3535+ Tls_async.listen ~on_handler_error config where_to_listen handler
3636+;;
3737+3838+let test_server ~low_level port =
3939+ let handler (_ : Socket.Address.Inet.t) (_ : Tls_async.Session.t) rd wr =
4040+ let pipe = Reader.pipe rd in
4141+ let rec read_from_pipe () =
4242+ (match%map Pipe.read pipe with
4343+ | `Ok line -> Writer.write wr line
4444+ | `Eof -> ())
4545+ >>= read_from_pipe
4646+ in
4747+ read_from_pipe ()
4848+ in
4949+ serve_tls ~low_level port handler
5050+;;
5151+5252+let cmd =
5353+ let open Command.Let_syntax in
5454+ Command.async
5555+ ~summary:"test server"
5656+ (let%map_open port = anon ("PORT" %: int)
5757+ and low_level = flag "-low-level" no_arg ~doc:"set up Tcp.server directly" in
5858+ fun () ->
5959+ let open Deferred.Let_syntax in
6060+ let%bind server = test_server ~low_level port in
6161+ Tcp.Server.close_finished server)
6262+;;
6363+6464+let () = Command_unix.run cmd
+200
vendor/opam/tls/async/io.ml
···11+open! Core
22+open! Async
33+include Io_intf
44+55+module Tls_error = struct
66+ module Alert = struct
77+ type t = Tls.Packet.alert_type
88+ let sexp_of_t a =
99+ Sexplib.Sexp.Atom (Tls.Packet.alert_type_to_string a)
1010+ end
1111+ module Fail = struct
1212+ type t = Tls.Engine.failure
1313+ let sexp_of_t a =
1414+ Sexplib.Sexp.Atom (Tls.Engine.string_of_failure a)
1515+ end
1616+ type t =
1717+ | Tls_alert of Alert.t
1818+ (** [Tls_alert] exception received from the other endpoint *)
1919+ | Tls_failure of Fail.t
2020+ (** [Tls_failure] exception while processing incoming data *)
2121+ | Connection_closed
2222+ | Connection_not_ready
2323+ | Unexpected_eof
2424+ | Unable_to_renegotiate
2525+ | Unable_to_update_key
2626+ [@@deriving sexp_of]
2727+end
2828+2929+module Make (Fd : Fd) : S with module Fd := Fd = struct
3030+ open Deferred.Or_error.Let_syntax
3131+3232+ module State = struct
3333+ type t =
3434+ | Active of Tls.Engine.state
3535+ | Eof
3636+ | Error of Tls_error.t
3737+ end
3838+3939+ type t =
4040+ { fd : Fd.t
4141+ ; mutable state : State.t
4242+ ; mutable linger : string option
4343+ ; recv_buf : bytes
4444+ }
4545+4646+ let tls_error = Fn.compose Deferred.Or_error.error_s Tls_error.sexp_of_t
4747+4848+ let rec read_react t =
4949+ let handle tls buf =
5050+ match Tls.Engine.handle_tls tls buf with
5151+ | Ok (state, eof, `Response resp, `Data data) ->
5252+ t.state
5353+ <- (match eof with
5454+ | None -> Active state
5555+ | Some `Eof -> Eof);
5656+ let%map () =
5757+ match resp with
5858+ | None -> return ()
5959+ | Some resp -> Fd.write_full t.fd resp
6060+ in
6161+ `Ok data
6262+ | Error (alert, `Response resp) ->
6363+ t.state <- Error (match alert with `Alert a -> Tls_alert a | f -> Tls_failure f);
6464+ let%bind () = Fd.write_full t.fd resp in
6565+ read_react t
6666+ in
6767+ match t.state with
6868+ | Error e -> tls_error e
6969+ | Eof -> return `Eof
7070+ | Active _ ->
7171+ let%bind n = Fd.read t.fd t.recv_buf in
7272+ (match t.state, n with
7373+ | Active _, `Eof ->
7474+ t.state <- Eof;
7575+ return `Eof
7676+ | Active tls, `Ok n -> handle tls (Stdlib.Bytes.sub_string t.recv_buf 0 n)
7777+ | Error e, _ -> tls_error e
7878+ | Eof, _ -> return `Eof)
7979+ ;;
8080+8181+ let rec read t buf =
8282+ let writeout res =
8383+ let rlen = String.length res in
8484+ let n = min (Bytes.length buf) rlen in
8585+ Stdlib.Bytes.blit_string res 0 buf 0 n;
8686+ t.linger <- (if n < rlen then Some (Stdlib.String.sub res n (rlen - n)) else None);
8787+ return n
8888+ in
8989+ match t.linger with
9090+ | Some res -> writeout res
9191+ | None ->
9292+ (match%bind read_react t with
9393+ | `Eof -> return 0
9494+ | `Ok None -> read t buf
9595+ | `Ok (Some res) -> writeout res)
9696+ ;;
9797+9898+ let writev t css =
9999+ match t.state with
100100+ | Error err -> tls_error err
101101+ | Eof -> tls_error Connection_closed
102102+ | Active tls ->
103103+ (match Tls.Engine.send_application_data tls css with
104104+ | Some (tls, tlsdata) ->
105105+ t.state <- Active tls;
106106+ Fd.write_full t.fd tlsdata
107107+ | None -> tls_error Connection_not_ready)
108108+ ;;
109109+110110+ (*
111111+ * XXX bad XXX
112112+ * This is a point that should particularly be protected from concurrent r/w.
113113+ * Doing this before a `t` is returned is safe; redoing it during rekeying is
114114+ * not, as the API client already sees the `t` and can mistakenly interleave
115115+ * writes while this is in progress.
116116+ * *)
117117+ let rec drain_handshake t =
118118+ let push_linger t mcs =
119119+ match mcs, t.linger with
120120+ | None, _ -> ()
121121+ | scs, None -> t.linger <- scs
122122+ | Some cs, Some l -> t.linger <- Some (l ^ cs)
123123+ in
124124+ match t.state with
125125+ | Active tls when not (Tls.Engine.handshake_in_progress tls) -> return t
126126+ | _ ->
127127+ (match%bind read_react t with
128128+ | `Eof -> tls_error Unexpected_eof
129129+ | `Ok cs ->
130130+ push_linger t cs;
131131+ drain_handshake t)
132132+ ;;
133133+134134+ let reneg ?authenticator ?acceptable_cas ?cert ?(drop = true) t =
135135+ match t.state with
136136+ | Error err -> tls_error err
137137+ | Eof -> tls_error Connection_closed
138138+ | Active tls ->
139139+ (match Tls.Engine.reneg ?authenticator ?acceptable_cas ?cert tls with
140140+ | None -> tls_error Unable_to_renegotiate
141141+ | Some (tls', buf) ->
142142+ if drop then t.linger <- None;
143143+ t.state <- Active tls';
144144+ let%bind () = Fd.write_full t.fd buf in
145145+ let%bind _ = drain_handshake t in
146146+ return ())
147147+ ;;
148148+149149+ let key_update ?request t =
150150+ match t.state with
151151+ | Error err -> tls_error err
152152+ | Eof -> tls_error Connection_closed
153153+ | Active tls ->
154154+ (match Tls.Engine.key_update ?request tls with
155155+ | Error _ -> tls_error Unable_to_update_key
156156+ | Ok (tls', buf) ->
157157+ t.state <- Active tls';
158158+ Fd.write_full t.fd buf)
159159+ ;;
160160+161161+ let close_tls t =
162162+ match t.state with
163163+ | Active tls ->
164164+ let _, buf = Tls.Engine.send_close_notify tls in
165165+ t.state <- Eof;
166166+ Fd.write_full t.fd buf
167167+ | _ -> return ()
168168+ ;;
169169+170170+ let server_of_fd config fd =
171171+ drain_handshake
172172+ { state = Active (Tls.Engine.server config)
173173+ ; fd
174174+ ; linger = None
175175+ ; recv_buf = Bytes.create 4096
176176+ }
177177+ ;;
178178+179179+ let client_of_fd config ?host fd =
180180+ let config' =
181181+ match host with
182182+ | None -> config
183183+ | Some host -> Tls.Config.peer config host
184184+ in
185185+ let t = { state = Eof; fd; linger = None; recv_buf = Bytes.create 4096 } in
186186+ let tls, init = Tls.Engine.client config' in
187187+ let t = { t with state = Active tls } in
188188+ let%bind () = Fd.write_full t.fd init in
189189+ drain_handshake t
190190+ ;;
191191+192192+ let epoch t =
193193+ match t.state with
194194+ | Active tls -> (match Tls.Engine.epoch tls with
195195+ | Ok _ as o -> o
196196+ | Error () -> Or_error.error_string "no TLS state available yet")
197197+ | Eof -> Or_error.error_string "TLS state is end of file"
198198+ | Error _ -> Or_error.error_string "TLS state is error"
199199+ ;;
200200+end
+6
vendor/opam/tls/async/io.mli
···11+open! Core
22+33+module type Fd = Io_intf.Fd
44+module type S = Io_intf.S
55+66+module Make (Fd : Fd) : S with module Fd := Fd
+64
vendor/opam/tls/async/io_intf.ml
···11+open! Core
22+open! Async
33+44+module type Fd = sig
55+ type t
66+77+ val read : t -> bytes -> [ `Ok of int | `Eof ] Deferred.Or_error.t
88+ val write_full : t -> string -> unit Deferred.Or_error.t
99+end
1010+1111+module type S = sig
1212+ module Fd : Fd
1313+1414+ (** Abstract type of a session *)
1515+ type t
1616+1717+ (** {2 Constructors} *)
1818+1919+ (** [server_of_fd server fd] is [t], after server-side TLS
2020+ handshake of [fd] using [server] configuration. *)
2121+ val server_of_fd : Tls.Config.server -> Fd.t -> t Deferred.Or_error.t
2222+2323+ (** [client_of_fd client ~host fd] is [t], after client-side
2424+ TLS handshake of [fd] using [client] configuration and [host]. *)
2525+ val client_of_fd
2626+ : Tls.Config.client
2727+ -> ?host:[ `host ] Domain_name.t
2828+ -> Fd.t
2929+ -> t Deferred.Or_error.t
3030+3131+ (** {2 Common stream operations} *)
3232+3333+ (** [read t buffer] is [length], the number of bytes read into
3434+ [buffer]. *)
3535+ val read : t -> bytes -> int Deferred.Or_error.t
3636+3737+ (** [writev t buffers] writes the [buffers] to the session. *)
3838+ val writev : t -> string list -> unit Deferred.Or_error.t
3939+4040+ (** [close t] closes the TLS session by sending a close notify to the peer. *)
4141+ val close_tls : t -> unit Deferred.Or_error.t
4242+4343+ (** [reneg ~authenticator ~acceptable_cas ~cert ~drop t] renegotiates the
4444+ session, and blocks until the renegotiation finished. Optionally, a new
4545+ [authenticator] and [acceptable_cas] can be used. The own certificate can
4646+ be adjusted by [cert]. If [drop] is [true] (the default),
4747+ application data received before the renegotiation finished is dropped. *)
4848+ val reneg
4949+ : ?authenticator:X509.Authenticator.t
5050+ -> ?acceptable_cas:X509.Distinguished_name.t list
5151+ -> ?cert:Tls.Config.own_cert
5252+ -> ?drop:bool
5353+ -> t
5454+ -> unit Deferred.Or_error.t
5555+5656+ (** [key_update ~request t] updates the traffic key and requests a traffic key
5757+ update from the peer if [request] is provided and [true] (the default).
5858+ This is only supported in TLS 1.3. *)
5959+ val key_update : ?request:bool -> t -> unit Deferred.Or_error.t
6060+6161+ (** [epoch t] returns [epoch], which contains information of the
6262+ active session. *)
6363+ val epoch : t -> Tls.Core.epoch_data Or_error.t
6464+end
+27
vendor/opam/tls/async/session.ml
···11+open! Core
22+open! Async
33+44+module Fd = struct
55+ type t = Reader.t * Writer.t
66+77+ let read (reader, (_ : Writer.t)) buf =
88+ Deferred.Or_error.try_with (fun () -> Reader.read reader buf)
99+ ;;
1010+1111+ let write ((_ : Reader.t), writer) buf =
1212+ Deferred.Or_error.try_with (fun () ->
1313+ Writer.write writer buf;
1414+ Writer.flushed writer)
1515+ ;;
1616+1717+ let rec write_full fd buf =
1818+ let open Deferred.Or_error.Let_syntax in
1919+ match String.length buf with
2020+ | 0 -> return ()
2121+ | len ->
2222+ let%bind () = write fd buf in
2323+ write_full fd (String.sub buf ~pos:len ~len:(String.length buf - len))
2424+ ;;
2525+end
2626+2727+include Io.Make (Fd)
+3
vendor/opam/tls/async/session.mli
···11+open! Core
22+open! Async
33+include Io.S with type Fd.t = Reader.t * Writer.t
+154
vendor/opam/tls/async/tls_async.ml
···11+open! Core
22+open! Async
33+module Session = Session
44+module X509_async = X509_async
55+66+let try_to_close t =
77+ match%map Session.close_tls t with
88+ | Ok () -> ()
99+ | Error tls_close_error -> Log.Global.error_s [%sexp (tls_close_error : Error.t)]
1010+;;
1111+1212+let pipe t =
1313+ let b_reader = Bytes.create 0x8000 in
1414+ let rec f_reader writer =
1515+ match%bind Session.read t b_reader with
1616+ | Ok 0 ->
1717+ Pipe.close writer;
1818+ return ()
1919+ | Ok len ->
2020+ let%bind () = Pipe.write writer (Stdlib.Bytes.sub_string b_reader 0 len) in
2121+ f_reader writer
2222+ | Error read_error ->
2323+ Log.Global.error_s [%sexp (read_error : Error.t)];
2424+ Pipe.close writer;
2525+ return ()
2626+ in
2727+ let rec f_writer reader =
2828+ let%bind pipe_read = Pipe.read reader in
2929+ match pipe_read with
3030+ | `Ok s ->
3131+ (match%bind Session.writev t [ s ] with
3232+ | Ok () -> f_writer reader
3333+ | Error (_ : Error.t) -> try_to_close t)
3434+ | `Eof -> try_to_close t
3535+ in
3636+ Pipe.create_reader ~close_on_exception:false f_reader, Pipe.create_writer f_writer
3737+;;
3838+3939+let upgrade_connection tls_session ((_ : Reader.t), outer_writer) =
4040+ let pipe_r, pipe_w = pipe tls_session in
4141+ let%bind inner_reader = Reader.of_pipe (Info.of_string "tls_reader") pipe_r in
4242+ let%map inner_writer, `Closed_and_flushed_downstream inner_cafd =
4343+ Writer.of_pipe (Info.of_string "tls_writer") pipe_w
4444+ in
4545+ Writer.set_raise_when_consumer_leaves inner_writer false;
4646+ let outer_cafd =
4747+ (* Ordering is important here to ensure no data is lost during the session shutdown *)
4848+ let%bind () = Writer.close_finished inner_writer in
4949+ let%bind () = inner_cafd in
5050+ let%bind () = try_to_close tls_session in
5151+ Writer.flushed outer_writer
5252+ in
5353+ tls_session, inner_reader, inner_writer, `Tls_closed_and_flushed_downstream outer_cafd
5454+;;
5555+5656+let upgrade_server_reader_writer_to_tls config rw =
5757+ let open Deferred.Or_error.Let_syntax in
5858+ let%bind tls_session = Session.server_of_fd config rw in
5959+ upgrade_connection tls_session rw |> Deferred.ok
6060+;;
6161+6262+let upgrade_client_reader_writer_to_tls ?host config rw =
6363+ let open Deferred.Or_error.Let_syntax in
6464+ let%bind tls_session = Session.client_of_fd ?host config rw in
6565+ upgrade_connection tls_session rw |> Deferred.ok
6666+;;
6767+6868+type 'a io_handler = Reader.t -> Writer.t -> 'a Deferred.t
6969+type 'a tls_handler = Session.t -> 'a io_handler
7070+7171+let upgrade_server_handler ~config handle_client outer_reader outer_writer =
7272+ let%bind ( tls_session
7373+ , inner_reader
7474+ , inner_writer
7575+ , `Tls_closed_and_flushed_downstream inner_cafd )
7676+ =
7777+ upgrade_server_reader_writer_to_tls config (outer_reader, outer_writer)
7878+ |> Deferred.Or_error.ok_exn
7979+ in
8080+ Monitor.protect
8181+ (fun () -> handle_client tls_session inner_reader inner_writer)
8282+ ~finally:(fun () ->
8383+ Deferred.all_unit
8484+ [ Reader.close inner_reader; Writer.close inner_writer; inner_cafd ])
8585+;;
8686+8787+let listen
8888+ ?buffer_age_limit
8989+ ?max_connections
9090+ ?max_accepts_per_batch
9191+ ?backlog
9292+ ?socket
9393+ ~on_handler_error
9494+ config
9595+ where_to_listen
9696+ handle_client
9797+ =
9898+ Tcp.Server.create
9999+ ?buffer_age_limit
100100+ ?max_connections
101101+ ?max_accepts_per_batch
102102+ ?backlog
103103+ ?socket
104104+ ~on_handler_error
105105+ where_to_listen
106106+ (fun sock ->
107107+ upgrade_server_handler ~config (handle_client sock))
108108+;;
109109+110110+let upgrade_client_to_tls config ~host outer_reader outer_writer =
111111+ let open Deferred.Or_error.Let_syntax in
112112+ let%bind ( tls_session
113113+ , inner_reader
114114+ , inner_writer
115115+ , `Tls_closed_and_flushed_downstream inner_cafd )
116116+ =
117117+ upgrade_client_reader_writer_to_tls ?host config (outer_reader, outer_writer)
118118+ in
119119+ don't_wait_for
120120+ (let%bind.Deferred () = inner_cafd in
121121+ Deferred.all_unit [ Writer.close outer_writer; Reader.close outer_reader ]);
122122+ return (tls_session, inner_reader, inner_writer)
123123+;;
124124+125125+let connect
126126+ ?socket
127127+ ?buffer_age_limit
128128+ ?interrupt
129129+ ?reader_buffer_size
130130+ ?writer_buffer_size
131131+ ?timeout
132132+ ?time_source
133133+ config
134134+ where_to_connect
135135+ ~host
136136+ =
137137+ let open Deferred.Or_error.Let_syntax in
138138+ let%bind (_ : ([ `Active ], 'a) Socket.t), outer_reader, outer_writer =
139139+ Tcp.connect
140140+ ?socket
141141+ ?buffer_age_limit
142142+ ?interrupt
143143+ ?reader_buffer_size
144144+ ?writer_buffer_size
145145+ ?timeout
146146+ ?time_source
147147+ where_to_connect
148148+ |> Deferred.ok
149149+ in
150150+ upgrade_client_to_tls ~host config outer_reader outer_writer
151151+;;
152152+153153+(* initialized RNG early to maximise available entropy. *)
154154+let () = Mirage_crypto_rng_unix.use_default ()
+74
vendor/opam/tls/async/tls_async.mli
···11+open! Core
22+open! Async
33+44+(** Low-level API for working with TLS sessions.
55+ Most applications should use the high-level API below *)
66+module Session = Session
77+88+(** Helper functions for [Async_unix]-specific IO operations commonly used with X509
99+ certificates, such as loading from a Unix filesystem *)
1010+module X509_async = X509_async
1111+1212+(** [listen] creates a [Tcp.Server.t] with the requested parameters, including those
1313+ specified in [Tls.Config.server]. The handler function exposes the low-level
1414+ [Session.t] to accommodate cases like interrogating a client certificate *)
1515+val listen
1616+ : ?buffer_age_limit:Writer.buffer_age_limit
1717+ -> ?max_connections:int (** defaults to [10_000]. *)
1818+ -> ?max_accepts_per_batch:int (** defaults to [1]. *)
1919+ -> ?backlog:int (** defaults to [64]. *)
2020+ -> ?socket:([ `Unconnected ], ([< Socket.Address.t ] as 'address)) Socket.t
2121+ -> on_handler_error:[ `Call of 'address -> exn -> unit | `Ignore | `Raise ]
2222+ -> Tls.Config.server
2323+ -> ('address, 'listening_on) Tcp.Where_to_listen.t
2424+ -> ('address -> Session.t -> Reader.t -> Writer.t -> unit Deferred.t)
2525+ -> ('address, 'listening_on) Tcp.Server.t Deferred.t
2626+2727+type 'a io_handler = Reader.t -> Writer.t -> 'a Deferred.t
2828+type 'a tls_handler = Session.t -> 'a io_handler
2929+3030+(** [upgrade_server_handler] is what [listen] calls to handle each client.
3131+ It is exposed so that low-level end-users of the library can use tls-async
3232+ inside of code that manages Tcp services directly.
3333+3434+ The [tls_handler] argument will be called with the client Tls session,
3535+ reader and writer to be used for cleartext data.
3636+3737+ The outer [reader] and [writer] will read encrypted data from and write
3838+ encrypted data to the connected socket. *)
3939+val upgrade_server_handler
4040+ : config:Tls.Config.server
4141+ -> 'a tls_handler
4242+ -> 'a io_handler
4343+4444+(** [connect] behaves similarly to [Tcp.connect], exposing a cleartext reader and writer.
4545+ Callers should ensure they close the [Writer.t] and wait for the [unit Deferred.t]
4646+ returned by [`Closed_and_flushed_downstream] to completely shut down the TLS connection
4747+4848+ [host] is used for peer name verification and should generally be provided. Passing
4949+ [None] will disable peer name verification unless [peer_name] was provided in the
5050+ [Tls.Config.client]. If both are present [host] overwrites [peer_name].
5151+*)
5252+val connect
5353+ : ?socket:([ `Unconnected ], 'addr) Socket.t
5454+ -> (Tls.Config.client
5555+ -> 'addr Tcp.Where_to_connect.t
5656+ -> host:[ `host ] Domain_name.t option
5757+ -> (Session.t * Reader.t * Writer.t) Deferred.Or_error.t)
5858+ Tcp.Aliases.with_connect_options
5959+6060+(** [upgrade_client_to_tls] upgrades an existing reader/writer to TLS,
6161+ returning a cleartext reader and writer.
6262+ Callers should ensure they close the [Writer.t] and wait for the [unit Deferred.t]
6363+ returned by [`Closed_and_flushed_downstream] to completely shut down the TLS connection
6464+6565+ [host] is used for peer name verification and should generally be provided. Passing
6666+ [None] will disable peer name verification unless [peer_name] was provided in the
6767+ [Tls.Config.client]. If both are present [host] overwrites [peer_name].
6868+*)
6969+val upgrade_client_to_tls
7070+ : Tls.Config.client
7171+ -> host:[ `host ] Domain_name.t option
7272+ -> Reader.t
7373+ -> Writer.t
7474+ -> (Session.t * Reader.t * Writer.t) Deferred.Or_error.t
+271
vendor/opam/tls/async/x509_async.ml
···11+open! Core
22+open! Async
33+44+let file_contents file =
55+ Deferred.Or_error.try_with ~name:(sprintf "read %s" file) (fun () ->
66+ Reader.file_contents file)
77+;;
88+99+let load_all_in_directory ~directory ~f =
1010+ let open Deferred.Or_error.Let_syntax in
1111+ let%bind files = Deferred.Or_error.try_with (fun () -> Sys.ls_dir directory) in
1212+ Deferred.Or_error.List.map ~how:`Sequential files ~f:(fun file ->
1313+ let%bind contents = file_contents (directory ^/ file) in
1414+ f ~contents)
1515+;;
1616+1717+module Or_error = struct
1818+ include Or_error
1919+2020+ let of_result ~to_string = Result.map_error ~f:(Fn.compose Error.of_string to_string)
2121+ let of_result_msg x = of_result x ~to_string:(fun (`Msg msg) -> msg)
2222+2323+ let lift_result_msg_of_string f ~contents =
2424+ f contents |> of_result_msg
2525+ ;;
2626+2727+ let lift_asn_error_of_string f ~contents =
2828+ f contents |> of_result ~to_string:(fun (`Parse msg) -> msg)
2929+ ;;
3030+end
3131+3232+module CRL = struct
3333+ include X509.CRL
3434+3535+ let decode_der = Or_error.lift_result_msg_of_string decode_der
3636+3737+ let revoke ?digest ~issuer ~this_update ?next_update ?extensions revoked_certs key =
3838+ revoke ?digest ~issuer ~this_update ?next_update ?extensions revoked_certs key
3939+ |> Or_error.of_result_msg
4040+ ;;
4141+4242+ let revoke_certificate revoked ~this_update ?next_update crl key =
4343+ revoke_certificate revoked ~this_update ?next_update crl key |> Or_error.of_result_msg
4444+ ;;
4545+4646+ let revoke_certificates revoked ~this_update ?next_update crl key =
4747+ revoke_certificates revoked ~this_update ?next_update crl key
4848+ |> Or_error.of_result_msg
4949+ ;;
5050+5151+ let of_pem_dir ~directory =
5252+ load_all_in_directory ~directory ~f:(fun ~contents ->
5353+ decode_der ~contents |> Deferred.return)
5454+ ;;
5555+end
5656+5757+module Certificate = struct
5858+ include X509.Certificate
5959+ open Deferred.Or_error.Let_syntax
6060+6161+ let decode_pem_multiple = Or_error.lift_result_msg_of_string decode_pem_multiple
6262+ let decode_pem = Or_error.lift_result_msg_of_string decode_pem
6363+ let decode_der = Or_error.lift_result_msg_of_string decode_der
6464+6565+ let of_pem_file ca_file =
6666+ let%bind contents = file_contents ca_file in
6767+ decode_pem_multiple ~contents |> Deferred.return
6868+ ;;
6969+7070+ let of_pem_directory ~directory =
7171+ load_all_in_directory ~directory ~f:(fun ~contents ->
7272+ decode_pem_multiple ~contents |> Deferred.return)
7373+ >>| List.concat
7474+ ;;
7575+end
7676+7777+module Authenticator = struct
7878+ include X509.Authenticator
7979+8080+ module Param = struct
8181+ module Chain_of_trust = struct
8282+ type t =
8383+ { trust_anchors : [ `File of Filename.t | `Directory of Filename.t ]
8484+ ; allowed_hashes : Digestif.hash' list option
8585+ ; crls : Filename.t option
8686+ }
8787+8888+ let to_certs = function
8989+ | `File file -> Certificate.of_pem_file file
9090+ | `Directory directory -> Certificate.of_pem_directory ~directory
9191+ ;;
9292+ end
9393+9494+ type t =
9595+ | Chain_of_trust of Chain_of_trust.t
9696+ | Cert_fingerprint of Digestif.hash' * string
9797+ | Key_fingerprint of Digestif.hash' * string
9898+9999+ let ca_file ?allowed_hashes ?crls filename () =
100100+ let trust_anchors = `File filename in
101101+ Chain_of_trust { trust_anchors; allowed_hashes; crls }
102102+ ;;
103103+104104+ let ca_dir ?allowed_hashes ?crls directory_name () =
105105+ let trust_anchors = `Directory directory_name in
106106+ Chain_of_trust { trust_anchors; allowed_hashes; crls }
107107+ ;;
108108+109109+ let cert_fingerprint hash fingerprint = Cert_fingerprint (hash, fingerprint)
110110+111111+ let key_fingerprint hash fingerprint = Key_fingerprint (hash, fingerprint)
112112+113113+ let cleanup_fingerprint fingerprint =
114114+ let known_delimiters = [ ':'; ' ' ] in
115115+ String.filter fingerprint ~f:(fun c ->
116116+ not (List.exists known_delimiters ~f:(Char.equal c)))
117117+ |> Ohex.decode
118118+ ;;
119119+120120+ let of_cas ~time ({ trust_anchors; allowed_hashes; crls } : Chain_of_trust.t) =
121121+ let open Deferred.Or_error.Let_syntax in
122122+ let%bind cas = Chain_of_trust.to_certs trust_anchors in
123123+ let%map crls =
124124+ match crls with
125125+ | Some directory ->
126126+ let%map crls = CRL.of_pem_dir ~directory in
127127+ Some crls
128128+ | None -> return None
129129+ in
130130+ X509.Authenticator.chain_of_trust ?allowed_hashes ?crls ~time cas
131131+ ;;
132132+133133+ let of_cert_fingerprint ~time hash fingerprint =
134134+ let fingerprint = cleanup_fingerprint fingerprint in
135135+ X509.Authenticator.cert_fingerprint ~time ~hash ~fingerprint
136136+ ;;
137137+138138+ let of_key_fingerprint ~time hash fingerprint =
139139+ let fingerprint = cleanup_fingerprint fingerprint in
140140+ X509.Authenticator.key_fingerprint ~time ~hash ~fingerprint
141141+ ;;
142142+143143+ let time = Fn.compose Ptime.of_float_s Unix.gettimeofday
144144+145145+ let to_authenticator ~time param =
146146+ match param with
147147+ | Chain_of_trust chain_of_trust -> of_cas ~time chain_of_trust
148148+ | Cert_fingerprint (hash, fingerprint) ->
149149+ of_cert_fingerprint ~time hash fingerprint |> Deferred.Or_error.return
150150+ | Key_fingerprint (hash, fingerprint) ->
151151+ of_key_fingerprint ~time hash fingerprint |> Deferred.Or_error.return
152152+ ;;
153153+ end
154154+end
155155+156156+module Distinguished_name = struct
157157+ include X509.Distinguished_name
158158+159159+ let decode_der = Or_error.lift_result_msg_of_string decode_der
160160+end
161161+162162+module OCSP = struct
163163+ include X509.OCSP
164164+165165+ module Request = struct
166166+ include Request
167167+168168+ let create ?certs ?digest ?requestor_name ?key cert_ids =
169169+ create ?certs ?digest ?requestor_name ?key cert_ids |> Or_error.of_result_msg
170170+ ;;
171171+172172+ let decode_der = Or_error.lift_asn_error_of_string decode_der
173173+ end
174174+175175+ module Response = struct
176176+ include Response
177177+178178+ let create_success
179179+ ?digest
180180+ ?certs
181181+ ?response_extensions
182182+ private_key
183183+ responderID
184184+ producedAt
185185+ responses
186186+ =
187187+ create_success
188188+ ?digest
189189+ ?certs
190190+ ?response_extensions
191191+ private_key
192192+ responderID
193193+ producedAt
194194+ responses
195195+ |> Or_error.of_result_msg
196196+ ;;
197197+198198+ let responses t = responses t |> Or_error.of_result_msg
199199+ let decode_der = Or_error.lift_asn_error_of_string decode_der
200200+ end
201201+end
202202+203203+module PKCS12 = struct
204204+ include X509.PKCS12
205205+206206+ let decode_der = Or_error.lift_result_msg_of_string decode_der
207207+ let verify password t = verify password t |> Or_error.of_result_msg
208208+end
209209+210210+module Private_key = struct
211211+ include X509.Private_key
212212+213213+ let sign hash ?scheme key data =
214214+ sign hash ?scheme key data
215215+ |> Or_error.of_result_msg
216216+ ;;
217217+218218+ let decode_der = Or_error.lift_result_msg_of_string decode_der
219219+ let decode_pem = Or_error.lift_result_msg_of_string decode_pem
220220+221221+ let of_pem_file file =
222222+ let%map contents = Reader.file_contents file in
223223+ decode_pem ~contents
224224+ ;;
225225+end
226226+227227+module Public_key = struct
228228+ include X509.Public_key
229229+230230+ let verify hash ?scheme ~signature key data =
231231+ verify hash ?scheme ~signature key data |> Or_error.of_result_msg
232232+ ;;
233233+234234+ let decode_der = Or_error.lift_result_msg_of_string decode_der
235235+ let decode_pem = Or_error.lift_result_msg_of_string decode_pem
236236+end
237237+238238+module Signing_request = struct
239239+ include X509.Signing_request
240240+241241+ let decode_der ?allowed_hashes der =
242242+ decode_der ?allowed_hashes der |> Or_error.of_result_msg
243243+ ;;
244244+245245+ let decode_pem pem = decode_pem pem |> Or_error.of_result_msg
246246+247247+ let create subject ?digest ?extensions key =
248248+ create subject ?digest ?extensions key |> Or_error.of_result_msg
249249+ ;;
250250+251251+ let sign
252252+ ?allowed_hashes
253253+ ?digest
254254+ ?serial
255255+ ?extensions
256256+ t
257257+ key
258258+ issuer
259259+ ~valid_from
260260+ ~valid_until
261261+ =
262262+ sign ?allowed_hashes ?digest ?serial ?extensions t key issuer ~valid_from ~valid_until
263263+ |> Or_error.of_result ~to_string:(Fmt.to_to_string X509.Validation.pp_signature_error)
264264+ ;;
265265+end
266266+267267+module Extension = X509.Extension
268268+module General_name = X509.General_name
269269+module Host = X509.Host
270270+module Key_type = X509.Key_type
271271+module Validation = X509.Validation
+231
vendor/opam/tls/async/x509_async.mli
···11+open! Core
22+open! Async
33+44+include module type of struct
55+ include X509
66+end
77+88+module Authenticator : sig
99+ include module type of struct
1010+ include Authenticator
1111+ end
1212+1313+ module Param : sig
1414+ type t
1515+1616+ val ca_file
1717+ : ?allowed_hashes:Digestif.hash' list
1818+ -> ?crls:Filename.t
1919+ -> Filename.t
2020+ -> unit
2121+ -> t
2222+2323+ val ca_dir
2424+ : ?allowed_hashes:Digestif.hash' list
2525+ -> ?crls:Filename.t
2626+ -> Filename.t
2727+ -> unit
2828+ -> t
2929+3030+ (** The fingerprint can be collected from a browser or by invoking an openssl command
3131+ like 'openssl x509 -in <pem_file> -noout -fingerprint -sha256' *)
3232+ val cert_fingerprint
3333+ : Digestif.hash'
3434+ -> string
3535+ -> t
3636+3737+ (** The fingerprint can be collected from a browser or by invoking an openssl command
3838+ like 'openssl x509 -in <pem_file> -noout -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256' *)
3939+ val key_fingerprint
4040+ : Digestif.hash'
4141+ -> string
4242+ -> t
4343+4444+ (** Async programs often don't use [Ptime_clock], so this is provided as a convenience
4545+ function. Relies on [Unix.gettimeofday]. *)
4646+ val time : unit -> Ptime.t option
4747+4848+ val to_authenticator
4949+ : time:(unit -> Ptime.t option)
5050+ -> t
5151+ -> Authenticator.t Deferred.Or_error.t
5252+ end
5353+end
5454+5555+module Private_key : sig
5656+ include module type of struct
5757+ include Private_key
5858+ end
5959+6060+ val sign
6161+ : Digestif.hash'
6262+ -> ?scheme:Key_type.signature_scheme
6363+ -> t
6464+ -> [ `Digest of string | `Message of string ]
6565+ -> string Or_error.t
6666+6767+ val decode_der : contents:string -> t Or_error.t
6868+ val decode_pem : contents:string -> t Or_error.t
6969+ val of_pem_file : Filename.t -> t Deferred.Or_error.t
7070+end
7171+7272+module Public_key : sig
7373+ include module type of struct
7474+ include Public_key
7575+ end
7676+7777+ val verify
7878+ : Digestif.hash'
7979+ -> ?scheme:Key_type.signature_scheme
8080+ -> signature:string
8181+ -> t
8282+ -> [ `Digest of string | `Message of string ]
8383+ -> unit Or_error.t
8484+8585+ val decode_der : contents:string -> t Or_error.t
8686+ val decode_pem : contents:string -> t Or_error.t
8787+end
8888+8989+module Certificate : sig
9090+ include module type of struct
9191+ include Certificate
9292+ end
9393+9494+ val decode_pem_multiple : contents:string -> t list Or_error.t
9595+ val decode_pem : contents:string -> t Or_error.t
9696+ val decode_der : contents:string -> t Or_error.t
9797+ val of_pem_file : Filename.t -> t list Deferred.Or_error.t
9898+ val of_pem_directory : directory:Filename.t -> t list Deferred.Or_error.t
9999+end
100100+101101+module Distinguished_name : sig
102102+ include module type of struct
103103+ include Distinguished_name
104104+ end
105105+106106+ val decode_der : contents:string -> t Or_error.t
107107+end
108108+109109+module CRL : sig
110110+ include module type of struct
111111+ include CRL
112112+ end
113113+114114+ val decode_der : contents:string -> t Or_error.t
115115+116116+ val revoke
117117+ : ?digest:Digestif.hash'
118118+ -> issuer:Distinguished_name.t
119119+ -> this_update:Ptime.t
120120+ -> ?next_update:Ptime.t
121121+ -> ?extensions:Extension.t
122122+ -> revoked_cert list
123123+ -> Private_key.t
124124+ -> t Or_error.t
125125+126126+ val revoke_certificate
127127+ : revoked_cert
128128+ -> this_update:Ptime.t
129129+ -> ?next_update:Ptime.t
130130+ -> t
131131+ -> Private_key.t
132132+ -> t Or_error.t
133133+134134+ val revoke_certificates
135135+ : revoked_cert list
136136+ -> this_update:Ptime.t
137137+ -> ?next_update:Ptime.t
138138+ -> t
139139+ -> Private_key.t
140140+ -> t Or_error.t
141141+142142+ val of_pem_dir : directory:Filename.t -> t list Deferred.Or_error.t
143143+end
144144+145145+module OCSP : sig
146146+ include module type of struct
147147+ include OCSP
148148+ end
149149+150150+ module Request : sig
151151+ include module type of struct
152152+ include Request
153153+ end
154154+155155+ val create
156156+ : ?certs:Certificate.t list
157157+ -> ?digest:Digestif.hash'
158158+ -> ?requestor_name:General_name.b
159159+ -> ?key:Private_key.t
160160+ -> cert_id list
161161+ -> t Or_error.t
162162+163163+ val decode_der : contents:string -> t Or_error.t
164164+ end
165165+166166+ module Response : sig
167167+ include module type of struct
168168+ include Response
169169+ end
170170+171171+ val create_success
172172+ : ?digest:Digestif.hash'
173173+ -> ?certs:Certificate.t list
174174+ -> ?response_extensions:Extension.t
175175+ -> Private_key.t
176176+ -> responder_id
177177+ -> Ptime.t
178178+ -> single_response list
179179+ -> t Or_error.t
180180+181181+ val responses : t -> single_response list Or_error.t
182182+ val decode_der : contents:string -> t Or_error.t
183183+ end
184184+end
185185+186186+module PKCS12 : sig
187187+ include module type of struct
188188+ include PKCS12
189189+ end
190190+191191+ val decode_der : contents:string -> t Or_error.t
192192+193193+ val verify
194194+ : string
195195+ -> t
196196+ -> [ `Certificate of Certificate.t
197197+ | `Crl of CRL.t
198198+ | `Decrypted_private_key of Private_key.t
199199+ | `Private_key of Private_key.t
200200+ ]
201201+ list
202202+ Or_error.t
203203+end
204204+205205+module Signing_request : sig
206206+ include module type of struct
207207+ include Signing_request
208208+ end
209209+210210+ val decode_der : ?allowed_hashes:Digestif.hash' list -> string -> t Or_error.t
211211+ val decode_pem : string -> t Or_error.t
212212+213213+ val create
214214+ : Distinguished_name.t
215215+ -> ?digest:Digestif.hash'
216216+ -> ?extensions:Ext.t
217217+ -> Private_key.t
218218+ -> t Or_error.t
219219+220220+ val sign
221221+ : ?allowed_hashes:Digestif.hash' list
222222+ -> ?digest:Digestif.hash'
223223+ -> ?serial:string
224224+ -> ?extensions:Extension.t
225225+ -> t
226226+ -> Private_key.t
227227+ -> Distinguished_name.t
228228+ -> valid_from:Ptime.t
229229+ -> valid_until:Ptime.t
230230+ -> Certificate.t Or_error.t
231231+end
+293
vendor/opam/tls/attacks.md
···11+### Attacks on TLS
22+33+TLS the most widely deployed security protocol on the Internet and, at
44+over 15 years, is also showing its age. As such, a flaw is a valuable
55+commodity due to the commercially sensitive nature of data that is
66+encrypted with TLS. Various vulnerabilities on different layers of TLS
77+have been found - [heartbleed][] and others are implementation
88+specific, advancements in cryptanalysis such as [collisions of
99+MD5][md5_collision] lead to vulnerabilities, and even others are due
1010+to incorrect usage of TLS ([truncation attack][truncation] or
1111+[BREACH][breach]). Finally, some weaknesses are in the protocol
1212+itself. Extensive [overviews][tls_attacks] of [attacks on
1313+TLS][mitls_attacks] are available.
1414+1515+We look at protocol level attacks of TLS and how [ocaml-tls][ocaml-tls]
1616+implements mitigations against these. [TLS 1.2 RFC][RFC5246] provides an
1717+overview of attacks and mitigations, and we [track][issue31] our progress in
1818+covering them. This is slightly out of date as the RFC is roughly six years old and
1919+in the meantime more attacks have been published, such as the [renegotiation
2020+flaw][understanding_reneg].
2121+2222+We track all our [mitigated][closed] and [open][open] security issues
2323+on our GitHub issue tracker.
2424+2525+Due to the choice of using OCaml, a memory managed programming
2626+language, we obstruct entire bug classes, namely temporal and spatial
2727+memory safety.
2828+2929+Cryptanalysis and improvement of computational power weaken some
3030+ciphers, such as RC4 and 3DES (see [issue 8][issue8] and [issue
3131+10][issue10]). If we phase these two ciphers out, there wouldn't be
3232+any matching ciphersuite left to communicate with some compliant TLS-1.0
3333+implementations, such as Windows XP, that do not support AES.
3434+3535+[issue8]: https://github.com/mirleft/ocaml-tls/issues/8
3636+[issue10]: https://github.com/mirleft/ocaml-tls/issues/10
3737+[open]: https://github.com/mirleft/ocaml-tls/issues?labels=security+concern&page=1&state=open
3838+[closed]: https://github.com/mirleft/ocaml-tls/issues?labels=security+concern&page=1&state=closed
3939+[ocaml-tls]: https://github.com/mirleft/ocaml-tls
4040+[understanding_reneg]: http://www.educatedguesswork.org/2009/11/understanding_the_tls_renegoti.html
4141+[heartbleed]: https://en.wikipedia.org/wiki/Heartbleed
4242+[md5_collision]: http://eprint.iacr.org/2005/067
4343+[truncation]: http://www.theregister.co.uk/2013/08/01/gmail_hotmail_hijacking/
4444+[breach]: http://breachattack.com/
4545+[RFC5246]: https://tools.ietf.org/html/rfc5246#appendix-D.4
4646+[tls_attacks]: http://eprint.iacr.org/2013/049.pdf
4747+[mitls_attacks]: http://www.mitls.org/wsgi/tls-attacks
4848+[issue31]: https://github.com/mirleft/ocaml-tls/issues/31
4949+5050+**Timing attacks**
5151+5252+When the timing characteristics between the common case and the error
5353+case are different, this might potentially leak confidential
5454+information. Timing is a very prominent side-channel and there are a huge
5555+variety of timing attacks on different layers, which are observable by
5656+different attackers. Small differences in timing behaviour might
5757+initially be exploitable only by a local attacker, but advancements to
5858+the attack (e.g. increasing the number of tests) might allow a
5959+remote attacker to filter the noise and exploit the different timing
6060+behaviour.
6161+6262+**Timing of cryptographic primitives**
6363+6464+We [already mentioned][nocrypto-intro] [cache][] [timing][cache_timing]
6565+attacks on our AES implementation, and that we use [blinding][]
6666+techniques to mitigate RSA timing attacks.
6767+6868+By using a memory managed programming language, we open the attack
6969+vector of garbage collector (GC) timing attacks (also mentioned [in
7070+our nocrypto introduction][nocrypto-intro]).
7171+7272+Furthermore, research has been done on virtual machine side channels
7373+([l3][], [cross vm][cross_vm] and [cache timing][cache_vm]), which we
7474+will need to study and mitigate appropriately.
7575+7676+**For the time being we suggest to not use the stack on a multi-tenant
7777+shared host or on a shared host which malicious users might have
7878+access to.**
7979+8080+[blinding]: https://en.wikipedia.org/wiki/Blinding_(cryptography)
8181+[cache]: http://www.cs.tau.ac.il/~tromer/papers/cache.pdf
8282+[cache_timing]: http://cr.yp.to/antiforgery/cachetiming-20050414.pdf
8383+[l3]: http://eprint.iacr.org/2013/448.pdf
8484+[cross_vm]: http://www.cs.unc.edu/~reiter/papers/2012/CCS.pdf
8585+[cache_vm]: http://fc12.ifca.ai/pre-proceedings/paper_70.pdf
8686+8787+**Bleichenbacher**
8888+8989+In 1998, Daniel Bleichenbacher discovered a [timing flaw in the
9090+PKCS1][bleichenbacher] encoding of the premaster secret: the TLS server
9191+failed faster when the padding was wrong than when the decryption
9292+failed. Using this timing, an attacker can run an adaptive chosen
9393+ciphertext attack and find out the plain text of a PKCS1 encrypted
9494+message. In TLS, when RSA is used as the key exchange method, this
9595+leads to discovery of the premaster secret, which is used to derive the
9696+keys for the current session.
9797+9898+The mitigation is to have both padding and decryption failures use the
9999+exact same amount of time, thus there should not be any data-dependent
100100+branches or different memory access patterns in the code. We
101101+implemented this mitigation in [Handshake_server][answer_client_key_exchange].
102102+103103+[bleichenbacher]: http://archiv.infsec.ethz.ch/education/fs08/secsem/Bleichenbacher98.pdf
104104+[answer_client_key_exchange]: https://github.com/mirleft/ocaml-tls/blob/c06cbaaffe49024d8570916b70f7839603a54692/lib/handshake_server.ml#L45
105105+106106+**Padding oracle and CBC timing**
107107+108108+[Vaudenay][] discovered a vulnerability involving block ciphers: if an
109109+attacker can distinguish between bad mac and bad padding, recovery of
110110+the plaintext is possible (within an adaptive chosen ciphertext
111111+attack). Another approach using the same issue is to use
112112+[timing][practical] information instead of separate error messages.
113113+Further details are described [here][tls_cbc].
114114+115115+The countermeasure, which we implement [here][cbc_mit], is to continue
116116+with the mac computation even though the padding is
117117+incorrect. Furthermore, we send the same alert (`bad_record_mac`)
118118+independent of whether the padding is malformed or the mac is
119119+incorrect.
120120+121121+[tls_cbc]: https://www.openssl.org/~bodo/tls-cbc.txt
122122+[Vaudenay]: http://www.iacr.org/archive/eurocrypt2002/23320530/cbc02_e02d.pdf
123123+[practical]: http://lasecwww.epfl.ch/memo/memo_ssl.shtml
124124+[cbc_mit]: https://github.com/mirleft/ocaml-tls/blob/c06cbaaffe49024d8570916b70f7839603a54692/lib/engine.ml#L100
125125+126126+**Lucky 13**
127127+128128+An advancement of the CBC timing attack was discovered in 2013, named
129129+[Lucky 13][Lucky13]. Due to the fact that the mac is computed over the
130130+plaintext without padding, there is a slight (but measurable)
131131+difference in timing between computing the mac of the plaintext and
132132+computing the fake mac of the ciphertext. This leaks information. We
133133+do not have proper mitigation against Lucky 13 in place yet. You can
134134+find further discussion in [issue 7][issue7] and [pull request
135135+49][pull49].
136136+137137+[Lucky13]: http://www.isg.rhul.ac.uk/tls/Lucky13.html
138138+[issue7]: https://github.com/mirleft/ocaml-tls/issues/7
139139+[pull49]: https://github.com/mirleft/ocaml-tls/pull/49
140140+141141+**Renegotiation not authenticated**
142142+143143+In 2009, Marsh Ray published a vulnerability of the TLS protocol which
144144+lets an attacker prepend arbitrary data to a session due to
145145+[unauthenticated renegotiation][understanding_reneg]. The attack
146146+exploits the fact that a renegotiation of ciphers and key material is
147147+possible within a session, and this renegotiated handshake is not
148148+authenticated by the previous handshake. A man in the middle can
149149+initiate a session with a server, send some data, and hand over the
150150+session to a client. Neither the client nor the server can detect the
151151+man in the middle.
152152+153153+A fix for this issue is the [secure renegotiation extension][RFC5746],
154154+which embeds authenticated data of the previous handshake into the
155155+client and server hello messages. Now, if a man in the middle
156156+initiates a renegotiation, the server will not complete it due to
157157+missing authentication data (the client believes this is the first
158158+handshake).
159159+160160+We implement and require the secure renegotiation extension by
161161+default, but it is possible to configure `ocaml-tls` to not require
162162+it -- to be able to communicate with servers and
163163+clients which do not support this extension.
164164+165165+Implementation of the mitigation is on the server side in
166166+[ensure_reneg][] and on the client side in [validate_reneg][]. The
167167+data required for the secure renegotiation is stored in
168168+[`handshake_state`][reneg_state] while sending and receiving Finished
169169+messages. You can find further discussion in [issue 3][issue3].
170170+171171+[RFC5746]: https://tools.ietf.org/html/rfc5746
172172+[validate_reneg]: https://github.com/mirleft/ocaml-tls/blob/c06cbaaffe49024d8570916b70f7839603a54692/lib/handshake_client.ml#L50
173173+[ensure_reneg]: https://github.com/mirleft/ocaml-tls/blob/c06cbaaffe49024d8570916b70f7839603a54692/lib/handshake_server.ml#L85
174174+[issue3]: https://github.com/mirleft/ocaml-tls/issues/3
175175+[reneg_state]: https://github.com/mirleft/ocaml-tls/blob/c06cbaaffe49024d8570916b70f7839603a54692/lib/state.ml#L97
176176+177177+**TLS 1.0 and known-plaintext (BEAST)**
178178+179179+TLS 1.0 reuses the last ciphertext block as IV in CBC mode. If an attacker
180180+has a (partially) known plaintext, she can find the remaining plaintext.
181181+This is known as the [BEAST][] attack and there is a [long discussion][mozilla-bug]
182182+about mitigations. Our mitigation is to prepend each TLS-1.0
183183+application data fragment with an empty fragment to randomize the IV.
184184+We do this exactly [here][empty_iv]. There is further discussion in
185185+[issue 2][issue2].
186186+187187+Our mitigation is slightly different from the 1/n-1 splitting proposed
188188+[here][qualys]: we split every application data frame into a 0 byte
189189+and n byte frame, whereas they split into a 1 byte and a n-1 byte
190190+frame.
191191+192192+Researchers have exploited this vulnerability in 2011, although it was
193193+known since [2006][]. TLS versions 1.1 and 1.2 use an explicit IV,
194194+instead of reusing the last cipher block on the wire.
195195+196196+[qualys]: https://community.qualys.com/blogs/securitylabs/2013/09/10/is-beast-still-a-threat
197197+[mozilla-bug]: https://bugzilla.mozilla.org/show_bug.cgi?id=665814
198198+[BEAST]: http://vnhacker.blogspot.co.uk/2011/09/beast.html
199199+[empty_iv]: https://github.com/mirleft/ocaml-tls/blob/c06cbaaffe49024d8570916b70f7839603a54692/lib/engine.ml#L375
200200+[2006]: http://eprint.iacr.org/2006/136
201201+[issue2]: https://github.com/mirleft/ocaml-tls/issues/2
202202+203203+**Compression and information leakage (CRIME)**
204204+205205+When using compression on a chosen-plaintext, encrypting this can leak
206206+information, known as [CRIME][crime]. [BREACH][breach] furthermore
207207+exploits application layer compression, such as HTTP compression. We
208208+mitigate CRIME by not providing any TLS compression support, while we
209209+cannot do anything to mitigate BREACH.
210210+211211+[crime]: http://arstechnica.com/security/2012/09/crime-hijacks-https-sessions/
212212+213213+**Traffic analysis**
214214+215215+Due to limited amount of padding data, the actual size of transmitted
216216+data can be recovered. The mitigation is to implement [length hiding
217217+policies][length_hiding]. This is tracked as [issue 162][issue162].
218218+219219+[issue162]: https://github.com/mirleft/ocaml-tls/issues/162
220220+[length_hiding]: http://tools.ietf.org/html/draft-pironti-tls-length-hiding-02
221221+222222+**Version rollback**
223223+224224+SSL-2.0 is insecure, a man in the middle can downgrade the version to
225225+SSL-2.0. The mitigation we implement is that we do not support
226226+SSL-2.0, and thus cannot be downgraded. Also, we check that the
227227+version of the client hello matches the first two bytes in the
228228+premaster secret [here][client_version]. You can find further discussion in
229229+[issue 5][issue5].
230230+231231+[client_version]: https://github.com/mirleft/ocaml-tls/blob/c06cbaaffe49024d8570916b70f7839603a54692/lib/handshake_server.ml#L55
232232+[issue5]: https://github.com/mirleft/ocaml-tls/issues/5
233233+234234+**Triple handshake**
235235+236236+A vulnerability including session resumption and renegotiation was
237237+discovered by the [miTLS team][mitls], named [triple
238238+handshake][triple]. Mitigations include disallowing renegotiation,
239239+disallowing modification of the certificate during renegotiation, or
240240+a hello extension. Since we do not support session resumption yet, we
241241+have not yet implemented any of the mentioned mitigations. There is
242242+further discussion in [issue 9][issue9].
243243+244244+[mitls]: http://www.mitls.org
245245+[issue9]: https://github.com/mirleft/ocaml-tls/issues/9
246246+[triple]: https://secure-resumption.com/
247247+248248+**Alert attack**
249249+250250+A [fragment of an alert][alert_attack] can be sent by a man in the
251251+middle during the initial handshake. If the fragment is not cleared
252252+once the handshake is finished, the authentication of alerts is
253253+broken. This was discovered in 2012; our mitigation is to discard
254254+fragmented alerts.
255255+256256+[alert_attack]: http://www.mitls.org/wsgi/alert-attack
257257+258258+### EOF.
259259+260260+Within six months, two hackers managed to develop a clean-slate TLS
261261+stack, together with required crypto primitives, ASN.1, and X.509
262262+handling, in a high-level pure language. We interoperate with widely
263263+deployed TLS stacks, as shown by our [demo server][demo]. The code
264264+size is nearly two orders of magnitude smaller than OpenSSL, the most
265265+widely used open source library (written in C, which a lot of
266266+programming languages wrap instead of providing their own TLS
267267+implementation). Our code base seems to be robust -- the [demo
268268+server][demo] successfully finished over 22500 sessions in less than a
269269+week, with only 11 failing traces.
270270+271271+There is a huge need for high quality TLS implementations, because
272272+several TLS implementations suffered this year from severe security
273273+problems, such as [heartbleed][], [goto fail][CVE-2014-1266], [session
274274+id][CVE-2014-3466], [Bleichenbacher][java], [change cipher
275275+suite][CVE-2014-0224] and [GCM DoS][polar]. The main cause is
276276+implementation complexity due to lack of abstraction, and memory
277277+safety issues.
278278+279279+We still need to address some security issues, and improve our performance. We
280280+invite people to do rigorous code audits (both manual and automated) and try
281281+testing our code in their services.
282282+283283+**Please be aware that this release is a *beta* and is missing external code audits.
284284+It is not yet intended for use in any security critical applications.**
285285+286286+[demo]: https://tls.openmirage.org
287287+[polar]: https://polarssl.org/tech-updates/security-advisories/polarssl-security-advisory-2014-02
288288+[java]: http://armoredbarista.blogspot.de/2014/04/easter-hack-even-more-critical-bugs-in.html
289289+[CVE-2014-1266]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-1266
290290+[CVE-2014-3466]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3466
291291+[CVE-2014-0224]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0224
292292+293293+[nocrypto-intro]: http://openmirage.org/blog/introducing-nocrypto
···11+let now = Ptime_clock.now
22+33+let cert ~digest ~key =
44+ let subject =
55+ let open X509.Distinguished_name in
66+ [ Relative_distinguished_name.singleton (CN "ocaml-tls") ]
77+ in
88+ let csr = X509.Signing_request.create ~digest subject key |> Result.get_ok in
99+ let pubkey = (X509.Signing_request.info csr).public_key in
1010+ let extensions =
1111+ let open X509.Extension in
1212+ let auth =
1313+ (Some (X509.Public_key.id pubkey), X509.General_name.empty, None)
1414+ in
1515+ singleton Authority_key_id (false, auth)
1616+ |> add Subject_key_id (false, X509.Public_key.id pubkey)
1717+ |> add Basic_constraints (true, (true, None))
1818+ |> add Key_usage
1919+ (true,
2020+ [ `Key_cert_sign
2121+ ; `CRL_sign
2222+ ; `Digital_signature
2323+ ; `Content_commitment
2424+ ; `Key_encipherment ])
2525+ |> add Ext_key_usage (true, [ `Server_auth ])
2626+ in
2727+ let valid_from = now () in
2828+ let valid_until = Ptime.add_span valid_from (Ptime.Span.of_int_s 60) in
2929+ let valid_until = Option.get valid_until in
3030+ let cert = X509.Signing_request.sign csr ~valid_from ~valid_until ~digest
3131+ ~extensions key subject in
3232+ match cert with
3333+ | Ok cert -> cert
3434+ | Error e -> Fmt.failwith "cert error %a" X509.Validation.pp_signature_error e
3535+3636+let authenticator ?ip:_ ~host:_ _certs = Ok None
3737+3838+let consume state input =
3939+ match Tls.Engine.handle_tls state input with
4040+ | Ok (state, Some `Eof, `Response out, `Data v) ->
4141+ let data = Option.fold ~none:0 ~some:String.length v in
4242+ `Eof state, out, data
4343+ | Ok (state, None, `Response out, `Data v) ->
4444+ let data = Option.fold ~none:0 ~some:String.length v in
4545+ `Continue state, out, data
4646+ | Error (err, `Response out) ->
4747+ `Error err, Some out, 0
4848+4949+let to_state state input =
5050+ match consume state input with
5151+ | `Eof _, _, _ -> Fmt.failwith "Unexpected eof"
5252+ | `Error err, _, _ -> Fmt.failwith "Unexpected error: %a" Tls.Engine.pp_failure err
5353+ | `Continue state, out, data -> state, out, data
5454+5555+type flow =
5656+ | To_client of Tls.Engine.state * Tls.Engine.state * string option
5757+ | To_server of Tls.Engine.state * Tls.Engine.state * string option
5858+5959+type state =
6060+ { flow : flow
6161+ ; server_out : int
6262+ ; client_out : int
6363+ ; direction : [ `To_server | `To_client ] }
6464+6565+let get_ok = function
6666+ | Ok cfg -> cfg
6767+ | Error `Msg msg -> invalid_arg msg
6868+6969+let make ?groups ~cipher ~digest ~key version direction =
7070+ let cert = cert ~digest ~key in
7171+ let client_cfg =
7272+ get_ok (Tls.Config.client ?groups ~version:(version, version)
7373+ ~ciphers:[ cipher ] ~authenticator ())
7474+ and server_cfg =
7575+ get_ok (Tls.Config.server ~certificates:(`Single ([ cert ], key)) ())
7676+ in
7777+ let client_state, client_out = Tls.Engine.client client_cfg
7878+ and server_state = Tls.Engine.server server_cfg in
7979+ { flow= To_server (client_state, server_state, Some client_out)
8080+ ; server_out= 0
8181+ ; client_out= 0
8282+ ; direction }
8383+8484+let actually_send_application_data client_state server_state direction buf =
8585+ match direction with
8686+ | `To_server ->
8787+ let[@warning "-8"] Some (client_state, to_server) =
8888+ Tls.Engine.send_application_data client_state [ buf ] in
8989+ To_server (client_state, server_state, Some to_server)
9090+ | `To_client ->
9191+ let[@warning "-8"] Some (server_state, to_client) =
9292+ Tls.Engine.send_application_data server_state [ buf ] in
9393+ To_client (client_state, server_state, Some to_client)
9494+9595+let rec once state buf = match state.flow, buf with
9696+ | To_server (client_state, server_state, None), Some buf
9797+ | To_client (client_state, server_state, None), Some buf ->
9898+ let flow = actually_send_application_data
9999+ client_state server_state state.direction buf in
100100+ once { state with flow } None
101101+ | To_server (_, _, None), None
102102+ | To_client (_, _, None), None -> state
103103+ | To_server (client_state, server_state, Some to_server), buf ->
104104+ let server_state, to_client, n = to_state server_state to_server in
105105+ let flow = To_client (client_state, server_state, to_client) in
106106+ once { state with flow; server_out= state.server_out + n } buf
107107+ | To_client (client_state, server_state, Some to_client), _ ->
108108+ let client_state, to_server, n = to_state client_state to_client in
109109+ let flow = To_server (client_state, server_state, to_server) in
110110+ once { state with flow; client_out= state.client_out + n } buf
111111+112112+let to_consumer state =
113113+ let state = ref state in
114114+ fun buf -> state := once !state (Some buf)
115115+116116+module Time = struct
117117+ let time ~n fn a =
118118+ let t1 = Sys.time () in
119119+ for _i = 0 to n - 1 do ignore (fn a) done;
120120+ let t2 = Sys.time () in
121121+ (t2 -. t1)
122122+end
123123+124124+let burn_period = 2.0
125125+let sizes = [ 16; 64; 256; 1024; 4096; 8192 ]
126126+127127+let burn fn size =
128128+ let cs = Mirage_crypto_rng.generate size in
129129+ let (t1, i1) =
130130+ let rec go it =
131131+ let t = Time.time ~n:it fn cs in
132132+ if t > 0.2 then (t, it) else go (it * 10) in
133133+ go 10 in
134134+ let iters = int_of_float (float i1 *. burn_period /. t1) in
135135+ let time = Time.time ~n:iters fn cs in
136136+ (iters, time, float (size * iters) /. time)
137137+138138+let mb = 1024. *. 1024.
139139+140140+let throughput title fn =
141141+ Fmt.pr "\n## %s\n\n%!" title ;
142142+ Fmt.pr "| block | MB/s |\n%!" ;
143143+ Fmt.pr "| ----- | ------- |\n%!" ;
144144+ List.iter begin fun size ->
145145+ Gc.full_major ();
146146+ let (_iters, _time, bw) = burn fn size in
147147+ Fmt.pr "| %5d | %7.2f |\n%!" size (bw /. mb)
148148+ end sizes
149149+150150+let bm name fn = (name, fun () -> fn name)
151151+152152+let count_period = 10.
153153+154154+let count f n =
155155+ ignore (f n);
156156+ let i1 = 5 in
157157+ let t1 = Time.time ~n:i1 f n in
158158+ let iters = int_of_float (float i1 *. count_period /. t1) in
159159+ let time = Time.time ~n:iters f n in
160160+ (iters, time)
161161+162162+let count title f to_str args =
163163+ Printf.printf "\n## %s\n\n%!" title ;
164164+ Printf.printf "| group | hs/s |\n%!" ;
165165+ Printf.printf "| --------- | ------- |\n%!" ;
166166+ args |> List.iter @@ fun arg ->
167167+ Gc.full_major () ;
168168+ let iters, time = count f arg in
169169+ Printf.printf "| %s | %7.2f |\n%!"
170170+ (to_str arg) (float iters /. time)
171171+172172+let print_group group =
173173+ let str = Fmt.to_to_string Tls.Core.pp_group group in
174174+ let pad = 9 - String.length str in
175175+ str ^ String.make pad ' '
176176+177177+let throughput =
178178+ [ bm "tls-1.3, rsa/2048, x25519, aes-128-ccm-sha256" begin fun name ->
179179+ let key = X509.Private_key.generate ~bits:2048 `RSA in
180180+ let state = make ~groups:[ `X25519 ] ~cipher:`AES_128_CCM_SHA256 ~digest:`SHA256 ~key `TLS_1_3 `To_server in
181181+ throughput name (to_consumer state)
182182+ end
183183+ ; bm "tls-1.3, rsa/2048, x25519, aes-128-gcm-sha256" begin fun name ->
184184+ let key = X509.Private_key.generate ~bits:2048 `RSA in
185185+ let state = make ~groups:[ `X25519 ] ~cipher:`AES_128_GCM_SHA256 ~digest:`SHA256 ~key `TLS_1_3 `To_server in
186186+ throughput name (to_consumer state)
187187+ end
188188+ ; bm "tls-1.3, rsa/2048, x25519, aes-256-gcm-sha384" begin fun name ->
189189+ let key = X509.Private_key.generate ~bits:2048 `RSA in
190190+ let state = make ~groups:[ `X25519 ] ~cipher:`AES_256_GCM_SHA384 ~digest:`SHA256 ~key `TLS_1_3 `To_server in
191191+ throughput name (to_consumer state)
192192+ end
193193+ ; bm "tls-1.3, rsa/2048, x25519, chacha20-poly1305-sha256" begin fun name ->
194194+ let key = X509.Private_key.generate ~bits:2048 `RSA in
195195+ let state = make ~groups:[ `X25519 ] ~cipher:`CHACHA20_POLY1305_SHA256 ~digest:`SHA256 ~key `TLS_1_3 `To_server in
196196+ throughput name (to_consumer state)
197197+ end
198198+ ; bm "tls-1.2, rsa/2048, ffdhe2048, aes-128-ccm" begin fun name ->
199199+ let key = X509.Private_key.generate ~bits:2048 `RSA in
200200+ let state = make ~groups:[ `FFDHE2048 ] ~cipher:`DHE_RSA_WITH_AES_128_CCM ~digest:`SHA256 ~key `TLS_1_2 `To_server in
201201+ throughput name (to_consumer state)
202202+ end
203203+ ; bm "tls-1.2, rsa/2048, ffdhe2048, aes-256-ccm" begin fun name ->
204204+ let key = X509.Private_key.generate ~bits:2048 `RSA in
205205+ let state = make ~groups:[ `FFDHE2048 ] ~cipher:`DHE_RSA_WITH_AES_256_CCM ~digest:`SHA256 ~key `TLS_1_2 `To_server in
206206+ throughput name (to_consumer state)
207207+ end
208208+ ; bm "tls-1.2, rsa/2048, ffdhe2048, aes-128-gcm-sha256" begin fun name ->
209209+ let key = X509.Private_key.generate ~bits:2048 `RSA in
210210+ let state = make ~groups:[ `FFDHE2048 ] ~cipher:`DHE_RSA_WITH_AES_128_GCM_SHA256 ~digest:`SHA256 ~key `TLS_1_2 `To_server in
211211+ throughput name (to_consumer state)
212212+ end
213213+ ; bm "tls-1.2, rsa/2048, ffdhe2048, aes-256-gcm-sha384" begin fun name ->
214214+ let key = X509.Private_key.generate ~bits:2048 `RSA in
215215+ let state = make ~groups:[ `FFDHE2048 ] ~cipher:`DHE_RSA_WITH_AES_256_GCM_SHA384 ~digest:`SHA256 ~key `TLS_1_2 `To_server in
216216+ throughput name (to_consumer state)
217217+ end
218218+ ; bm "tls-1.2, rsa/2048, ffdhe2048, chacha20_poly1305_sha256" begin fun name ->
219219+ let key = X509.Private_key.generate ~bits:2048 `RSA in
220220+ let state = make ~groups:[ `FFDHE2048 ] ~cipher:`DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 ~digest:`SHA256 ~key `TLS_1_2 `To_server in
221221+ throughput name (to_consumer state)
222222+ end
223223+ ]
224224+225225+and handshake =
226226+ [ bm "tls-1.3 handshake, rsa2048" begin fun name ->
227227+ let key = X509.Private_key.generate ~bits:2048 `RSA in
228228+ count name begin fun group ->
229229+ let state = make ~groups:[ group ] ~cipher:`CHACHA20_POLY1305_SHA256 ~digest:`SHA256 ~key `TLS_1_3 `To_server in
230230+ ignore (once state None)
231231+ end
232232+ print_group
233233+ ([ `X25519 ; `P256 ; `P384 ; `P521 ; `FFDHE2048 ; `FFDHE3072 ])
234234+ end
235235+ ; bm "tls-1.3 handshake, ed25519" begin fun name ->
236236+ let key = X509.Private_key.generate `ED25519 in
237237+ count name begin fun group ->
238238+ let state = make ~groups:[ group ] ~cipher:`CHACHA20_POLY1305_SHA256 ~digest:`SHA256 ~key `TLS_1_3 `To_server in
239239+ ignore (once state None)
240240+ end
241241+ print_group
242242+ ([ `X25519 ; `P256 ; `P384 ; `P521 ; `FFDHE2048 ; `FFDHE3072 ])
243243+ end
244244+ ; bm "tls-1.3 handshake, p256" begin fun name ->
245245+ let key = X509.Private_key.generate `P256 in
246246+ count name begin fun group ->
247247+ let state = make ~groups:[ group ] ~cipher:`CHACHA20_POLY1305_SHA256 ~digest:`SHA256 ~key `TLS_1_3 `To_server in
248248+ ignore (once state None)
249249+ end
250250+ print_group
251251+ ([ `X25519 ; `P256 ; `P384 ; `P521 ; `FFDHE2048 ; `FFDHE3072 ])
252252+ end
253253+ ; bm "tls-1.2 handshake, rsa2048" begin fun name ->
254254+ let key = X509.Private_key.generate ~bits:2048 `RSA in
255255+ count name begin fun group ->
256256+ let cipher = match group with
257257+ | `FFDHE4096 | `FFDHE6144 | `FFDHE8192 | `FFDHE2048 | `FFDHE3072 -> `DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
258258+ | `X25519 | `P256 | `P384 | `P521 -> `ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
259259+ in
260260+ let state = make ~groups:[ group ] ~cipher ~digest:`SHA256 ~key `TLS_1_2 `To_server in
261261+ ignore (once state None)
262262+ end
263263+ print_group
264264+ ([ `X25519 ; `P256; `P384 ; `P521 ; `FFDHE2048 ; `FFDHE3072 ])
265265+ end
266266+ ]
267267+268268+let run fns =
269269+ List.iter (fun (_, fn) -> fn ()) fns
270270+271271+let () = Mirage_crypto_rng_unix.use_default ()
272272+273273+let () =
274274+ let seed = "0xdeadbeef" in
275275+ let g = Mirage_crypto_rng.(create ~seed (module Fortuna)) in
276276+ Mirage_crypto_rng.set_default_generator g;
277277+ let bench =
278278+ match Sys.argv.(1) with
279279+ | exception Invalid_argument _ -> throughput @ handshake
280280+ | "hs" -> handshake
281281+ | "bw" -> throughput
282282+ | _ -> invalid_arg "supported is: 'hs' (for handshake) or 'bw' (for bandwidth)"
283283+ in
284284+ run bench
···11+### The OCaml-TLS architecture
22+33+The OCaml ecosystem has several distinct ways of interacting with the outside world
44+(and the network in particular): straightforward [unix][ocaml-unix] interfaces
55+and the asynchronous programming libraries [lwt][] and [async][]. One of the
66+early considerations was not to restrict ourselves to any of those -- we wanted
77+to support them all.
88+99+There were also two distinct basic "platforms" we wanted to target from the
1010+outset: the case of a simple executable, and the case of `Mirage` unikernels.
1111+1212+So one of the first questions we faced was deciding how to represent
1313+interactions with the network in a portable way. This can be done by
1414+systematically abstracting out the API boundary which gives access to network
1515+operations, but we had a third thing in mind as well: we wanted to exploit the
1616+functional nature of OCaml to its fullest extent!
1717+1818+Our various prior experiences with Haskell and Idris convinced us to adopt
1919+what is called "purely functional" technique. We believe it to be an approach
2020+which first forces the programmer to give principled answers to all the
2121+difficult design questions (errors and global data-flow) *in advance*, and then
2222+leads to far cleaner and composable code later on. A purely functional system
2323+has all the data paths made completely explicit in the form of function
2424+arguments and results. There are no unaccounted-for interactions between
2525+components mediated by shared state, and all the activity of the parts of the
2626+system is exposed through types since, after all, it's only about computing
2727+values from values.
2828+2929+For these reasons, the library is split into two parts: the directory `/lib`
3030+(and the corresponding findlib package `tls`) contains the core TLS logic, and
3131+`/mirage` and `/lwt` (packaged as `tls.mirage` and `tls.lwt` respectively)
3232+contain front-ends that tie the core to `Mirage` and `Lwt_unix`.
3333+3434+[ocaml-unix]: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Unix.html
3535+[lwt]: http://ocsigen.org/lwt/
3636+[async]: https://realworldocaml.org/v1/en/html/concurrent-programming-with-async.html
3737+3838+### Core
3939+4040+The [core][tls-engine-mli] library is purely functional. A TLS session is represented by the
4141+abstract type `Tls.Engine.state`, and various functions consume this session
4242+type together with raw bytes (`Cstruct.t` -- which is by itself mutable, but
4343+`ocaml-tls` eschews this) and produce new session values and resulting buffers.
4444+4545+The central entry point is [handle_tls][], which transforms an input state and a
4646+buffer to an output state, a (possibly empty) buffer to send to the
4747+communication partner, and an optional buffer of data intended to be received by
4848+the application:
4949+5050+```OCaml
5151+type state
5252+5353+type ret = [
5454+ | `Ok of [ `Ok of state | `Eof | `Alert of alert ] *
5555+ [ `Response of Cstruct.t ] * [ `Data of Cstruct.t option ]
5656+ | `Fail of alert * [ `Response of Cstruct.t ]
5757+]
5858+5959+val handle_tls : state -> Cstruct.t -> ret
6060+```
6161+6262+As the signature shows, errors are signalled through the `ret` type, which is a [polymorphic variant][poly]. This
6363+reflects the actual internal structure: all the errors are represented as
6464+values, and operations are composed using an error [monad][monad-ml].
6565+6666+Other entry points share the same basic behaviour: they transform the prior
6767+state and input bytes into the later state and output bytes.
6868+6969+Here's a rough outline of what happens in `handle_tls`:
7070+7171+- TLS packets consist of a header, which contains the protocol
7272+ version, length, and content type, and the payload of the given
7373+ content type. Once inside our [main handler][handle_tls], we
7474+ [separate][separate_records] the buffer into TLS records, and
7575+ [process][handle_raw_record] each individually. We first check that
7676+ the version number is correct, then [decrypt][decrypt], and [verify
7777+ the mac][verify_mac].
7878+7979+- Decrypted data is then [dispatched][handle_packet] to one of four
8080+ sub-protocol handlers (Handshake, Change Cipher Spec, Alert and
8181+ Application Data). Each handler can [return][return_types] a new
8282+ handshake state, outgoing data, application data, the new decryption
8383+ state or an error (with the outgoing data being an interleaved list
8484+ of buffers and new encryption states).
8585+8686+- The outgoing buffers and the encryption states are
8787+ [traversed][encrypt] to produce the final output to be sent to the
8888+ communication partner, and the final encryption, decryption and
8989+ handshake states are combined into a new overall state which is
9090+ returned to the caller.
9191+9292+Handshake is (by far) the most complex TLS sub-protocol, with an elaborate state
9393+machine. Our [client][client_handshake] and [server][server_handshake] encode
9494+this state as a "flat" [sum type][handshake_states], with exactly one incoming
9595+message allowed per state. The handlers first [parse][parse_handshake] the
9696+handshake packet (which fails in case of malformed or unknown data) and then
9797+dispatch it to the handling function. The [handshake state][handshake_state] is
9898+carried around and a fresh one is returned from the handler in case it needs
9999+updates. It consists of a protocol version, the handshake state, configuration,
100100+renegotiation data, and possibly a handshake fragment.
101101+102102+Logic of both handshake handlers is very localised, and does not mutate any
103103+global data structures.
104104+105105+[poly]: https://realworldocaml.org/v1/en/html/variants.html#polymorphic-variants
106106+[monad-ml]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/control.ml
107107+[return_types]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/state.ml#L109
108108+[encrypt]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/engine.ml#L48
109109+[handle_packet]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/engine.ml#L240
110110+[verify_mac]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/engine.ml#L85
111111+[decrypt]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/engine.ml#L95
112112+[handle_tls]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/engine.ml#L321
113113+[handle_raw_record]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/engine.ml#L275
114114+[separate_records]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/engine.ml#L150
115115+116116+[handshake_state]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/state.ml#L92
117117+[parse_handshake]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/reader.ml#L361
118118+[separate_handshakes]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/engine.ml#L217
119119+[handshake_states]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/state.ml#L61
120120+[server_handshake]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/handshake_server.ml#L247
121121+[client_handshake]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/handshake_client.ml#L285
122122+123123+### Core API
124124+125125+OCaml permits the implementation a module to be exported via a more
126126+abstract *signature* that hides the internal representation
127127+details. Our public API for the core library consists of the
128128+[Tls.Engine][tls-engine-mli] and [Tls.Config][tls-config-mli] modules.
129129+130130+`Tls.Engine` contains the basic reactive function `handle_tls`, mentioned above,
131131+which processes incoming data and optionally produces a response, together with
132132+several operations that allow one to initiate message transfer like
133133+`send_application_data` (which processes application-level messages for
134134+sending), `send_close_notify` (for sending the ending message) and `reneg`
135135+(which initiates full TLS renegotiation).
136136+137137+The module also contains the only two ways to obtain the initial state:
138138+139139+```OCaml
140140+val client : Config.client -> (state * Cstruct.t)
141141+val server : Config.server -> state
142142+```
143143+144144+That is, one needs a configuration value to create it. The `Cstruct.t`
145145+that `client` emits is the initial Client Hello since in TLS,
146146+the client starts the session.
147147+148148+`Tls.Config` synthesizes configurations, separately for client and server
149149+endpoints, through the functions `client_exn` and `server_exn`. They take a
150150+number of parameters that define a TLS session, check them for consistency, and
151151+return the sanitized `config` value which can be used to create a `state` and,
152152+thus, a session. If the check fails, they raise an exception.
153153+154154+The parameters include the pair of a certificate and its private key for the
155155+server, and an `X509.Authenticator.t` for the client, both produced by our
156156+[ocaml-x509][] library and described in a [previous article][x509-intro].
157157+158158+This design reflects our attempts to make the API as close to "fire and forget"
159159+as we could, given the complexity of TLS: we wanted the library to be relatively
160160+straightforward to use, have a minimal API footprint and, above all, fail very
161161+early and very loudly when misconfigured.
162162+163163+[tls-engine-mli]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/engine.mli
164164+165165+[tls-config-mli]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lib/config.mli
166166+167167+[ocaml-x509]: https://github.com/mirleft/ocaml-x509
168168+169169+170170+### Effectful front-ends
171171+172172+Clearly, reading and writing network data *does* change the state of the world.
173173+Having a pure value describing the state of a TLS session is not really useful
174174+once we write something onto the network; it is certainly not the case that we
175175+can use more than one distinct `state` to process further data, as only one
176176+value is in sync with the other endpoint at any given time.
177177+178178+Therefore we wrap the core types into stateful structures loosely inspired by
179179+sockets and provide IO operations on those. The structures of `mirage` and `lwt`
180180+front-ends mirror one another.
181181+182182+In both cases, the structure is pull-based in the sense that no processing is
183183+done until the client requires a read, as opposed to a callback-driven design
184184+where the client registers a callback and the library starts spinning in a
185185+listening loop and invoking it as soon as there is data to be processed. We do
186186+this because in an asynchronous context, it is easy to create a callback-driven
187187+interface from a demand-driven one, but the opposite is possible only with
188188+unbounded buffering of incoming data.
189189+190190+One exception to demand-driven design is the initial session creation: the
191191+library will only yield the connection after the first handshake is over,
192192+ensuring the invariant that it is impossible to interact with a connection if it
193193+hasn't already been fully established.
194194+195195+**Mirage**
196196+197197+The `Mirage` [interface][tls_mirage_types_mli] matches the [FLOW][flow]
198198+signature (with additional TLS-specific operations). We provide a functor that
199199+needs to be applied to an underlying TCP module, to obtain a TLS transport on
200200+top. For example:
201201+202202+```OCaml
203203+module Server (Stack: STACKV4) (KV: KV_RO) =
204204+struct
205205+206206+ module TLS = Tls_mirage.Make (Stack.TCPV4)
207207+ module X509 = Tls_mirage.X509 (KV) (Clock)
208208+209209+ let accept conf flow =
210210+ TLS.server_of_tcp_flow conf flow >>= function
211211+ | `Ok tls ->
212212+ TLS.read tls >>= function
213213+ | `Ok buf ->
214214+ TLS.write tls buf >>= fun () -> TLS.close buf
215215+216216+ let start stack e kv =
217217+ lwt authenticator = X509.authenticator kv `Default in
218218+ let conf = Tls.Config.server_exn ~authenticator () in
219219+ Stack.listen_tcpv4 stack 4433 (accept conf) ;
220220+ Stack.listen stack
221221+222222+end
223223+```
224224+225225+**Lwt**
226226+227227+The `lwt` interface has [two layers][tls_lwt_mli]. `Tls_lwt.Unix` is loosely based
228228+on read/write operations from `Lwt_unix` and provides in-place update of
229229+buffers. `read`, for example, takes a `Cstruct.t` to write into and returns the
230230+number of bytes read. The surrounding module, `Tls_lwt`, provides a simpler,
231231+`Lwt_io`-compatible API built on top:
232232+233233+```OCaml
234234+let main host port =
235235+ lwt authenticator = X509_lwt.authenticator (`Ca_dir nss_trusted_ca_dir) in
236236+ lwt (ic, oc) = Tls_lwt.connect ~authenticator (host, port) in
237237+ let req = String.concat "\r\n" [
238238+ "GET / HTTP/1.1" ; "Host: " ^ host ; "Connection: close" ; "" ; ""
239239+ ] in
240240+ Lwt_io.(write oc req >>= fun () -> read ic >>= print)
241241+```
242242+243243+We have further plans to provide wrappers for [`Async`][async] and plain [`Unix`][ocaml-unix] in a
244244+similar vein.
245245+246246+[tls_mirage_types_mli]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/mirage/tls_mirage_types.mli
247247+[flow]: https://github.com/mirage/mirage/blob/ae3c966f8d726dc97208595b8005e02e39478cb1/types/V1.mli#L136
248248+[example_unikernel]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/mirage/example/unikernel.ml
249249+[tls_lwt_mli]: https://github.com/mirleft/ocaml-tls/blob/6dc9258a38489665abf2bd6cdbed8a1ba544d522/lwt/tls_lwt.mli
···11+(copy_files ../../certificates/*.crt)
22+(copy_files ../../certificates/*.key)
33+(copy_files ../../certificates/*.pem)
44+55+(mdx
66+ (package tls-eio)
77+ (deps
88+ server.pem
99+ server.key
1010+ server-ec.pem
1111+ server-ec.key
1212+ (package tls-eio)
1313+ (package mirage-crypto-rng)
1414+ (package eio_main)))
1515+1616+; "dune runtest" just does a quick run with random inputs.
1717+;
1818+; To run with afl-fuzz instead (make sure you have a compiler with the afl option on!):
1919+;
2020+; dune runtest
2121+; mkdir input
2222+; echo hi > input/foo
2323+; cp certificates/server.{key,pem} .
2424+; afl-fuzz -m 1000 -i input -o output ./_build/default/eio/tests/fuzz.exe @@
2525+(test
2626+ (package tls-eio)
2727+ (libraries crowbar tls-eio eio.mock logs logs.fmt)
2828+ (deps server.pem server.key)
2929+ (name fuzz)
3030+ (action (run %{test} --repeat 200)))
+297
vendor/opam/tls/eio/tests/fuzz.ml
···11+(* Fuzz testing for tls-eio.
22+33+ This code picks two random strings, one for the client to send and one for
44+ the server. It then starts a send and receive fiber for each end.
55+66+ A dispatcher fiber then sends commands to these worker fibers
77+ (see [action] for the possible actions).
88+99+ This is intended to check for bugs in the Eio wrapper (rather than in Tls itself).
1010+ At the moment, it's just checking that tls-eio works when used correctly.
1111+ Each endpoint overlaps reads with writes (but not reads with other reads or
1212+ writes with other writes).
1313+1414+ Some possible future improvements:
1515+1616+ - It currently only checks the basic read/write/close operations.
1717+ It should be extended to check [reneg], etc too.
1818+1919+ - Currently, cancelling a read operation marks the Tls flow as broken.
2020+ We should allow resuming after a cancelled read, and test that here.
2121+2222+ - We should try injecting faults and make sure they're handled sensibly.
2323+2424+ - It would be good to get coverage reports for these tests.
2525+ However, this requires changes to crowbar:
2626+ https://github.com/stedolan/crowbar/issues/4#issuecomment-1310277551
2727+ (a patched version reported 54% coverage of Tls_eio.ml) *)
2828+2929+open Eio.Std
3030+3131+let src = Logs.Src.create "fuzz" ~doc:"Fuzz tests"
3232+module Log = (val Logs.src_log src : Logs.LOG)
3333+3434+module W = Eio.Buf_write
3535+3636+type transmit_amount = Mock_socket.transmit_amount
3737+3838+type op =
3939+ | Send of int (* The application sends some bytes to Tls *)
4040+ | Transmit of transmit_amount (* The network sends some types to the peer *)
4141+ | Recv (* The application tries to read some data *)
4242+ | Shutdown_send (* The application shuts down the sending side *)
4343+4444+let label name gen =
4545+ Crowbar.with_printer Fmt.(const string name) gen
4646+4747+let op =
4848+ Crowbar.choose @@ [
4949+ Crowbar.(map [range 4096]) (fun n -> Send n);
5050+ Crowbar.(map [range ~min:1 4096]) (fun n -> Transmit (`Bytes n));
5151+ label "recv" @@ Crowbar.const Recv;
5252+ label "shutdown-send" @@ Crowbar.const Shutdown_send;
5353+ ]
5454+5555+type dir = To_client | To_server
5656+5757+let pp_dir f = function
5858+ | To_server -> Fmt.string f "client-to-server"
5959+ | To_client -> Fmt.string f "server-to-client"
6060+6161+let dir =
6262+ Crowbar.choose [
6363+ label "server-to-client" @@ Crowbar.const To_client;
6464+ label "client-to-server" @@ Crowbar.const To_server;
6565+ ]
6666+6767+(* A test case is a random sequence of [action]s, followed by party shutting
6868+ down the sending side of the connection (if it hasn't already done so) and
6969+ the network draining any queued traffic.
7070+7171+ Once all fibers have finished, we check that what was sent matches the data
7272+ that has been received. *)
7373+7474+let action =
7575+ Crowbar.option (Crowbar.pair dir op) (* None means yield *)
7676+7777+(* A [Path] is one direction (either server-to-client or client-to-server).
7878+ The two paths can be tested mostly independently (except for shutdown at the moment). *)
7979+module Path : sig
8080+ type t
8181+8282+ val create :
8383+ sender:(Tls_eio.t, exn) result Promise.t ->
8484+ receiver:(Tls_eio.t, exn) result Promise.t ->
8585+ transmit:(transmit_amount -> unit) ->
8686+ dir -> string -> t
8787+ (** Create a test driver for one direction, from [sender] to [receiver].
8888+ [transmit n] causes [n] bytes to be transferred over the mock network. *)
8989+9090+ val close : t -> unit
9191+ (** [close t] causes the sender to close the socket for sending.
9292+ Futher send operations will be ignored. *)
9393+9494+ val run : t -> unit
9595+ (** Run the send and receive fibers. Returns once the receiver has read EOF. *)
9696+9797+ val enqueue : t -> op -> unit
9898+ (** Send a command to the send or receive fiber (depending on [op]). *)
9999+end = struct
100100+ type t = {
101101+ dir : dir;
102102+ message : string; (* The complete message to be transmitted over this path. *)
103103+ (* We need to construct [t] before the handshake is done, so these are promises: *)
104104+ sender : Tls_eio.t Promise.or_exn;
105105+ receiver : Tls_eio.t Promise.or_exn;
106106+ mutable sent : int; (* Bytes of [message] sent so far *)
107107+ mutable recv : int; (* Bytes of [message] received so far *)
108108+ send_commands : [`Send of int | `Exit] Eio.Stream.t; (* Commands for the sending fiber *)
109109+ recv_commands : [`Recv | `Drain] Eio.Stream.t; (* Commands for the receiving fiber *)
110110+ transmit : transmit_amount -> unit;
111111+ }
112112+113113+ let pp_dir f t =
114114+ pp_dir f t.dir
115115+116116+ let create ~sender ~receiver ~transmit dir message =
117117+ let send_commands = Eio.Stream.create max_int in
118118+ let recv_commands = Eio.Stream.create max_int in
119119+ { dir; message; sender; receiver; sent = 0; recv = 0;
120120+ send_commands; recv_commands; transmit }
121121+122122+ let shutdown t =
123123+ Eio.Stream.add t.send_commands `Exit
124124+125125+ let close t =
126126+ shutdown t; (* Sender stops sending *)
127127+ t.transmit `Drain; (* Network transmits everything *)
128128+ Eio.Stream.add t.recv_commands `Drain (* Receiver reads everything *)
129129+130130+ let run_send_thread t =
131131+ let sender = Promise.await_exn t.sender in
132132+ Logs.info (fun f -> f "%a: sender ready" pp_dir t);
133133+ let rec aux () =
134134+ match Eio.Stream.take t.send_commands with
135135+ | `Exit ->
136136+ Log.info (fun f -> f "%a: shutdown send (Tls level)" pp_dir t);
137137+ Eio.Flow.shutdown sender `Send
138138+ | `Send len ->
139139+ let available = String.length t.message - t.sent in
140140+ let len = min len available in
141141+ if len > 0 then (
142142+ let msg = Cstruct.of_string ~off:t.sent ~len t.message in
143143+ t.sent <- t.sent + len;
144144+ Log.info (fun f -> f "%a: sending %S" pp_dir t (Cstruct.to_string msg));
145145+ Eio.Flow.write sender [msg];
146146+ );
147147+ aux ()
148148+ in
149149+ aux()
150150+151151+ let run_recv_thread t =
152152+ let recv = Promise.await_exn t.receiver in
153153+ Logs.info (fun f -> f "%a: receiver ready" pp_dir t);
154154+ try
155155+ let drain = ref false in
156156+ while true do
157157+ if !drain = false then (
158158+ begin match Eio.Stream.take t.recv_commands with
159159+ | `Recv -> ()
160160+ | `Drain -> drain := true
161161+ end
162162+ );
163163+ let buf = Cstruct.create 4096 in
164164+ let got = Eio.Flow.single_read recv buf in
165165+ let received = Cstruct.to_string buf ~len:got in
166166+ Log.info (fun f -> f "%a: received %S" pp_dir t received);
167167+ let expected = String.sub t.message t.recv got in
168168+ if received <> expected then
169169+ Fmt.failwith "%a: excepted %S but got %S!" pp_dir t expected received;
170170+ t.recv <- t.recv + got
171171+ done
172172+ with End_of_file ->
173173+ if t.recv <> t.sent then (
174174+ Fmt.failwith "%a: Sender sent %d bytes, but receiver got EOF after reading only %d"
175175+ pp_dir t
176176+ t.sent
177177+ t.recv
178178+ );
179179+ Log.info (fun f -> f "%a: recv thread done (got EOF)" pp_dir t)
180180+181181+ let run t =
182182+ Fiber.both
183183+ (fun () -> run_send_thread t)
184184+ (fun () -> run_recv_thread t)
185185+186186+ let pp_amount f = function
187187+ | `Bytes n -> Fmt.pf f "%d bytes" n
188188+ | `Drain -> Fmt.string f "all bytes"
189189+190190+ let enqueue t = function
191191+ | Send i->
192192+ Log.info (fun f -> f "%a: enqueue send %d bytes of plaintext" pp_dir t i);
193193+ Eio.Stream.add t.send_commands @@ `Send i;
194194+ | Recv ->
195195+ Log.info (fun f -> f "%a: enqueue read from Tls" pp_dir t);
196196+ Eio.Stream.add t.recv_commands @@ `Recv;
197197+ | Transmit i ->
198198+ Log.info (fun f -> f "%a: enqueue transmit %a over network" pp_dir t pp_amount i);
199199+ t.transmit i
200200+ | Shutdown_send ->
201201+ Log.info (fun f -> f "%a: enqueue shutdown send" pp_dir t);
202202+ shutdown t
203203+end
204204+205205+module Config : sig
206206+ val client : Tls.Config.client
207207+ val server : Tls.Config.server
208208+end = struct
209209+ let null_auth ?ip:_ ~host:_ _ = Ok None
210210+211211+ let client =
212212+ Result.get_ok (Tls.Config.client ~authenticator:null_auth ())
213213+214214+ let read_file path =
215215+ let ch = open_in_bin path in
216216+ let len = in_channel_length ch in
217217+ let data = really_input_string ch len in
218218+ close_in ch;
219219+ data
220220+221221+ let server =
222222+ let certs = Result.get_ok (X509.Certificate.decode_pem_multiple (read_file "server.pem")) in
223223+ let pk = Result.get_ok (X509.Private_key.decode_pem (read_file "server.key")) in
224224+ let certificates = `Single (certs, pk) in
225225+ Result.get_ok Tls.Config.(server ~version:(`TLS_1_0, `TLS_1_3) ~certificates ~ciphers:Ciphers.supported ())
226226+end
227227+228228+let dispatch_commands ~to_server ~to_client actions =
229229+ let rec aux = function
230230+ | [] ->
231231+ Log.info (fun f -> f "dispatch_commands: done");
232232+ Path.close to_client;
233233+ Path.close to_server
234234+ | None :: xs ->
235235+ Fiber.yield (); aux xs
236236+ | Some (dir, op) :: xs ->
237237+ let path =
238238+ match dir with
239239+ | To_server-> to_server
240240+ | To_client -> to_client
241241+ in
242242+ Path.enqueue path op;
243243+ aux xs
244244+ in
245245+ aux actions
246246+247247+(* In some runs we automatically perform these actions first, which allows the handshake to complete.
248248+ This lets the fuzz tester get to the interesting cases more quickly. *)
249249+let quickstart_actions = [
250250+ Some (To_server, Transmit (`Bytes 4096));
251251+ None; (* Client sends handshake *)
252252+ None; (* Server reads handshake *)
253253+ Some (To_client, Transmit (`Bytes 4096));
254254+ None; (* Server replies to handshake *)
255255+ None; (* Client reads reply *)
256256+ Some (To_server, Transmit (`Bytes 4096));
257257+ None; (* Client sends final part *)
258258+ None; (* Server receives it *)
259259+ Some (To_client, Recv);
260260+ Some (To_server, Recv);
261261+]
262262+263263+let main client_message server_message quickstart actions =
264264+ let actions =
265265+ if quickstart then quickstart_actions @ actions
266266+ else actions
267267+ in
268268+ Eio_mock.Backend.run @@ fun () ->
269269+ Switch.run @@ fun sw ->
270270+ let insecure_test_rng = Mirage_crypto_rng.create (module Test_rng) in
271271+ Mirage_crypto_rng.set_default_generator insecure_test_rng;
272272+ let client_socket, server_socket = Mock_socket.create_pair () in
273273+ let server_flow = Fiber.fork_promise ~sw (fun () -> Tls_eio.server_of_flow Config.server server_socket) in
274274+ let client_flow = Fiber.fork_promise ~sw (fun () -> Tls_eio.client_of_flow Config.client client_socket) in
275275+ let to_server =
276276+ Path.create
277277+ ~sender:client_flow
278278+ ~receiver:server_flow
279279+ ~transmit:(Mock_socket.transmit client_socket)
280280+ To_server client_message in
281281+ let to_client =
282282+ Path.create
283283+ ~sender:server_flow
284284+ ~receiver:client_flow
285285+ ~transmit:(Mock_socket.transmit server_socket)
286286+ To_client server_message
287287+ in
288288+ Fiber.all [
289289+ (fun () -> dispatch_commands actions ~to_server ~to_client);
290290+ (fun () -> Path.run to_server);
291291+ (fun () -> Path.run to_client);
292292+ ]
293293+294294+let () =
295295+ Logs.set_level (Some Warning);
296296+ Logs.set_reporter (Logs_fmt.reporter ());
297297+ Crowbar.(add_test ~name:"random ops" [bytes; bytes; bool; list action] main)
+94
vendor/opam/tls/eio/tests/mock_socket.ml
···11+open Eio.Std
22+33+module W = Eio.Buf_write
44+55+let src = Logs.Src.create "mock-socket" ~doc:"Test socket"
66+module Log = (val Logs.src_log src : Logs.LOG)
77+88+type transmit_amount = [`Bytes of int | `Drain]
99+1010+type ty = [`Mock_tls | Eio.Flow.two_way_ty | Eio.Resource.close_ty]
1111+type t = ty r
1212+1313+let rec takev len = function
1414+ | [] -> []
1515+ | x :: xs ->
1616+ if len = 0 then []
1717+ else if Cstruct.length x >= len then [Cstruct.sub x 0 len]
1818+ else x :: takev (len - Cstruct.length x) xs
1919+2020+module Impl = struct
2121+ type t = {
2222+ to_peer : W.t;
2323+ from_peer : W.t;
2424+ label : string;
2525+ output_sizes : transmit_amount Eio.Stream.t;
2626+ }
2727+2828+ let create ~to_peer ~from_peer label = {
2929+ to_peer;
3030+ from_peer;
3131+ label;
3232+ output_sizes = Eio.Stream.create max_int;
3333+ }
3434+3535+ let transmit t x =
3636+ Eio.Stream.add t.output_sizes x
3737+3838+ let single_write t bufs =
3939+ let size =
4040+ match Eio.Stream.take t.output_sizes with
4141+ | `Drain -> Eio.Stream.add t.output_sizes `Drain; Cstruct.lenv bufs
4242+ | `Bytes size -> size
4343+ in
4444+ let bufs = takev size bufs in
4545+ List.iter (W.cstruct t.to_peer) bufs;
4646+ let len = Cstruct.lenv bufs in
4747+ Log.info (fun f -> f "%s: wrote %d bytes to network" t.label len);
4848+ len
4949+5050+ let copy t ~src = Eio.Flow.Pi.simple_copy ~single_write t ~src
5151+5252+ let single_read t buf =
5353+ let batch = W.await_batch t.from_peer in
5454+ let got, _ = Cstruct.fillv ~src:batch ~dst:buf in
5555+ Log.info (fun f -> f "%s: read %d bytes from network" t.label got);
5656+ W.shift t.from_peer got;
5757+ got
5858+5959+ let shutdown t = function
6060+ | `Send ->
6161+ Log.info (fun f -> f "%s: close writer" t.label);
6262+ W.close t.to_peer
6363+ | _ -> failwith "Not implemented"
6464+6565+ let close t =
6666+ Log.info (fun f -> f "%s: close connection" t.label)
6767+6868+ let read_methods = []
6969+7070+ type (_, _, _) Eio.Resource.pi += Raw : ('t, 't -> t, ty) Eio.Resource.pi
7171+ let raw (Eio.Resource.T (t, ops)) = Eio.Resource.get ops Raw t
7272+end
7373+7474+let handler =
7575+ Eio.Resource.handler (
7676+ H (Impl.Raw, Fun.id) ::
7777+ H (Eio.Resource.Close, Impl.close) ::
7878+ Eio.Resource.bindings (Eio.Flow.Pi.two_way (module Impl))
7979+ )
8080+8181+let transmit t x =
8282+ let t = Impl.raw t in
8383+ Impl.transmit t x
8484+8585+let create ~from_peer ~to_peer label =
8686+ let t = Impl.create ~from_peer ~to_peer label in
8787+ Eio.Resource.T (t, handler)
8888+8989+let create_pair () =
9090+ let to_a = W.create 100 in
9191+ let to_b = W.create 100 in
9292+ let a = create ~from_peer:to_a ~to_peer:to_b "client" in
9393+ let b = create ~from_peer:to_b ~to_peer:to_a "server" in
9494+ a, b
+13
vendor/opam/tls/eio/tests/mock_socket.mli
···11+open Eio.Std
22+33+type transmit_amount = [
44+ | `Bytes of int (* Send the next n bytes of data *)
55+ | `Drain (* Transmit all data immediately from now on *)
66+]
77+88+type t = [`Mock_tls | Eio.Flow.two_way_ty | Eio.Resource.close_ty] r
99+1010+val create_pair : unit -> t * t
1111+(** Create a pair of sockets [client, server], such that writes to one can be read from the other. *)
1212+1313+val transmit : t -> transmit_amount -> unit
+21
vendor/opam/tls/eio/tests/test_rng.ml
···11+(* Insecure predictable RNG for fuzz testing. *)
22+33+type g = int ref
44+55+let block = 1
66+77+let create ?time:_ () = ref 1234
88+99+let generate_into ~g buf ~off n =
1010+ for i = off to off + n - 1 do
1111+ Bytes.set_uint8 buf i !g;
1212+ g := !g + 1
1313+ done
1414+1515+let reseed ~g:_ _ = ()
1616+1717+let accumulate ~g:_ _ = `Acc ignore
1818+1919+let seeded ~g:_ = true
2020+2121+let pools = 0
+114
vendor/opam/tls/eio/tests/tls_eio.md
···11+```ocaml
22+# #require "digestif.c";;
33+# #require "eio_main";;
44+# #require "tls-eio";;
55+# #require "mirage-crypto-rng.unix";;
66+```
77+88+```ocaml
99+open Eio.Std
1010+1111+module Flow = Eio.Flow
1212+```
1313+1414+## Test client
1515+1616+```ocaml
1717+let null_auth ?ip:_ ~host:_ _ = Ok None
1818+1919+let mypsk = ref None
2020+2121+let ticket_cache = {
2222+ Tls.Config.lookup = (fun _ -> None) ;
2323+ ticket_granted = (fun psk epoch -> mypsk := Some (psk, epoch)) ;
2424+ lifetime = 0l ;
2525+ timestamp = Ptime_clock.now
2626+}
2727+2828+let test_client ~net (host, service) =
2929+ match Eio.Net.getaddrinfo_stream net host ~service with
3030+ | [] -> failwith "No addresses found!"
3131+ | addr :: _ ->
3232+ let authenticator = null_auth in
3333+ Switch.run @@ fun sw ->
3434+ let socket = Eio.Net.connect ~sw net addr in
3535+ let flow =
3636+ let host =
3737+ Result.to_option
3838+ (Result.bind (Domain_name.of_string host) Domain_name.host)
3939+ in
4040+ Tls_eio.client_of_flow
4141+ (Result.get_ok Tls.Config.(client ~version:(`TLS_1_0, `TLS_1_3) ?cached_ticket:!mypsk ~ticket_cache ~authenticator ~ciphers:Ciphers.supported ()))
4242+ ?host socket
4343+ in
4444+ let req = String.concat "\r\n" [
4545+ "GET / HTTP/1.1" ; "Host: " ^ host ; "Connection: close" ; "" ; ""
4646+ ] in
4747+ Flow.copy_string req flow;
4848+ let r = Eio.Buf_read.of_flow flow ~max_size:max_int in
4949+ let line = Eio.Buf_read.take 3 r in
5050+ traceln "client <- %s" line;
5151+ Eio.Resource.close flow;
5252+ traceln "client done."
5353+```
5454+5555+## Test server
5656+5757+```ocaml
5858+let server_config dir =
5959+ let ( / ) = Eio.Path.( / ) in
6060+ let certificate =
6161+ X509_eio.private_of_pems
6262+ ~cert:(dir / "server.pem")
6363+ ~priv_key:(dir / "server.key")
6464+ in
6565+ let ec_certificate =
6666+ X509_eio.private_of_pems
6767+ ~cert:(dir / "server-ec.pem")
6868+ ~priv_key:(dir / "server-ec.key")
6969+ in
7070+ let certificates = `Multiple [ certificate ; ec_certificate ] in
7171+ Result.get_ok Tls.Config.(server ~version:(`TLS_1_0, `TLS_1_3) ~certificates ~ciphers:Ciphers.supported ())
7272+7373+let serve_ssl ~config server_s callback =
7474+ Switch.run @@ fun sw ->
7575+ let client, addr = Eio.Net.accept ~sw server_s in
7676+ let flow = Tls_eio.server_of_flow config client in
7777+ traceln "server -> connect";
7878+ callback flow addr
7979+```
8080+8181+## Test case
8282+8383+```ocaml
8484+# Eio_main.run @@ fun env ->
8585+ let net = env#net in
8686+ let certificates_dir = env#cwd in
8787+ Mirage_crypto_rng_unix.use_default ();
8888+ Switch.run @@ fun sw ->
8989+ let addr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 4433) in
9090+ let listening_socket = Eio.Net.listen ~sw net ~backlog:5 ~reuse_addr:true addr in
9191+ (* Eio.Time.with_timeout_exn env#clock 0.1 @@ fun () -> *)
9292+ Fiber.both
9393+ (fun () ->
9494+ traceln "server -> start @@ %a" Eio.Net.Sockaddr.pp addr;
9595+ let config = server_config certificates_dir in
9696+ serve_ssl ~config listening_socket @@ fun flow _addr ->
9797+ traceln "handler accepted";
9898+ let r = Eio.Buf_read.of_flow flow ~max_size:max_int in
9999+ let line = Eio.Buf_read.line r in
100100+ traceln "handler + %s" line;
101101+ Flow.copy_string line flow
102102+ )
103103+ (fun () ->
104104+ test_client ~net ("127.0.0.1", "4433")
105105+ )
106106+ ;;
107107++server -> start @ tcp:127.0.0.1:4433
108108++server -> connect
109109++handler accepted
110110++handler + GET / HTTP/1.1
111111++client <- GET
112112++client done.
113113+- : unit = ()
114114+```
+250
vendor/opam/tls/eio/tls_eio.ml
···11+open Eio.Std
22+33+module Flow = Eio.Flow
44+55+exception Tls_alert of Tls.Packet.alert_type
66+exception Tls_failure of Tls.Engine.failure
77+88+type Eio.Exn.Backend.t += Tls_socket_closed
99+let () = Eio.Exn.Backend.register_pp (fun f -> function
1010+ | Tls_socket_closed -> Fmt.pf f "TLS_socket_closed"; true
1111+ | _ -> false
1212+ )
1313+1414+type ty = [ `Tls | Eio.Flow.two_way_ty | Eio.Resource.close_ty ]
1515+type t = ty r
1616+1717+module Raw = struct
1818+1919+ (* We could replace [`Eof] with [`Error End_of_file] and then use
2020+ a regular [result] type here. *)
2121+ type t = {
2222+ flow : [Flow.two_way_ty | Eio.Resource.close_ty] r;
2323+ mutable state : [ `Active of Tls.Engine.state
2424+ | `Read_closed of Tls.Engine.state
2525+ | `Write_closed of Tls.Engine.state
2626+ | `Closed
2727+ | `Error of exn ] ;
2828+ mutable linger : Cstruct.t option ;
2929+ recv_buf : Cstruct.t ;
3030+ }
3131+3232+ let half_close state mode =
3333+ match state, mode with
3434+ | `Active tls, `read -> `Read_closed tls
3535+ | `Active tls, `write -> `Write_closed tls
3636+ | `Active _, `read_write -> `Closed
3737+ | `Read_closed tls, `read -> `Read_closed tls
3838+ | `Read_closed _, (`write | `read_write) -> `Closed
3939+ | `Write_closed tls, `write -> `Write_closed tls
4040+ | `Write_closed _, (`read | `read_write) -> `Closed
4141+ | (`Closed | `Error _) as e, (`read | `write | `read_write) -> e
4242+4343+ let inject_state tls = function
4444+ | `Active _ -> `Active tls
4545+ | `Read_closed _ -> `Read_closed tls
4646+ | `Write_closed _ -> `Write_closed tls
4747+ | (`Closed | `Error _) as e -> e
4848+4949+ let write_t t s =
5050+ try Flow.copy_string s t.flow
5151+ with exn ->
5252+ (match t.state with
5353+ | `Error _ -> ()
5454+ | _ -> t.state <- `Error exn) ;
5555+ raise exn
5656+5757+ let try_write_t t cs =
5858+ try write_t t cs
5959+ with _ -> Eio.Fiber.check () (* Error is in [t.state] *)
6060+6161+ let rec read_react t =
6262+6363+ let handle tls buf =
6464+ match Tls.Engine.handle_tls tls buf with
6565+ | Ok (state', eof, `Response resp, `Data data) ->
6666+ let state' = inject_state state' t.state in
6767+ let state' = Option.(value ~default:state' (map (fun `Eof -> half_close state' `read) eof)) in
6868+ t.state <- state' ;
6969+ Option.iter (try_write_t t) resp;
7070+ Option.map Cstruct.of_string data
7171+7272+ | Error (fail, `Response resp) ->
7373+ t.state <- `Error (match fail with `Alert a -> Tls_alert a | f -> Tls_failure f) ;
7474+ write_t t resp; read_react t
7575+ in
7676+7777+ match t.state with
7878+ | `Error e -> raise e
7979+ | `Closed
8080+ | `Read_closed _ -> raise End_of_file
8181+ | _ ->
8282+ match Flow.single_read t.flow t.recv_buf with
8383+ | exception End_of_file ->
8484+ t.state <- half_close t.state `read;
8585+ raise End_of_file
8686+ | exception exn ->
8787+ (match t.state with
8888+ | `Error _ -> ()
8989+ | _ -> t.state <- `Error exn) ;
9090+ raise exn
9191+ | n ->
9292+ match t.state with
9393+ | `Error e -> raise e
9494+ | `Active tls | `Read_closed tls | `Write_closed tls ->
9595+ handle tls (Cstruct.to_string t.recv_buf ~off:0 ~len:n)
9696+ | `Closed -> raise End_of_file
9797+9898+ let rec single_read t buf =
9999+100100+ let writeout res =
101101+ let open Cstruct in
102102+ let rlen = length res in
103103+ let n = min (length buf) rlen in
104104+ blit res 0 buf 0 n ;
105105+ t.linger <-
106106+ (if n < rlen then Some (sub res n (rlen - n)) else None) ;
107107+ n in
108108+109109+ match t.linger with
110110+ | Some res -> writeout res
111111+ | None ->
112112+ match read_react t with
113113+ | None -> single_read t buf
114114+ | Some res -> writeout res
115115+116116+ let writev t css =
117117+ match t.state with
118118+ | `Error err -> raise err
119119+ | `Write_closed _ | `Closed -> raise (Eio.Net.err (Connection_reset Tls_socket_closed))
120120+ | `Active tls | `Read_closed tls ->
121121+ let css = List.map Cstruct.to_string css in
122122+ match Tls.Engine.send_application_data tls css with
123123+ | Some (tls, tlsdata) ->
124124+ ( t.state <- inject_state tls t.state ; write_t t tlsdata )
125125+ | None -> invalid_arg "tls: write: socket not ready"
126126+127127+ let single_write t bufs =
128128+ writev t bufs;
129129+ Cstruct.lenv bufs
130130+131131+ (*
132132+ * XXX bad XXX
133133+ * This is a point that should particularly be protected from concurrent r/w.
134134+ * Doing this before a `t` is returned is safe; redoing it during rekeying is
135135+ * not, as the API client already sees the `t` and can mistakenly interleave
136136+ * writes while this is in progress.
137137+ * *)
138138+ let rec drain_handshake t =
139139+ let push_linger t mcs =
140140+ match (mcs, t.linger) with
141141+ | (None, _) -> ()
142142+ | (scs, None) -> t.linger <- scs
143143+ | (Some cs, Some l) -> t.linger <- Some (Cstruct.append l cs)
144144+ in
145145+ match t.state with
146146+ | `Active tls when not (Tls.Engine.handshake_in_progress tls) ->
147147+ t
148148+ | _ ->
149149+ let cs = read_react t in
150150+ push_linger t cs; drain_handshake t
151151+152152+ let reneg ?authenticator ?acceptable_cas ?cert ?(drop = true) t =
153153+ match t.state with
154154+ | `Error err -> raise err
155155+ | `Closed | `Read_closed _ | `Write_closed _ -> invalid_arg "tls: closed socket"
156156+ | `Active tls ->
157157+ match Tls.Engine.reneg ?authenticator ?acceptable_cas ?cert tls with
158158+ | None -> invalid_arg "tls: can't renegotiate"
159159+ | Some (tls', buf) ->
160160+ if drop then t.linger <- None ;
161161+ t.state <- inject_state tls' t.state ;
162162+ write_t t buf;
163163+ ignore (drain_handshake t : t)
164164+165165+ let key_update ?request t =
166166+ match t.state with
167167+ | `Error err -> raise err
168168+ | `Write_closed _ | `Closed -> invalid_arg "tls: closed socket"
169169+ | `Active tls | `Read_closed tls ->
170170+ match Tls.Engine.key_update ?request tls with
171171+ | Error f -> Fmt.invalid_arg "tls: can't update key: %a" Tls.Engine.pp_failure f
172172+ | Ok (tls', buf) ->
173173+ t.state <- inject_state tls' t.state ;
174174+ write_t t buf
175175+176176+ let shutdown t = function
177177+ | `Receive -> ()
178178+ | `Send | `All ->
179179+ match t.state with
180180+ | `Active tls | `Read_closed tls ->
181181+ let tls', buf = Tls.Engine.send_close_notify tls in
182182+ t.state <- inject_state tls' (half_close t.state `write) ;
183183+ write_t t buf
184184+ | _ -> ()
185185+186186+ let server_of_flow config flow =
187187+ drain_handshake {
188188+ state = `Active (Tls.Engine.server config) ;
189189+ flow = (flow :> [Flow.two_way_ty | Eio.Resource.close_ty] r) ;
190190+ linger = None ;
191191+ recv_buf = Cstruct.create 4096
192192+ }
193193+194194+ let client_of_flow config ?host flow =
195195+ let config' = match host with
196196+ | None -> config
197197+ | Some host -> Tls.Config.peer config host
198198+ in
199199+ let (tls, init) = Tls.Engine.client config' in
200200+ let t = {
201201+ state = `Active tls ;
202202+ flow = (flow :> [Flow.two_way_ty | Eio.Resource.close_ty] r);
203203+ linger = None ;
204204+ recv_buf = Cstruct.create 4096
205205+ } in
206206+ write_t t init;
207207+ drain_handshake t
208208+209209+210210+ let epoch t =
211211+ match t.state with
212212+ | `Active tls | `Read_closed tls | `Write_closed tls -> Tls.Engine.epoch tls
213213+ | `Closed | `Error _ -> Error ()
214214+215215+ let copy t ~src = Eio.Flow.Pi.simple_copy ~single_write t ~src
216216+217217+ let read_methods = []
218218+219219+ let close t = Eio.Resource.close t.flow
220220+221221+ type (_, _, _) Eio.Resource.pi += T : ('t, 't -> t, ty) Eio.Resource.pi
222222+end
223223+224224+let raw (Eio.Resource.T (t, ops)) = Eio.Resource.get ops Raw.T t
225225+226226+let handler =
227227+ Eio.Resource.handler [
228228+ H (Eio.Flow.Pi.Source, (module Raw));
229229+ H (Eio.Flow.Pi.Sink, (module Raw));
230230+ H (Eio.Flow.Pi.Shutdown, (module Raw));
231231+ H (Eio.Resource.Close, Raw.close);
232232+ H (Raw.T, Fun.id);
233233+ ]
234234+235235+let of_t t = Eio.Resource.T (t, handler)
236236+237237+let server_of_flow config flow = Raw.server_of_flow config flow |> of_t
238238+let client_of_flow config ?host flow = Raw.client_of_flow config ?host flow |> of_t
239239+240240+let reneg ?authenticator ?acceptable_cas ?cert ?drop (t:t) = Raw.reneg ?authenticator ?acceptable_cas ?cert ?drop (raw t)
241241+let key_update ?request (t:t) = Raw.key_update ?request (raw t)
242242+let epoch (t:t) = Raw.epoch (raw t)
243243+244244+let () =
245245+ Printexc.register_printer (function
246246+ | Tls_alert typ ->
247247+ Some ("TLS alert from peer: " ^ Tls.Packet.alert_type_to_string typ)
248248+ | Tls_failure f ->
249249+ Some ("TLS failure: " ^ Tls.Engine.string_of_failure f)
250250+ | _ -> None)
+59
vendor/opam/tls/eio/tls_eio.mli
···11+(** Effectful operations using Eio for pure TLS.
22+33+ The pure TLS is state and buffer in, state and buffer out. This
44+ module uses Eio for communication over the network. *)
55+66+open Eio.Std
77+88+(** [Tls_alert] exception received from the other endpoint *)
99+exception Tls_alert of Tls.Packet.alert_type
1010+1111+(** [Tls_failure] exception while processing incoming data *)
1212+exception Tls_failure of Tls.Engine.failure
1313+1414+type t = [ `Tls | Eio.Flow.two_way_ty | Eio.Resource.close_ty ] r
1515+1616+(** {2 Constructors} *)
1717+1818+(** [server_of_flow server flow] is [t], after server-side TLS
1919+ handshake of [flow] using [server] configuration.
2020+2121+ You must ensure a RNG is installed while using TLS, e.g. using [Mirage_crypto_rng_unix.use_default ()].
2222+ Ideally, this would be part of the [server] config so you couldn't forget it,
2323+ but for now you'll get a runtime error if you forget. *)
2424+val server_of_flow :
2525+ Tls.Config.server ->
2626+ [> Eio.Flow.two_way_ty | Eio.Resource.close_ty] r -> t
2727+2828+(** [client_of_flow client ~host fd] is [t], after client-side
2929+ TLS handshake of [flow] using [client] configuration and [host].
3030+3131+ You must ensure a RNG is installed while using TLS, e.g. using [Mirage_crypto_rng_unix.use_default ()].
3232+ Ideally, this would be part of the [client] config so you couldn't forget it,
3333+ but for now you'll get a runtime error if you forget. *)
3434+val client_of_flow :
3535+ Tls.Config.client -> ?host:[ `host ] Domain_name.t ->
3636+ [> Eio.Flow.two_way_ty | Eio.Resource.close_ty] r -> t
3737+3838+(** {2 Control of TLS features} *)
3939+4040+(** [reneg ~authenticator ~acceptable_cas ~cert ~drop t] renegotiates the
4141+ session, and blocks until the renegotiation finished. Optionally, a new
4242+ [authenticator] and [acceptable_cas] can be used. The own certificate can
4343+ be adjusted by [cert]. If [drop] is [true] (the default),
4444+ application data received before the renegotiation finished is dropped. *)
4545+val reneg :
4646+ ?authenticator:X509.Authenticator.t ->
4747+ ?acceptable_cas:X509.Distinguished_name.t list ->
4848+ ?cert:Tls.Config.own_cert ->
4949+ ?drop:bool ->
5050+ t -> unit
5151+5252+(** [key_update ~request t] updates the traffic key and requests a traffic key
5353+ update from the peer if [request] is provided and [true] (the default).
5454+ This is only supported in TLS 1.3. *)
5555+val key_update : ?request:bool -> t -> unit
5656+5757+(** [epoch t] returns [epoch], which contains information of the
5858+ active session. *)
5959+val epoch : t -> (Tls.Core.epoch_data, unit) result
+89
vendor/opam/tls/eio/x509_eio.ml
···11+open Eio.Std
22+33+module Path = Eio.Path
44+55+let (</>) = Path.( / )
66+77+let extension str =
88+ let n = String.length str in
99+ let rec scan = function
1010+ | i when i = 0 -> None
1111+ | i when str.[i - 1] = '.' ->
1212+ Some (String.sub str i (n - i))
1313+ | i -> scan (pred i) in
1414+ scan n
1515+1616+1717+let private_of_pems ~cert ~priv_key =
1818+ let certs =
1919+ try
2020+ let pem = Path.load cert in
2121+ match X509.Certificate.decode_pem_multiple pem with
2222+ | Ok cs -> cs
2323+ | Error (`Msg m) -> invalid_arg ("failed to parse certificates " ^ m)
2424+ with Invalid_argument m ->
2525+ Fmt.failwith "Private certificates %a: %s" Path.pp cert m
2626+ in
2727+ let pk =
2828+ try
2929+ let pem = Path.load priv_key in
3030+ match X509.Private_key.decode_pem pem with
3131+ | Ok key -> key
3232+ | Error (`Msg m) -> invalid_arg ("failed to parse private key " ^ m)
3333+ with Invalid_argument m ->
3434+ Fmt.failwith "Private key (%a): %s" Path.pp priv_key m
3535+ in
3636+ (certs, pk)
3737+3838+let certs_of_pem path =
3939+ try
4040+ let pem = Path.load path in
4141+ match X509.Certificate.decode_pem_multiple pem with
4242+ | Ok cs -> cs
4343+ | Error (`Msg m) -> invalid_arg ("failed to parse certificates " ^ m)
4444+ with Invalid_argument m ->
4545+ Fmt.failwith "Certificates in %a: %s" Path.pp path m
4646+4747+let certs_of_pem_dir path =
4848+ Path.read_dir path
4949+ |> List.filter (fun file -> extension file = Some "crt")
5050+ |> Fiber.List.map (fun file -> certs_of_pem (path </> file))
5151+ |> List.concat
5252+5353+let crl_of_pem path =
5454+ try
5555+ let data = Path.load path in
5656+ match X509.CRL.decode_der data with
5757+ | Ok cs -> cs
5858+ | Error (`Msg m) -> invalid_arg ("failed to parse CRL " ^ m)
5959+ with Invalid_argument m ->
6060+ Fmt.failwith "CRL in %a: %s" Path.pp path m
6161+6262+let crls_of_pem_dir path =
6363+ Path.read_dir path
6464+ |> Fiber.List.map (fun file -> crl_of_pem (path </> file))
6565+6666+(* Would be better to take an Eio.Time.clock here, but that API is likely to change soon. *)
6767+let authenticator ?allowed_hashes ?crls param =
6868+ let time () = Some (Ptime_clock.now ()) in
6969+ let of_cas cas =
7070+ let crls = Option.map crls_of_pem_dir crls in
7171+ X509.Authenticator.chain_of_trust ?allowed_hashes ?crls ~time cas
7272+ and dotted_hex_to_cs hex =
7373+ Cstruct.to_string (Cstruct.of_hex (String.map (function ':' -> ' ' | x -> x) hex))
7474+ and fingerp hash fingerprint =
7575+ X509.Authenticator.key_fingerprint ~time ~hash ~fingerprint
7676+ and cert_fingerp hash fingerprint =
7777+ X509.Authenticator.cert_fingerprint ~time ~hash ~fingerprint
7878+ in
7979+ match param with
8080+ | `Ca_file path -> certs_of_pem path |> of_cas
8181+ | `Ca_dir path -> certs_of_pem_dir path |> of_cas
8282+ | `Key_fingerprint (hash, fp) -> fingerp hash fp
8383+ | `Hex_key_fingerprint (hash, fp) ->
8484+ let fp = dotted_hex_to_cs fp in
8585+ fingerp hash fp
8686+ | `Cert_fingerprint (hash, fp) -> cert_fingerp hash fp
8787+ | `Hex_cert_fingerprint (hash, fp) ->
8888+ let fp = dotted_hex_to_cs fp in
8989+ cert_fingerp hash fp
+26
vendor/opam/tls/eio/x509_eio.mli
···11+(** X.509 certificate handling using Eio. *)
22+33+(** [private_of_pems ~cert ~priv_key] is [priv], after reading the
44+ private key and certificate chain from the given PEM-encoded
55+ files. *)
66+val private_of_pems : cert:_ Eio.Path.t -> priv_key:_ Eio.Path.t -> Tls.Config.certchain
77+88+(** [certs_of_pem file] is [certificates], which are read from the
99+ PEM-encoded [file]. *)
1010+val certs_of_pem : _ Eio.Path.t -> X509.Certificate.t list
1111+1212+(** [certs_of_pem_dir dir] is [certificates], which are read from all
1313+ PEM-encoded files in [dir]. *)
1414+val certs_of_pem_dir : _ Eio.Path.t -> X509.Certificate.t list
1515+1616+(** [authenticator methods] constructs an [authenticator] using the
1717+ specified method and data. *)
1818+val authenticator : ?allowed_hashes:Digestif.hash' list -> ?crls:_ Eio.Path.t ->
1919+ [ `Ca_file of _ Eio.Path.t
2020+ | `Ca_dir of _ Eio.Path.t
2121+ | `Key_fingerprint of Digestif.hash' * string
2222+ | `Hex_key_fingerprint of Digestif.hash' * string
2323+ | `Cert_fingerprint of Digestif.hash' * string
2424+ | `Hex_cert_fingerprint of Digestif.hash' * string
2525+ ]
2626+ -> X509.Authenticator.t
+366
vendor/opam/tls/lib/ciphersuite.ml
···11+(** Ciphersuite definitions and some helper functions. *)
22+33+(** sum type of all possible key exchange methods *)
44+type key_exchange_algorithm_dhe = [ `FFDHE | `ECDHE ]
55+type key_exchange_algorithm = [ key_exchange_algorithm_dhe | `RSA ]
66+77+let pp_key_exchange_algorithm_dhe ppf = function
88+ | `FFDHE -> Fmt.string ppf "FFDHE"
99+ | `ECDHE -> Fmt.string ppf "ECDHE"
1010+1111+let pp_key_exchange_algorithm ppf = function
1212+ | #key_exchange_algorithm_dhe as d -> pp_key_exchange_algorithm_dhe ppf d
1313+ | `RSA -> Fmt.string ppf "RSA"
1414+1515+(** [required_usage kex] is [usage] which a certificate must have if it is used in the given [kex] method *)
1616+let required_usage = function
1717+ | #key_exchange_algorithm_dhe -> `Digital_signature
1818+ | `RSA -> `Key_encipherment
1919+2020+type block_cipher =
2121+ | TRIPLE_DES_EDE_CBC
2222+ | AES_128_CBC
2323+ | AES_256_CBC
2424+2525+let pp_block_cipher ppf = function
2626+ | TRIPLE_DES_EDE_CBC -> Fmt.string ppf "3DES EDE CBC"
2727+ | AES_128_CBC -> Fmt.string ppf "AES128 CBC"
2828+ | AES_256_CBC -> Fmt.string ppf "AES256 CBC"
2929+3030+type aead_cipher =
3131+ | AES_128_CCM
3232+ | AES_256_CCM
3333+ | AES_128_GCM
3434+ | AES_256_GCM
3535+ | CHACHA20_POLY1305
3636+3737+let pp_aead_cipher ppf = function
3838+ | AES_128_CCM -> Fmt.string ppf "AES128 CCM"
3939+ | AES_256_CCM -> Fmt.string ppf "AES256 CCM"
4040+ | AES_128_GCM -> Fmt.string ppf "AES128 GCM"
4141+ | AES_256_GCM -> Fmt.string ppf "AES256 GCM"
4242+ | CHACHA20_POLY1305 -> Fmt.string ppf "CHACHA20 POLY1305"
4343+4444+type payload_protection13 = [ `AEAD of aead_cipher ]
4545+4646+let pp_payload_protection13 ppf = function
4747+ | `AEAD a -> Fmt.pf ppf "AEAD %a" pp_aead_cipher a
4848+4949+type payload_protection = [
5050+ payload_protection13
5151+ | `Block of block_cipher * Digestif.hash'
5252+ ]
5353+5454+let pp_hash ppf = function
5555+ | `MD5 -> Fmt.string ppf "MD5"
5656+ | `SHA1 -> Fmt.string ppf "SHA1"
5757+ | `SHA224 -> Fmt.string ppf "SHA224"
5858+ | `SHA256 -> Fmt.string ppf "SHA256"
5959+ | `SHA384 -> Fmt.string ppf "SHA384"
6060+ | `SHA512 -> Fmt.string ppf "SHA512"
6161+6262+let pp_payload_protection ppf = function
6363+ | #payload_protection13 as p -> pp_payload_protection13 ppf p
6464+ | `Block (b, h) -> Fmt.pf ppf "BLOCK %a %a" pp_block_cipher b pp_hash h
6565+6666+(* this is K_LEN, max 8 N_MIN from RFC5116 sections 5.1 & 5.2 -- as defined in TLS1.3 RFC 8446 Section 5.3 *)
6767+let kn_13 = function
6868+ | AES_128_GCM -> (16, 12)
6969+ | AES_256_GCM -> (32, 12)
7070+ | AES_128_CCM -> (16, 12)
7171+ | AES_256_CCM -> (32, 12)
7272+ | CHACHA20_POLY1305 -> (32, 12)
7373+7474+(** [key_length iv payload_protection] is [(key size, IV size, mac size)] where key IV, and mac sizes are the required bytes for the given [payload_protection] *)
7575+(* NB only used for <= TLS 1.2, IV length for AEAD defined in RFC 5288 Section 3 (for GCM), salt[4] for CCM in RFC 6655 Section 3 *)
7676+let key_length iv pp =
7777+ let mac_size m =
7878+ let module H = (val Digestif.module_of_hash' m) in
7979+ H.digest_size
8080+ in
8181+ match pp with
8282+ | `AEAD AES_128_CCM -> (16, 4 , 0)
8383+ | `AEAD AES_256_CCM -> (32, 4 , 0)
8484+ | `AEAD AES_128_GCM -> (16, 4 , 0)
8585+ | `AEAD AES_256_GCM -> (32, 4 , 0)
8686+ | `AEAD CHACHA20_POLY1305 -> (32, 12, 0)
8787+ | `Block (bc, mac) ->
8888+ let keylen, ivlen = match bc with
8989+ | TRIPLE_DES_EDE_CBC -> (24, 8)
9090+ | AES_128_CBC -> (16, 16)
9191+ | AES_256_CBC -> (32, 16)
9292+ and maclen = mac_size mac
9393+ in
9494+ match iv with
9595+ | None -> (keylen, 0, maclen)
9696+ | Some () -> (keylen, ivlen, maclen)
9797+9898+type ciphersuite13 = [
9999+ | `AES_128_GCM_SHA256
100100+ | `AES_256_GCM_SHA384
101101+ | `CHACHA20_POLY1305_SHA256
102102+ | `AES_128_CCM_SHA256
103103+]
104104+105105+let privprot13 = function
106106+ | `AES_128_GCM_SHA256 -> AES_128_GCM
107107+ | `AES_256_GCM_SHA384 -> AES_256_GCM
108108+ | `CHACHA20_POLY1305_SHA256 -> CHACHA20_POLY1305
109109+ | `AES_128_CCM_SHA256 -> AES_128_CCM
110110+111111+let hash13 = function
112112+ | `AES_128_GCM_SHA256 -> `SHA256
113113+ | `AES_256_GCM_SHA384 -> `SHA384
114114+ | `CHACHA20_POLY1305_SHA256 -> `SHA256
115115+ | `AES_128_CCM_SHA256 -> `SHA256
116116+117117+let any_ciphersuite_to_ciphersuite13 = function
118118+ | Packet.TLS_AES_128_GCM_SHA256 -> Some `AES_128_GCM_SHA256
119119+ | Packet.TLS_AES_256_GCM_SHA384 -> Some `AES_256_GCM_SHA384
120120+ | Packet.TLS_CHACHA20_POLY1305_SHA256 -> Some `CHACHA20_POLY1305_SHA256
121121+ | Packet.TLS_AES_128_CCM_SHA256 -> Some `AES_128_CCM_SHA256
122122+ | _ -> None
123123+124124+type ciphersuite = [
125125+ ciphersuite13
126126+ | `DHE_RSA_WITH_AES_128_GCM_SHA256
127127+ | `DHE_RSA_WITH_AES_256_GCM_SHA384
128128+ | `DHE_RSA_WITH_AES_256_CCM
129129+ | `DHE_RSA_WITH_AES_128_CCM
130130+ | `DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
131131+ | `DHE_RSA_WITH_AES_256_CBC_SHA256
132132+ | `DHE_RSA_WITH_AES_128_CBC_SHA256
133133+ | `DHE_RSA_WITH_AES_256_CBC_SHA
134134+ | `DHE_RSA_WITH_AES_128_CBC_SHA
135135+ | `DHE_RSA_WITH_3DES_EDE_CBC_SHA
136136+ | `ECDHE_RSA_WITH_AES_128_GCM_SHA256
137137+ | `ECDHE_RSA_WITH_AES_256_GCM_SHA384
138138+ | `ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
139139+ | `ECDHE_RSA_WITH_AES_256_CBC_SHA384
140140+ | `ECDHE_RSA_WITH_AES_128_CBC_SHA256
141141+ | `ECDHE_RSA_WITH_AES_256_CBC_SHA
142142+ | `ECDHE_RSA_WITH_AES_128_CBC_SHA
143143+ | `ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
144144+ | `RSA_WITH_AES_256_CBC_SHA256
145145+ | `RSA_WITH_AES_128_CBC_SHA256
146146+ | `RSA_WITH_AES_256_CBC_SHA
147147+ | `RSA_WITH_AES_128_CBC_SHA
148148+ | `RSA_WITH_3DES_EDE_CBC_SHA
149149+ | `RSA_WITH_AES_128_GCM_SHA256
150150+ | `RSA_WITH_AES_256_GCM_SHA384
151151+ | `RSA_WITH_AES_256_CCM
152152+ | `RSA_WITH_AES_128_CCM
153153+ | `ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
154154+ | `ECDHE_ECDSA_WITH_AES_128_CBC_SHA
155155+ | `ECDHE_ECDSA_WITH_AES_256_CBC_SHA
156156+ | `ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
157157+ | `ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
158158+ | `ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
159159+ | `ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
160160+ | `ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
161161+ ]
162162+163163+let ciphersuite_to_ciphersuite13 : ciphersuite -> ciphersuite13 option = function
164164+ | #ciphersuite13 as cs -> Some cs
165165+ | _ -> None
166166+167167+let any_ciphersuite_to_ciphersuite = function
168168+ | Packet.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 -> Some `DHE_RSA_WITH_AES_256_CBC_SHA256
169169+ | Packet.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 -> Some `DHE_RSA_WITH_AES_128_CBC_SHA256
170170+ | Packet.TLS_DHE_RSA_WITH_AES_256_CBC_SHA -> Some `DHE_RSA_WITH_AES_256_CBC_SHA
171171+ | Packet.TLS_DHE_RSA_WITH_AES_128_CBC_SHA -> Some `DHE_RSA_WITH_AES_128_CBC_SHA
172172+ | Packet.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA -> Some `DHE_RSA_WITH_3DES_EDE_CBC_SHA
173173+ | Packet.TLS_RSA_WITH_AES_256_CBC_SHA256 -> Some `RSA_WITH_AES_256_CBC_SHA256
174174+ | Packet.TLS_RSA_WITH_AES_128_CBC_SHA256 -> Some `RSA_WITH_AES_128_CBC_SHA256
175175+ | Packet.TLS_RSA_WITH_AES_256_CBC_SHA -> Some `RSA_WITH_AES_256_CBC_SHA
176176+ | Packet.TLS_RSA_WITH_AES_128_CBC_SHA -> Some `RSA_WITH_AES_128_CBC_SHA
177177+ | Packet.TLS_RSA_WITH_3DES_EDE_CBC_SHA -> Some `RSA_WITH_3DES_EDE_CBC_SHA
178178+ | Packet.TLS_RSA_WITH_AES_128_CCM -> Some `RSA_WITH_AES_128_CCM
179179+ | Packet.TLS_RSA_WITH_AES_256_CCM -> Some `RSA_WITH_AES_256_CCM
180180+ | Packet.TLS_DHE_RSA_WITH_AES_128_CCM -> Some `DHE_RSA_WITH_AES_128_CCM
181181+ | Packet.TLS_DHE_RSA_WITH_AES_256_CCM -> Some `DHE_RSA_WITH_AES_256_CCM
182182+ | Packet.TLS_RSA_WITH_AES_128_GCM_SHA256 -> Some `RSA_WITH_AES_128_GCM_SHA256
183183+ | Packet.TLS_RSA_WITH_AES_256_GCM_SHA384 -> Some `RSA_WITH_AES_256_GCM_SHA384
184184+ | Packet.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 -> Some `DHE_RSA_WITH_AES_128_GCM_SHA256
185185+ | Packet.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 -> Some `DHE_RSA_WITH_AES_256_GCM_SHA384
186186+ | Packet.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 -> Some `ECDHE_RSA_WITH_AES_128_GCM_SHA256
187187+ | Packet.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 -> Some `ECDHE_RSA_WITH_AES_256_GCM_SHA384
188188+ | Packet.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 -> Some `ECDHE_RSA_WITH_AES_256_CBC_SHA384
189189+ | Packet.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 -> Some `ECDHE_RSA_WITH_AES_128_CBC_SHA256
190190+ | Packet.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA -> Some `ECDHE_RSA_WITH_AES_256_CBC_SHA
191191+ | Packet.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA -> Some `ECDHE_RSA_WITH_AES_128_CBC_SHA
192192+ | Packet.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA -> Some `ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
193193+ | Packet.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 -> Some `ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
194194+ | Packet.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 -> Some `DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
195195+ | Packet.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA -> Some `ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
196196+ | Packet.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA -> Some `ECDHE_ECDSA_WITH_AES_128_CBC_SHA
197197+ | Packet.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA -> Some `ECDHE_ECDSA_WITH_AES_256_CBC_SHA
198198+ | Packet.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 -> Some `ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
199199+ | Packet.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 -> Some `ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
200200+ | Packet.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 -> Some `ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
201201+ | Packet.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 -> Some `ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
202202+ | Packet.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 -> Some `ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
203203+ | x -> any_ciphersuite_to_ciphersuite13 x
204204+205205+let ciphersuite_to_any_ciphersuite = function
206206+ | `DHE_RSA_WITH_AES_256_CBC_SHA256 -> Packet.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
207207+ | `DHE_RSA_WITH_AES_128_CBC_SHA256 -> Packet.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
208208+ | `DHE_RSA_WITH_AES_256_CBC_SHA -> Packet.TLS_DHE_RSA_WITH_AES_256_CBC_SHA
209209+ | `DHE_RSA_WITH_AES_128_CBC_SHA -> Packet.TLS_DHE_RSA_WITH_AES_128_CBC_SHA
210210+ | `DHE_RSA_WITH_3DES_EDE_CBC_SHA -> Packet.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
211211+ | `RSA_WITH_AES_256_CBC_SHA256 -> Packet.TLS_RSA_WITH_AES_256_CBC_SHA256
212212+ | `RSA_WITH_AES_128_CBC_SHA256 -> Packet.TLS_RSA_WITH_AES_128_CBC_SHA256
213213+ | `RSA_WITH_AES_256_CBC_SHA -> Packet.TLS_RSA_WITH_AES_256_CBC_SHA
214214+ | `RSA_WITH_AES_128_CBC_SHA -> Packet.TLS_RSA_WITH_AES_128_CBC_SHA
215215+ | `RSA_WITH_3DES_EDE_CBC_SHA -> Packet.TLS_RSA_WITH_3DES_EDE_CBC_SHA
216216+ | `RSA_WITH_AES_128_CCM -> Packet.TLS_RSA_WITH_AES_128_CCM
217217+ | `RSA_WITH_AES_256_CCM -> Packet.TLS_RSA_WITH_AES_256_CCM
218218+ | `DHE_RSA_WITH_AES_128_CCM -> Packet.TLS_DHE_RSA_WITH_AES_128_CCM
219219+ | `DHE_RSA_WITH_AES_256_CCM -> Packet.TLS_DHE_RSA_WITH_AES_256_CCM
220220+ | `RSA_WITH_AES_128_GCM_SHA256 -> Packet.TLS_RSA_WITH_AES_128_GCM_SHA256
221221+ | `RSA_WITH_AES_256_GCM_SHA384 -> Packet.TLS_RSA_WITH_AES_256_GCM_SHA384
222222+ | `DHE_RSA_WITH_AES_128_GCM_SHA256 -> Packet.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
223223+ | `DHE_RSA_WITH_AES_256_GCM_SHA384 -> Packet.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
224224+ | `ECDHE_RSA_WITH_AES_128_GCM_SHA256 -> Packet.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
225225+ | `ECDHE_RSA_WITH_AES_256_GCM_SHA384 -> Packet.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
226226+ | `ECDHE_RSA_WITH_AES_256_CBC_SHA384 -> Packet.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
227227+ | `ECDHE_RSA_WITH_AES_128_CBC_SHA256 -> Packet.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
228228+ | `ECDHE_RSA_WITH_AES_256_CBC_SHA -> Packet.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
229229+ | `ECDHE_RSA_WITH_AES_128_CBC_SHA -> Packet.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
230230+ | `ECDHE_RSA_WITH_3DES_EDE_CBC_SHA -> Packet.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
231231+ | `ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 -> Packet.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
232232+ | `DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 -> Packet.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
233233+ | `AES_128_GCM_SHA256 -> Packet.TLS_AES_128_GCM_SHA256
234234+ | `AES_256_GCM_SHA384 -> Packet.TLS_AES_256_GCM_SHA384
235235+ | `CHACHA20_POLY1305_SHA256 -> Packet.TLS_CHACHA20_POLY1305_SHA256
236236+ | `AES_128_CCM_SHA256 -> Packet.TLS_AES_128_CCM_SHA256
237237+ | `ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA -> Packet.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
238238+ | `ECDHE_ECDSA_WITH_AES_128_CBC_SHA -> Packet.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
239239+ | `ECDHE_ECDSA_WITH_AES_256_CBC_SHA -> Packet.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
240240+ | `ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 -> Packet.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
241241+ | `ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 -> Packet.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
242242+ | `ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 -> Packet.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
243243+ | `ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 -> Packet.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
244244+ | `ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 -> Packet.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
245245+246246+(** [get_kex_privprot ciphersuite] is [(kex, privacy_protection)] where it dissects the [ciphersuite] into a pair containing the key exchange method [kex], and its [privacy_protection] *)
247247+let get_keytype_kex_privprot = function
248248+ | `RSA_WITH_3DES_EDE_CBC_SHA -> (`RSA, `RSA, `Block (TRIPLE_DES_EDE_CBC, `SHA1))
249249+ | `DHE_RSA_WITH_3DES_EDE_CBC_SHA -> (`RSA, `FFDHE, `Block (TRIPLE_DES_EDE_CBC, `SHA1))
250250+ | `RSA_WITH_AES_128_CBC_SHA -> (`RSA, `RSA, `Block (AES_128_CBC, `SHA1))
251251+ | `DHE_RSA_WITH_AES_128_CBC_SHA -> (`RSA, `FFDHE, `Block (AES_128_CBC, `SHA1))
252252+ | `RSA_WITH_AES_256_CBC_SHA -> (`RSA, `RSA, `Block (AES_256_CBC, `SHA1))
253253+ | `DHE_RSA_WITH_AES_256_CBC_SHA -> (`RSA, `FFDHE, `Block (AES_256_CBC, `SHA1))
254254+ | `RSA_WITH_AES_128_CBC_SHA256 -> (`RSA, `RSA, `Block (AES_128_CBC, `SHA256))
255255+ | `RSA_WITH_AES_256_CBC_SHA256 -> (`RSA, `RSA, `Block (AES_256_CBC, `SHA256))
256256+ | `DHE_RSA_WITH_AES_128_CBC_SHA256 -> (`RSA, `FFDHE, `Block (AES_128_CBC, `SHA256))
257257+ | `DHE_RSA_WITH_AES_256_CBC_SHA256 -> (`RSA, `FFDHE, `Block (AES_256_CBC, `SHA256))
258258+ | `RSA_WITH_AES_128_CCM -> (`RSA, `RSA, `AEAD AES_128_CCM)
259259+ | `RSA_WITH_AES_256_CCM -> (`RSA, `RSA, `AEAD AES_256_CCM)
260260+ | `DHE_RSA_WITH_AES_128_CCM -> (`RSA, `FFDHE, `AEAD AES_128_CCM)
261261+ | `DHE_RSA_WITH_AES_256_CCM -> (`RSA, `FFDHE, `AEAD AES_256_CCM)
262262+ | `RSA_WITH_AES_128_GCM_SHA256 -> (`RSA, `RSA, `AEAD AES_128_GCM)
263263+ | `RSA_WITH_AES_256_GCM_SHA384 -> (`RSA, `RSA, `AEAD AES_256_GCM)
264264+ | `DHE_RSA_WITH_AES_128_GCM_SHA256 -> (`RSA, `FFDHE, `AEAD AES_128_GCM)
265265+ | `DHE_RSA_WITH_AES_256_GCM_SHA384 -> (`RSA, `FFDHE, `AEAD AES_256_GCM)
266266+ | `ECDHE_RSA_WITH_AES_128_GCM_SHA256 -> (`RSA, `ECDHE, `AEAD AES_128_GCM)
267267+ | `ECDHE_RSA_WITH_AES_256_GCM_SHA384 -> (`RSA, `ECDHE, `AEAD AES_256_GCM)
268268+ | `ECDHE_RSA_WITH_AES_256_CBC_SHA384 -> (`RSA, `ECDHE, `Block (AES_256_CBC, `SHA384))
269269+ | `ECDHE_RSA_WITH_AES_128_CBC_SHA256 -> (`RSA, `ECDHE, `Block (AES_128_CBC, `SHA256))
270270+ | `ECDHE_RSA_WITH_AES_256_CBC_SHA -> (`RSA, `ECDHE, `Block (AES_256_CBC, `SHA1))
271271+ | `ECDHE_RSA_WITH_AES_128_CBC_SHA -> (`RSA, `ECDHE, `Block (AES_128_CBC, `SHA1))
272272+ | `ECDHE_RSA_WITH_3DES_EDE_CBC_SHA -> (`RSA, `ECDHE, `Block (TRIPLE_DES_EDE_CBC, `SHA1))
273273+ | `DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 -> (`RSA, `FFDHE, `AEAD CHACHA20_POLY1305)
274274+ | `ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 -> (`RSA, `ECDHE, `AEAD CHACHA20_POLY1305)
275275+ | `ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA -> (`EC, `ECDHE, `Block (TRIPLE_DES_EDE_CBC, `SHA1))
276276+ | `ECDHE_ECDSA_WITH_AES_128_CBC_SHA -> (`EC, `ECDHE, `Block (AES_128_CBC, `SHA1))
277277+ | `ECDHE_ECDSA_WITH_AES_256_CBC_SHA -> (`EC, `ECDHE, `Block (AES_256_CBC, `SHA1))
278278+ | `ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 -> (`EC, `ECDHE, `Block (AES_128_CBC, `SHA256))
279279+ | `ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 -> (`EC, `ECDHE, `Block (AES_256_CBC, `SHA384))
280280+ | `ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 -> (`EC, `ECDHE, `AEAD AES_128_GCM)
281281+ | `ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 -> (`EC, `ECDHE, `AEAD AES_256_GCM)
282282+ | `ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 -> (`EC, `ECDHE, `AEAD CHACHA20_POLY1305)
283283+ | #ciphersuite13 as cs13 -> (`RSA, `FFDHE, `AEAD (privprot13 cs13)) (* this is mostly wrong *)
284284+285285+(** [ciphersuite_kex ciphersuite] is [kex], first projection of [get_kex_privprot] *)
286286+let ciphersuite_kex c =
287287+ let _keytype, kex, _pp = get_keytype_kex_privprot c in
288288+ kex
289289+290290+(** [ciphersuite_privprot ciphersuite] is [privprot], second projection of [get_kex_privprot] *)
291291+let ciphersuite_privprot c =
292292+ let _keytype, _kex, pp = get_keytype_kex_privprot c in
293293+ pp
294294+295295+let ciphersuite_keytype c =
296296+ let keytype, _kex, _pp = get_keytype_kex_privprot c in
297297+ keytype
298298+299299+let pp_ciphersuite ppf cs =
300300+ let keytype, kex, pp = get_keytype_kex_privprot cs in
301301+ let pp_keytype ppf = function
302302+ | `EC -> Fmt.string ppf "ECDSA"
303303+ | `RSA -> Fmt.string ppf "RSA"
304304+ in
305305+ match cs with
306306+ | #ciphersuite13 -> Fmt.pf ppf "%a" pp_payload_protection pp
307307+ | _ -> Fmt.pf ppf "%a %a %a" pp_key_exchange_algorithm kex pp_keytype keytype
308308+ pp_payload_protection pp
309309+310310+let pp_any_ciphersuite ppf cs =
311311+ match any_ciphersuite_to_ciphersuite cs with
312312+ | Some cs -> pp_ciphersuite ppf cs
313313+ | None -> Fmt.pf ppf "ciphersuite %04X" (Packet.any_ciphersuite_to_int cs)
314314+315315+let ciphersuite_fs cs =
316316+ match ciphersuite_kex cs with
317317+ | #key_exchange_algorithm_dhe -> true
318318+ | `RSA -> false
319319+320320+let ecdhe_only = function
321321+ | #ciphersuite13 -> false
322322+ | cs -> match get_keytype_kex_privprot cs with
323323+ | (_, `ECDHE, _) -> true
324324+ | _ -> false
325325+326326+let dhe_only = function
327327+ | #ciphersuite13 -> false
328328+ | cs -> match get_keytype_kex_privprot cs with
329329+ | (_, `FFDHE, _) -> true
330330+ | _ -> false
331331+332332+let ecdhe = function
333333+ | #ciphersuite13 -> true
334334+ | cs -> match get_keytype_kex_privprot cs with
335335+ | (_, `ECDHE, _) -> true
336336+ | _ -> false
337337+338338+let ciphersuite_tls12_only = function
339339+ | `DHE_RSA_WITH_AES_256_CBC_SHA256
340340+ | `DHE_RSA_WITH_AES_128_CBC_SHA256
341341+ | `RSA_WITH_AES_256_CBC_SHA256
342342+ | `RSA_WITH_AES_128_CBC_SHA256
343343+ | `RSA_WITH_AES_128_CCM
344344+ | `RSA_WITH_AES_256_CCM
345345+ | `DHE_RSA_WITH_AES_128_CCM
346346+ | `DHE_RSA_WITH_AES_256_CCM
347347+ | `RSA_WITH_AES_128_GCM_SHA256
348348+ | `RSA_WITH_AES_256_GCM_SHA384
349349+ | `DHE_RSA_WITH_AES_128_GCM_SHA256
350350+ | `DHE_RSA_WITH_AES_256_GCM_SHA384
351351+ | `ECDHE_RSA_WITH_AES_128_GCM_SHA256
352352+ | `ECDHE_RSA_WITH_AES_256_GCM_SHA384
353353+ | `ECDHE_RSA_WITH_AES_256_CBC_SHA384
354354+ | `ECDHE_RSA_WITH_AES_128_CBC_SHA256
355355+ | `DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
356356+ | `ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
357357+ | `ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
358358+ | `ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
359359+ | `ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
360360+ | `ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
361361+ | `ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 -> true
362362+ | _ -> false
363363+364364+let ciphersuite_tls13 = function
365365+ | #ciphersuite13 -> true
366366+ | _ -> false
+643
vendor/opam/tls/lib/config.ml
···11+open Core
22+33+let src = Logs.Src.create "tls.config" ~doc:"TLS config"
44+module Log = (val Logs.src_log src : Logs.LOG)
55+66+type certchain = X509.Certificate.t list * X509.Private_key.t
77+88+type own_cert = [
99+ | `None
1010+ | `Single of certchain
1111+ | `Multiple of certchain list
1212+ | `Multiple_default of certchain * certchain list
1313+]
1414+1515+let pp_cert ppf cs =
1616+ let from, until = X509.Certificate.validity cs in
1717+ Fmt.pf ppf "subject %a@ issuer %a@ valid from %a until %a"
1818+ X509.Distinguished_name.pp (X509.Certificate.subject cs)
1919+ X509.Distinguished_name.pp (X509.Certificate.issuer cs)
2020+ (Ptime.pp_human ~tz_offset_s:0 ()) from
2121+ (Ptime.pp_human ~tz_offset_s:0 ()) until
2222+2323+let pp_certchain ppf (chain, _) =
2424+ Fmt.(list ~sep:(any "@.") pp_cert) ppf chain
2525+2626+let pp_own_cert ppf = function
2727+ | `None -> Fmt.string ppf "NONE"
2828+ | `Single chain -> pp_certchain ppf chain
2929+ | `Multiple cs ->
3030+ Fmt.pf ppf "multiple: %a" Fmt.(list ~sep:(any "@.@.") pp_certchain) cs
3131+ | `Multiple_default (c, cs) ->
3232+ Fmt.pf ppf "multiple default:@.%a@.others:@.%a"
3333+ pp_certchain c
3434+ Fmt.(list ~sep:(any "@.@.") pp_certchain) cs
3535+3636+type session_cache = SessionID.t -> epoch_data option
3737+3838+type ticket_cache = {
3939+ lookup : string -> (psk13 * epoch_data) option ;
4040+ ticket_granted : psk13 -> epoch_data -> unit ;
4141+ lifetime : int32 ;
4242+ timestamp : unit -> Ptime.t
4343+}
4444+4545+(* TODO: min_rsa, min_dh *)
4646+type config = {
4747+ ciphers : Ciphersuite.ciphersuite list ;
4848+ protocol_versions : tls_version * tls_version ;
4949+ signature_algorithms : signature_algorithm list ;
5050+ use_reneg : bool ;
5151+ authenticator : X509.Authenticator.t option ;
5252+ peer_name : [`host] Domain_name.t option ;
5353+ own_certificates : own_cert ;
5454+ acceptable_cas : X509.Distinguished_name.t list ;
5555+ session_cache : session_cache ;
5656+ ticket_cache : ticket_cache option ;
5757+ cached_session : epoch_data option ;
5858+ cached_ticket : (psk13 * epoch_data) option ;
5959+ alpn_protocols : string list ;
6060+ groups : group list ;
6161+ zero_rtt : int32 ;
6262+ ip : Ipaddr.t option ;
6363+}
6464+6565+let pp_config ppf cfg =
6666+ Fmt.pf ppf
6767+ "ciphers: %a@. \
6868+ minimal protocol version: %a@. \
6969+ maximum protocol version: %a@. \
7070+ signature algorithms: %a@. \
7171+ renegotiation enabled %B@. \
7272+ peer name: %a@. \
7373+ own certificate: %a@. \
7474+ acceptable CAs: %a@. \
7575+ alpn protocols: %a@. \
7676+ groups: %a@. \
7777+ IP: %a@."
7878+ Fmt.(list ~sep:(any ", ") Ciphersuite.pp_ciphersuite) cfg.ciphers
7979+ pp_tls_version (fst cfg.protocol_versions)
8080+ pp_tls_version (snd cfg.protocol_versions)
8181+ Fmt.(list ~sep:(any ", ") pp_signature_algorithm) cfg.signature_algorithms
8282+ cfg.use_reneg
8383+ Fmt.(option ~none:(any "none provided") Domain_name.pp) cfg.peer_name
8484+ pp_own_cert cfg.own_certificates
8585+ Fmt.(list ~sep:(any ", ") X509.Distinguished_name.pp) cfg.acceptable_cas
8686+ Fmt.(list ~sep:(any ", ") string) cfg.alpn_protocols
8787+ Fmt.(list ~sep:(any ", ") pp_group) cfg.groups
8888+ Fmt.(option ~none:(any "none provided") Ipaddr.pp) cfg.ip
8989+9090+let ciphers13 cfg =
9191+ List.rev
9292+ (List.fold_left (fun acc cs ->
9393+ match Ciphersuite.ciphersuite_to_ciphersuite13 cs with
9494+ | None -> acc
9595+ | Some c -> c :: acc)
9696+ [] cfg.ciphers)
9797+9898+module Ciphers = struct
9999+100100+ (* A good place for various pre-baked cipher lists and helper functions to
101101+ * slice and groom those lists. *)
102102+103103+ let default13 = [
104104+ `AES_128_GCM_SHA256 ;
105105+ `AES_256_GCM_SHA384 ;
106106+ `CHACHA20_POLY1305_SHA256 ;
107107+ `AES_128_CCM_SHA256 ;
108108+ ]
109109+110110+ let default = default13 @ [
111111+ `DHE_RSA_WITH_AES_256_GCM_SHA384 ;
112112+ `DHE_RSA_WITH_AES_128_GCM_SHA256 ;
113113+ `DHE_RSA_WITH_AES_256_CCM ;
114114+ `DHE_RSA_WITH_AES_128_CCM ;
115115+ `DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 ;
116116+ `ECDHE_RSA_WITH_AES_128_GCM_SHA256 ;
117117+ `ECDHE_RSA_WITH_AES_256_GCM_SHA384 ;
118118+ `ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 ;
119119+ `ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ;
120120+ `ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ;
121121+ `ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 ;
122122+ ]
123123+124124+ let supported = default @ [
125125+ `DHE_RSA_WITH_AES_256_CBC_SHA256 ;
126126+ `DHE_RSA_WITH_AES_128_CBC_SHA256 ;
127127+ `DHE_RSA_WITH_AES_256_CBC_SHA ;
128128+ `DHE_RSA_WITH_AES_128_CBC_SHA ;
129129+ `ECDHE_RSA_WITH_AES_256_CBC_SHA384 ;
130130+ `ECDHE_RSA_WITH_AES_128_CBC_SHA256 ;
131131+ `ECDHE_RSA_WITH_AES_256_CBC_SHA ;
132132+ `ECDHE_RSA_WITH_AES_128_CBC_SHA ;
133133+ `ECDHE_ECDSA_WITH_AES_128_CBC_SHA ;
134134+ `ECDHE_ECDSA_WITH_AES_256_CBC_SHA ;
135135+ `ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 ;
136136+ `ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 ;
137137+ `RSA_WITH_AES_256_CBC_SHA256 ;
138138+ `RSA_WITH_AES_128_CBC_SHA256 ;
139139+ `RSA_WITH_AES_256_CBC_SHA ;
140140+ `RSA_WITH_AES_128_CBC_SHA ;
141141+ `RSA_WITH_AES_256_GCM_SHA384 ;
142142+ `RSA_WITH_AES_128_GCM_SHA256 ;
143143+ `RSA_WITH_AES_256_CCM ;
144144+ `RSA_WITH_AES_128_CCM ;
145145+ `DHE_RSA_WITH_3DES_EDE_CBC_SHA ;
146146+ `RSA_WITH_3DES_EDE_CBC_SHA ;
147147+ `ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA ;
148148+ ]
149149+150150+ (* as defined in https://httpwg.org/specs/rfc7540.html#BadCipherSuites *)
151151+ let http2 = default13 @ [
152152+ `DHE_RSA_WITH_AES_256_GCM_SHA384 ;
153153+ `DHE_RSA_WITH_AES_128_GCM_SHA256 ;
154154+ `DHE_RSA_WITH_AES_256_CCM ;
155155+ `DHE_RSA_WITH_AES_128_CCM ;
156156+ `DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 ;
157157+ `ECDHE_RSA_WITH_AES_128_GCM_SHA256 ;
158158+ `ECDHE_RSA_WITH_AES_256_GCM_SHA384 ;
159159+ `ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 ;
160160+ `ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ;
161161+ `ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ;
162162+ `ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 ;
163163+ ]
164164+165165+ let fs_of = List.filter Ciphersuite.ciphersuite_fs
166166+167167+ let fs = fs_of default
168168+end
169169+170170+let default_signature_algorithms =
171171+ [ `ECDSA_SECP256R1_SHA256 ;
172172+ `ECDSA_SECP384R1_SHA384 ;
173173+ `ECDSA_SECP521R1_SHA512 ;
174174+ `ED25519 ;
175175+ `RSA_PSS_RSAENC_SHA256 ;
176176+ `RSA_PSS_RSAENC_SHA384 ;
177177+ `RSA_PSS_RSAENC_SHA512 ;
178178+ `RSA_PKCS1_SHA256 ;
179179+ `RSA_PKCS1_SHA384 ;
180180+ `RSA_PKCS1_SHA512 ;
181181+ ]
182182+183183+let supported_signature_algorithms =
184184+ default_signature_algorithms @ [
185185+ `RSA_PKCS1_SHA224 ;
186186+ `ECDSA_SECP256R1_SHA1 ;
187187+ `RSA_PKCS1_SHA1 ;
188188+ `RSA_PKCS1_MD5
189189+ ]
190190+191191+let min_dh_size = 1024
192192+193193+let min_rsa_key_size = 1024
194194+195195+let supported_groups =
196196+ [ `X25519 ; `P384 ; `P256 ; `P521 ;
197197+ `FFDHE2048 ; `FFDHE3072 ; `FFDHE4096 ; `FFDHE6144 ; `FFDHE8192 ]
198198+199199+let elliptic_curve = function
200200+ | `X25519 | `P256 | `P384 | `P521 -> true
201201+ | `FFDHE2048 | `FFDHE3072 | `FFDHE4096 | `FFDHE6144 | `FFDHE8192 -> false
202202+203203+let default_config = {
204204+ ciphers = Ciphers.default ;
205205+ protocol_versions = (`TLS_1_2, `TLS_1_3) ;
206206+ signature_algorithms = default_signature_algorithms ;
207207+ use_reneg = false ;
208208+ authenticator = None ;
209209+ peer_name = None ;
210210+ own_certificates = `None ;
211211+ acceptable_cas = [] ;
212212+ session_cache = (fun _ -> None) ;
213213+ cached_session = None ;
214214+ cached_ticket = None ;
215215+ alpn_protocols = [] ;
216216+ groups = supported_groups ;
217217+ ticket_cache = None ;
218218+ zero_rtt = 0l ;
219219+ ip = None ;
220220+}
221221+222222+(* There are inter-configuration option constraints that are checked and
223223+ adjusted here. The overall approach is if the client explicitly provided
224224+ values, these are taken as granted (a conflict will result in an error). If
225225+ the defaults are used, they are adjusted depending on the others.
226226+227227+ The options in question are:
228228+ - ciphers, which before 1.3 include the key exchange (FFDHE, ECDHE, RSA)
229229+ - groups, which name the FFDHE and ECDHE groups used for DH
230230+ - signature_algorithms, which (since 1.2) specify the key type and algorithm
231231+ used for signatures (RSA-PKCS, RSA-PSS, ECDSA/EdDSA)
232232+ - certificate chains, which influence ciphers (before 1.3) and
233233+ signature_algorithms
234234+235235+ Using everywhere the default (but a custom certificate / or multiple) result
236236+ in a working configuration (where, depending on the certificate key type,
237237+ some signature_algorithms and ciphersuites are removed). The provided server
238238+ certificate may remove ciphers & signature_algorithms, but will only result
239239+ in failure if these will then be empty.
240240+241241+ An invalid configuration is for example: only FFDHE ciphersuites, but no
242242+ FFDHE groups. Or only EC signature algorithms, but only ciphers where the
243243+ key type is RSA.
244244+245245+ At session initiation time, the server implementation selects cipher,
246246+ certificate, signature_algorithm, and group depending on its configuration
247247+ and client request.
248248+*)
249249+250250+let ciphers_and_groups ?ciphers ?groups default_ciphers =
251251+ let tls13 = function #Ciphersuite.ciphersuite13 -> true | _ -> false in
252252+ match ciphers, groups with
253253+ | None, None -> Ok (default_ciphers, supported_groups)
254254+ | Some cs, None ->
255255+ Ok (cs,
256256+ let has_kex x = function
257257+ | #Ciphersuite.ciphersuite13 -> true
258258+ | c -> x = Ciphersuite.ciphersuite_kex c
259259+ in
260260+ begin
261261+ match List.exists (has_kex `ECDHE) cs, List.exists (has_kex `FFDHE) cs with
262262+ | true, true -> supported_groups
263263+ | true, false ->
264264+ Log.warn (fun m -> m "removed FFDHE groups (no FFDHE ciphersuite) from configuation");
265265+ List.filter elliptic_curve supported_groups
266266+ | false, true ->
267267+ Log.warn (fun m -> m "removed ECDHE groups (no ECDHE ciphersuite) from configuration");
268268+ List.filter (fun g -> not (elliptic_curve g)) supported_groups
269269+ | false, false -> []
270270+ end)
271271+ | None, Some g ->
272272+ Ok (begin match List.partition elliptic_curve g with
273273+ | [], [] ->
274274+ Log.warn (fun m -> m "removed DHE and ECDHE ciphersuites (empty groups provided) from configuration");
275275+ List.filter (fun c -> not (Ciphersuite.ciphersuite_fs c)) default_ciphers
276276+ | _::_, [] ->
277277+ Log.warn (fun m -> m "removed DHE ciphersuites (no FFDHE groups provided) from configuration");
278278+ List.filter (fun c -> not (Ciphersuite.dhe_only c)) default_ciphers
279279+ | [], _ :: _ ->
280280+ Log.warn (fun m -> m "removed ECDHE ciphersuites (no EC groups provided) from configuration");
281281+ List.filter (fun c -> not (Ciphersuite.ecdhe_only c)) default_ciphers
282282+ | _ -> default_ciphers
283283+ end, g)
284284+ | Some cs, Some g ->
285285+ if List.exists Ciphersuite.ecdhe_only cs && not (List.exists elliptic_curve g) then
286286+ Error (`Msg "ciphersuite with ECDHE provided, but no EC group")
287287+ else if List.exists Ciphersuite.dhe_only cs && not (List.exists (fun g -> not (elliptic_curve g)) g) then
288288+ Error (`Msg "ciphersuite with FFDHE provided, but no FF group")
289289+ else if List.exists Ciphersuite.ciphersuite_fs cs && g = [] then
290290+ Error (`Msg "ciphersuite with forward security provided, but no group")
291291+ else if List.exists elliptic_curve g && not (List.exists Ciphersuite.ecdhe cs) then
292292+ Error (`Msg "EC group provided, but no ciphersuite with ECDHE")
293293+ else if List.exists (fun g -> not (elliptic_curve g)) g &&
294294+ not (List.exists (fun c -> Ciphersuite.dhe_only c || tls13 c) cs)
295295+ then
296296+ Error (`Msg "FF group provided, but no ciphersuite with DHE")
297297+ else
298298+ Ok (cs, g)
299299+300300+let ciphers_and_sig_alg ?ciphers ?signature_algorithms default_ciphers =
301301+ let tls13 = function #Ciphersuite.ciphersuite13 -> true | _ -> false in
302302+ let default_sa_from_ciphers c =
303303+ let has_key k c = tls13 c || k = Ciphersuite.ciphersuite_keytype c in
304304+ match List.exists (has_key `RSA) c, List.exists (has_key `EC) c with
305305+ | true, true -> Ok supported_signature_algorithms
306306+ | true, false ->
307307+ Log.warn (fun m -> m "removed EC signature algorithms (no EC ciphersuite present)");
308308+ Ok (List.filter rsa_sigalg supported_signature_algorithms)
309309+ | false, true ->
310310+ Log.warn (fun m -> m "removed RSA signature algorithms (no RSA ciphersuite present)");
311311+ Ok (List.filter (fun sa -> not (rsa_sigalg sa)) supported_signature_algorithms)
312312+ | false, false ->
313313+ Error (`Msg "ciphersuite list without RSA and EC keys")
314314+ in
315315+ let ( let* ) = Result.bind in
316316+ match ciphers, signature_algorithms with
317317+ | None, None ->
318318+ let* sig_algs = default_sa_from_ciphers default_ciphers in
319319+ Ok (default_ciphers, sig_algs)
320320+ | Some c, None ->
321321+ let* sig_algs = default_sa_from_ciphers c in
322322+ Ok (c, sig_algs)
323323+ | None, Some sa ->
324324+ begin match List.partition rsa_sigalg sa with
325325+ | [], [] -> Error (`Msg "empty signature algorithms provided")
326326+ | _::_, [] ->
327327+ Log.warn (fun m -> m "removing EC ciphers (no EC signature algorithm provided)");
328328+ Ok (List.filter
329329+ (fun c -> tls13 c || not (Ciphersuite.ciphersuite_keytype c = `EC))
330330+ default_ciphers,
331331+ sa)
332332+ | [], _::_ ->
333333+ Log.warn (fun m -> m "removing RSA ciphers (no RSA signature algorithm provided)");
334334+ Ok (List.filter
335335+ (fun c -> tls13 c || not (Ciphersuite.ciphersuite_keytype c = `RSA))
336336+ default_ciphers,
337337+ sa)
338338+ | _::_, _::_ -> Ok (default_ciphers, sa)
339339+ end
340340+ | Some c, Some sa ->
341341+ if List.exists rsa_sigalg sa && not (List.exists (fun c -> Ciphersuite.ciphersuite_keytype c = `RSA) c) then
342342+ Error (`Msg "RSA signature algorithm, but no ciphersuites with RSA keys")
343343+ else if List.exists (fun s -> not (rsa_sigalg s)) sa && not (List.exists (fun c -> Ciphersuite.ciphersuite_keytype c = `EC) c) then
344344+ Error (`Msg "EC signature algorithm, but no ciphersuites with EC keys")
345345+ else if List.exists (fun c -> Ciphersuite.ciphersuite_keytype c = `RSA) c && not (List.exists rsa_sigalg sa) then
346346+ Error (`Msg "RSA ciphersuite, but no RSA signature algorithm")
347347+ else if List.exists (fun c -> Ciphersuite.ciphersuite_keytype c = `EC) c && not (List.exists (fun s -> not (rsa_sigalg s)) sa) then
348348+ Error (`Msg "EC ciphersuite, but no EC signature algorithm")
349349+ else
350350+ Ok (c, sa)
351351+352352+let validate_common config =
353353+ let ( let* ) = Result.bind in
354354+ let (v_min, v_max) = config.protocol_versions in
355355+ if v_max < v_min then
356356+ Error (`Msg "bad version range")
357357+ else
358358+ let* ciphers, signature_algorithms =
359359+ match v_min, v_max with
360360+ | _, `TLS_1_1 | _, `TLS_1_0 ->
361361+ Log.warn (fun m -> m "TLS 1.0 or TLS 1.1 as maximum version configured, removing 1.2 and 1.3 ciphersuites");
362362+ Ok (List.filter (fun c ->
363363+ not (Ciphersuite.ciphersuite_tls12_only c || Ciphersuite.ciphersuite_tls13 c))
364364+ config.ciphers,
365365+ [])
366366+ | _, `TLS_1_2 ->
367367+ if config.signature_algorithms = [] then
368368+ Error (`Msg "TLS 1.2 configured but no signature algorithms provided")
369369+ else begin
370370+ Log.warn (fun m -> m "TLS 1.2 as maximum version configured, removing 1.3 cipher suites");
371371+ Ok (List.filter
372372+ (fun c -> not (Ciphersuite.ciphersuite_tls13 c)) config.ciphers,
373373+ config.signature_algorithms)
374374+ end
375375+ | `TLS_1_3, `TLS_1_3 ->
376376+ let sa = List.filter tls13_sigalg config.signature_algorithms in
377377+ if sa = [] then
378378+ Error (`Msg "TLS 1.3 configured but no 1.3 signature algorithms provided")
379379+ else begin
380380+ Log.warn (fun m -> m "only TLS 1.3 configured, removing pre-1.3 cipher suites and signature algorithms");
381381+ Ok (List.filter Ciphersuite.ciphersuite_tls13 config.ciphers, sa)
382382+ end
383383+ | _ -> Ok (config.ciphers, config.signature_algorithms)
384384+ in
385385+ if not (Utils.List_set.is_proper_set ciphers) then
386386+ Error (`Msg "set of ciphers is not a proper set")
387387+ else if List.length ciphers = 0 then
388388+ Error (`Msg "set of ciphers is empty")
389389+ else if not (Utils.List_set.is_proper_set config.groups) then
390390+ Error (`Msg "set of groups is not a proper set")
391391+ else if not (Utils.List_set.is_proper_set signature_algorithms) then
392392+ Error (`Msg "set of signature algorithms is not a proper set")
393393+ else if List.exists (fun proto -> let len = String.length proto in len = 0 || len > 255) config.alpn_protocols then
394394+ Error (`Msg "invalid alpn protocol")
395395+ else if List.length config.alpn_protocols > 0xffff then
396396+ Error (`Msg "alpn protocols list too large")
397397+ else
398398+ Ok { config with ciphers ; signature_algorithms }
399399+400400+let validate_certificate_chain = function
401401+ | (s::chain, priv) ->
402402+ let ( let* ) = Result.bind in
403403+ let pub = X509.Private_key.public priv in
404404+ let* () =
405405+ match pub with
406406+ | `RSA pub when Mirage_crypto_pk.Rsa.pub_bits pub < min_rsa_key_size ->
407407+ Error (`Msg "RSA key too short!")
408408+ | _ -> Ok ()
409409+ in
410410+ let* () =
411411+ let eq_pub a b =
412412+ String.equal (X509.Public_key.fingerprint a) (X509.Public_key.fingerprint b)
413413+ in
414414+ if not (eq_pub pub (X509.Certificate.public_key s)) then
415415+ Error (`Msg "public / private key combination" )
416416+ else
417417+ Ok ()
418418+ in
419419+ ( match Utils.init_and_last chain with
420420+ | Some (ch, trust) ->
421421+ (* TODO: verify that certificates are x509 v3 if TLS_1_2 *)
422422+ ( match X509.Validation.verify_chain_of_trust ~time:(fun () -> None) ~host:None ~anchors:[trust] (s :: ch) with
423423+ | Ok _ -> Ok ()
424424+ | Error x ->
425425+ let s = Fmt.to_to_string X509.Validation.pp_validation_error x in
426426+ Error (`Msg ("certificate chain does not validate: " ^ s)))
427427+ | None -> Ok () )
428428+ | _ -> Error (`Msg "certificate chain")
429429+430430+let validate_client config =
431431+ match config.own_certificates with
432432+ | `None -> Ok ()
433433+ | `Single c -> validate_certificate_chain c
434434+ | _ -> Error (`Msg "multiple client certificates not supported in client config")
435435+436436+let non_overlapping cs =
437437+ let namessets =
438438+ List.filter_map (function
439439+ | (s :: _, _) -> Some s
440440+ | _ -> None)
441441+ cs
442442+ |> List.map X509.Certificate.hostnames
443443+ in
444444+ let rec check = function
445445+ | [] -> ()
446446+ | s::ss ->
447447+ if not (List.for_all (fun ss' ->
448448+ X509.Host.Set.is_empty (X509.Host.Set.inter s ss'))
449449+ ss)
450450+ then
451451+ invalid_arg "overlapping names in certificates"
452452+ else
453453+ check ss
454454+ in
455455+ check namessets
456456+457457+module KU = Set.Make (struct
458458+ type t = X509.Extension.key_usage
459459+ let compare a b = compare a b
460460+ end)
461461+462462+module PK = Map.Make (struct
463463+ type t = [ `RSA | `ED25519 | `P256 | `P384 | `P521 ]
464464+ let compare a b = compare a b
465465+ end)
466466+467467+let validate_server config =
468468+ let ( let* ) = Result.bind in
469469+ let open Ciphersuite in
470470+ let usages =
471471+ List.fold_left
472472+ (fun acc c -> KU.add (required_usage (ciphersuite_kex c)) acc)
473473+ KU.empty config.ciphers
474474+ in
475475+ let* certificate_chains =
476476+ match config.own_certificates with
477477+ | `Single c -> Ok [c]
478478+ | `Multiple cs -> Ok cs
479479+ | `Multiple_default (c, cs) -> Ok (c :: cs)
480480+ | `None -> Error (`Msg "no server certificate provided")
481481+ in
482482+ let* server_certs =
483483+ List.fold_left (fun acc cc ->
484484+ let* acc = acc in
485485+ match cc with
486486+ | (s::_,_) -> Ok (s :: acc)
487487+ | _ -> Error (`Msg "empty certificate chain"))
488488+ (Ok []) certificate_chains
489489+ in
490490+ let* () =
491491+ if not
492492+ (KU.for_all (fun u ->
493493+ List.exists (supports_key_usage ~not_present:true u) server_certs)
494494+ usages)
495495+ then
496496+ Error (`Msg "certificate usage does not match")
497497+ else
498498+ Ok ()
499499+ in
500500+ let* () =
501501+ List.fold_left (fun acc cc ->
502502+ let* () = acc in
503503+ validate_certificate_chain cc)
504504+ (Ok ()) certificate_chains
505505+ in
506506+ let rsa_cert, ec_cert =
507507+ let is_ec_cert c = match X509.Certificate.public_key c with
508508+ | `ED25519 _ | `P256 _ | `P384 _ | `P521 _ -> true
509509+ | _ -> false
510510+ and is_rsa_cert c = match X509.Certificate.public_key c with
511511+ | `RSA _ -> true | _ -> false
512512+ in
513513+ List.exists is_rsa_cert server_certs,
514514+ List.exists is_ec_cert server_certs
515515+ in
516516+ let ciphers =
517517+ List.filter
518518+ (function
519519+ | #Ciphersuite.ciphersuite13 -> true
520520+ | c ->
521521+ let keytype = ciphersuite_keytype c in
522522+ (rsa_cert && keytype = `RSA) || (ec_cert && keytype = `EC))
523523+ config.ciphers
524524+ in
525525+ ( match config.own_certificates with
526526+ | `Multiple cs
527527+ | `Multiple_default (_, cs) ->
528528+ let add k v acc = match PK.find_opt k acc with
529529+ | None -> PK.add k [v] acc
530530+ | Some r -> PK.add k (v :: r) acc
531531+ in
532532+ let pk =
533533+ List.fold_left (fun acc cs ->
534534+ match snd cs with
535535+ | `RSA _ -> add `RSA cs acc
536536+ | `ED25519 _ -> add `ED25519 cs acc
537537+ | `P256 _ -> add `P256 cs acc
538538+ | `P384 _ -> add `P384 cs acc
539539+ | `P521 _ -> add `P521 cs acc)
540540+ PK.empty cs
541541+ in
542542+ PK.iter (fun _ chains -> non_overlapping chains) pk
543543+ | _ -> () );
544544+ Ok { config with ciphers }
545545+546546+let validate_keys_sig_algs config =
547547+ let ( let* ) = Result.bind in
548548+ let _, v_max = config.protocol_versions in
549549+ if v_max = `TLS_1_2 || v_max = `TLS_1_3 then
550550+ let* certificate_chains =
551551+ match config.own_certificates with
552552+ | `Single c -> Ok [c]
553553+ | `Multiple cs -> Ok cs
554554+ | `Multiple_default (c, cs) -> Ok (c :: cs)
555555+ | `None -> Error (`Msg "no server certificate provided")
556556+ in
557557+ let* server_keys =
558558+ List.fold_left (fun acc cc ->
559559+ let* acc = acc in
560560+ match cc with
561561+ | (s::_,_) -> Ok (X509.Certificate.public_key s :: acc)
562562+ | _ -> Error (`Msg "empty certificate chain"))
563563+ (Ok []) certificate_chains
564564+ in
565565+ if not
566566+ (List.for_all (fun cert ->
567567+ List.exists (pk_matches_sa cert) config.signature_algorithms)
568568+ server_keys)
569569+ then
570570+ Error (`Msg "certificate provided which does not allow any signature algorithm")
571571+ else
572572+ Ok ()
573573+ else
574574+ Ok ()
575575+576576+type client = config
577577+type server = config
578578+579579+let of_server conf = conf
580580+and of_client conf = conf
581581+582582+let peer conf name = { conf with peer_name = Some name }
583583+584584+let with_authenticator conf auth = { conf with authenticator = Some auth }
585585+586586+let with_own_certificates conf own_certificates = { conf with own_certificates }
587587+588588+let with_acceptable_cas conf acceptable_cas = { conf with acceptable_cas }
589589+590590+let (<?>) ma b = match ma with None -> b | Some a -> a
591591+592592+let client
593593+ ~authenticator ?peer_name ?ciphers ?version ?signature_algorithms ?reneg ?certificates ?cached_session ?cached_ticket ?ticket_cache ?alpn_protocols ?groups ?ip () =
594594+ let ( let* ) = Result.bind in
595595+ let* ciphers', groups = ciphers_and_groups ?ciphers ?groups default_config.ciphers in
596596+ let* ciphers, signature_algorithms = ciphers_and_sig_alg ?ciphers ?signature_algorithms ciphers' in
597597+ let config =
598598+ { default_config with
599599+ authenticator = Some authenticator ;
600600+ ciphers ;
601601+ protocol_versions = version <?> default_config.protocol_versions ;
602602+ signature_algorithms ;
603603+ use_reneg = reneg <?> default_config.use_reneg ;
604604+ own_certificates = certificates <?> default_config.own_certificates ;
605605+ peer_name = peer_name ;
606606+ cached_session = cached_session ;
607607+ alpn_protocols = alpn_protocols <?> default_config.alpn_protocols ;
608608+ ticket_cache = ticket_cache ;
609609+ cached_ticket = cached_ticket ;
610610+ groups ;
611611+ ip ;
612612+ } in
613613+ let* config = validate_common config in
614614+ let* () = validate_client config in
615615+ Log.debug (fun m -> m "client with %a" pp_config config);
616616+ Ok config
617617+618618+let server
619619+ ?ciphers ?version ?signature_algorithms ?reneg ?certificates ?acceptable_cas ?authenticator ?session_cache ?ticket_cache ?alpn_protocols ?groups ?zero_rtt ?ip () =
620620+ let ( let* ) = Result.bind in
621621+ let* ciphers', groups = ciphers_and_groups ?ciphers ?groups default_config.ciphers in
622622+ let* ciphers, signature_algorithms = ciphers_and_sig_alg ?ciphers ?signature_algorithms ciphers' in
623623+ let config =
624624+ { default_config with
625625+ ciphers ;
626626+ protocol_versions = version <?> default_config.protocol_versions ;
627627+ signature_algorithms ;
628628+ use_reneg = reneg <?> default_config.use_reneg ;
629629+ own_certificates = certificates <?> default_config.own_certificates ;
630630+ acceptable_cas = acceptable_cas <?> default_config.acceptable_cas ;
631631+ authenticator = authenticator ;
632632+ session_cache = session_cache <?> default_config.session_cache ;
633633+ alpn_protocols = alpn_protocols <?> default_config.alpn_protocols ;
634634+ ticket_cache = ticket_cache ;
635635+ groups ;
636636+ zero_rtt = zero_rtt <?> default_config.zero_rtt ;
637637+ ip ;
638638+ } in
639639+ let* config = validate_server config in
640640+ let* config = validate_common config in
641641+ let* () = validate_keys_sig_algs config in
642642+ Log.debug (fun m -> m "server with %a" pp_config config);
643643+ Ok config
+174
vendor/opam/tls/lib/config.mli
···11+open Core
22+33+(** Configuration of the TLS stack *)
44+55+(** {1 Config type} *)
66+77+(** certificate chain and private key of the first certificate *)
88+type certchain = X509.Certificate.t list * X509.Private_key.t
99+1010+(** polymorphic variant of own certificates *)
1111+type own_cert = [
1212+ | `None
1313+ | `Single of certchain
1414+ | `Multiple of certchain list
1515+ | `Multiple_default of certchain * certchain list
1616+]
1717+1818+type session_cache = SessionID.t -> epoch_data option
1919+2020+type ticket_cache = {
2121+ lookup : string -> (psk13 * epoch_data) option ;
2222+ ticket_granted : psk13 -> epoch_data -> unit ;
2323+ lifetime : int32 ;
2424+ timestamp : unit -> Ptime.t
2525+}
2626+2727+(** configuration parameters *)
2828+type config = private {
2929+ ciphers : Ciphersuite.ciphersuite list ; (** ordered list (regarding preference) of supported cipher suites *)
3030+ protocol_versions : tls_version * tls_version ; (** supported protocol versions (min, max) *)
3131+ signature_algorithms : signature_algorithm list ; (** ordered list of supported signature algorithms (regarding preference) *)
3232+ use_reneg : bool ; (** endpoint should accept renegotiation requests *)
3333+ authenticator : X509.Authenticator.t option ; (** optional X509 authenticator *)
3434+ peer_name : [ `host ] Domain_name.t option ; (** optional name of other endpoint (used for SNI RFC4366) *)
3535+ own_certificates : own_cert ; (** optional default certificate chain and other certificate chains *)
3636+ acceptable_cas : X509.Distinguished_name.t list ; (** ordered list of acceptable certificate authorities *)
3737+ session_cache : session_cache ;
3838+ ticket_cache : ticket_cache option ;
3939+ cached_session : epoch_data option ;
4040+ cached_ticket : (psk13 * epoch_data) option ;
4141+ alpn_protocols : string list ; (** optional ordered list of accepted alpn_protocols *)
4242+ groups : group list ; (** the first FFDHE will be used for TLS 1.2 and below if a DHE ciphersuite is used *)
4343+ zero_rtt : int32 ;
4444+ ip : Ipaddr.t option ;
4545+}
4646+4747+(** [ciphers13 config] are the ciphersuites for TLS 1.3 in the configuration. *)
4848+val ciphers13 : config -> Ciphersuite.ciphersuite13 list
4949+5050+(** opaque type of a client configuration *)
5151+type client
5252+5353+(** opaque type of a server configuration *)
5454+type server
5555+5656+(** {1 Constructors} *)
5757+5858+(** [client authenticator ?peer_name ?ciphers ?version ?hashes ?reneg ?certificates ?alpn_protocols] is
5959+ [client] configuration with the given parameters. Returns an error if the configuration is invalid. *)
6060+val client :
6161+ authenticator : X509.Authenticator.t ->
6262+ ?peer_name : [ `host ] Domain_name.t ->
6363+ ?ciphers : Ciphersuite.ciphersuite list ->
6464+ ?version : tls_version * tls_version ->
6565+ ?signature_algorithms : signature_algorithm list ->
6666+ ?reneg : bool ->
6767+ ?certificates : own_cert ->
6868+ ?cached_session : epoch_data ->
6969+ ?cached_ticket : psk13 * epoch_data ->
7070+ ?ticket_cache : ticket_cache ->
7171+ ?alpn_protocols : string list ->
7272+ ?groups : group list ->
7373+ ?ip : Ipaddr.t ->
7474+ unit -> (client, [> `Msg of string ]) result
7575+7676+(** [server ?ciphers ?version ?hashes ?reneg ?certificates ?acceptable_cas ?authenticator ?alpn_protocols]
7777+ is [server] configuration with the given parameters. Returns an error if the configuration is invalid. *)
7878+val server :
7979+ ?ciphers : Ciphersuite.ciphersuite list ->
8080+ ?version : tls_version * tls_version ->
8181+ ?signature_algorithms : signature_algorithm list ->
8282+ ?reneg : bool ->
8383+ ?certificates : own_cert ->
8484+ ?acceptable_cas : X509.Distinguished_name.t list ->
8585+ ?authenticator : X509.Authenticator.t ->
8686+ ?session_cache : session_cache ->
8787+ ?ticket_cache : ticket_cache ->
8888+ ?alpn_protocols : string list ->
8989+ ?groups : group list ->
9090+ ?zero_rtt : int32 ->
9191+ ?ip : Ipaddr.t ->
9292+ unit -> (server, [> `Msg of string ]) result
9393+9494+(** [peer client name] is [client] with [name] as [peer_name] *)
9595+val peer : client -> [ `host ] Domain_name.t -> client
9696+9797+(** {1 Note on ALPN protocol selection}
9898+9999+ Both {!val:client} and {!val:server} constructors accept an [alpn_protocols] list. The list for server
100100+ should be given in a descending order of preference. In the case of protocol selection, the server will
101101+ iterate its list and select the first element that the client's list also advertises.
102102+103103+ For example, if the client advertises [["foo"; "bar"; "baz"]] and the server has [["bar"; "foo"]],
104104+ ["bar"] will be selected as the protocol of the handshake. *)
105105+106106+(** {1 Utility functions} *)
107107+108108+(** [default_signature_algorithms] is a list of signature algorithms used by default *)
109109+val default_signature_algorithms : signature_algorithm list
110110+111111+(** [supported_signature_algorithms] is a list of supported signature algorithms by this library *)
112112+val supported_signature_algorithms : signature_algorithm list
113113+114114+(** [min_dh_size] is minimal diffie hellman group size in bits (currently 1024) *)
115115+val min_dh_size : int
116116+117117+(** [supported_groups] are the Diffie-Hellman groups supported in this
118118+ library. *)
119119+val supported_groups : group list
120120+121121+(** [elliptic_curve group] is [true] if group is an elliptic curve, [false]
122122+ otherwise. *)
123123+val elliptic_curve : group -> bool
124124+125125+(** [min_rsa_key_size] is minimal RSA modulus key size in bits (currently 1024) *)
126126+val min_rsa_key_size : int
127127+128128+(** Cipher selection *)
129129+module Ciphers : sig
130130+131131+ open Ciphersuite
132132+133133+ (** Cipher selection related utilities. *)
134134+135135+ (** {1 Cipher selection} *)
136136+137137+ val default : ciphersuite list
138138+ (** [default] is a list of ciphersuites this library uses by default. *)
139139+140140+ val supported : ciphersuite list
141141+ (** [supported] is a list of ciphersuites this library supports
142142+ (larger than [default]). *)
143143+144144+ val fs : ciphersuite list
145145+ (** [fs] is a list of ciphersuites which provide forward secrecy
146146+ (sublist of [default]). *)
147147+148148+ val http2 : ciphersuite list
149149+ (** [http2] is a list of ciphersuites which are allowed to be used with HTTP2:
150150+ not a member of
151151+ {{:https://httpwg.org/specs/rfc7540.html#BadCipherSuites}bad cipher
152152+ suites}. These are only ephemeral key exchanges with AEAD ciphers. *)
153153+154154+ val fs_of : ciphersuite list -> ciphersuite list
155155+ (** [fs_of ciphers] selects all ciphersuites which provide forward
156156+ secrecy from [ciphers]. *)
157157+end
158158+159159+(** {1 Internal use only} *)
160160+161161+(** [of_client client] is a client configuration for [client] *)
162162+val of_client : client -> config
163163+164164+(** [of_server server] is a server configuration for [server] *)
165165+val of_server : server -> config
166166+167167+(** [with_authenticator config auth] is [config] with [auth] as [authenticator] *)
168168+val with_authenticator : config -> X509.Authenticator.t -> config
169169+170170+(** [with_own_certificates config cert] is [config] with [cert] as [own_cert] *)
171171+val with_own_certificates : config -> own_cert -> config
172172+173173+(** [with_acceptable_cas config cas] is [config] with [cas] as [accepted_cas] *)
174174+val with_acceptable_cas : config -> X509.Distinguished_name.t list -> config
+501
vendor/opam/tls/lib/core.ml
···11+(** Core type definitions *)
22+33+open Packet
44+open Ciphersuite
55+66+let ( let* ) = Result.bind
77+88+let guard p e = if p then Ok () else Error e
99+1010+let split_str ?(start = 0) str off =
1111+ String.sub str start off,
1212+ String.sub str (start + off) (String.length str - off - start)
1313+1414+let map_reader_error r =
1515+ Result.map_error (fun e -> `Fatal e) r
1616+1717+type tls13 = [ `TLS_1_3 ]
1818+1919+let pp_tls13 ppf `TLS_1_3 = Fmt.string ppf "TLS 1.3"
2020+2121+type tls_before_13 = [
2222+ | `TLS_1_0
2323+ | `TLS_1_1
2424+ | `TLS_1_2
2525+]
2626+2727+let pp_tls_before_13 ppf = function
2828+ | `TLS_1_0 -> Fmt.string ppf "TLS 1.0"
2929+ | `TLS_1_1 -> Fmt.string ppf "TLS 1.1"
3030+ | `TLS_1_2 -> Fmt.string ppf "TLS 1.2"
3131+3232+type tls_version = [ tls13 | tls_before_13 ]
3333+3434+let pp_tls_version ppf = function
3535+ | #tls13 as v -> pp_tls13 ppf v
3636+ | #tls_before_13 as v -> pp_tls_before_13 ppf v
3737+3838+let pair_of_tls_version = function
3939+ | `TLS_1_0 -> (3, 1)
4040+ | `TLS_1_1 -> (3, 2)
4141+ | `TLS_1_2 -> (3, 3)
4242+ | `TLS_1_3 -> (3, 4)
4343+4444+let compare_tls_version a b = match a, b with
4545+ | `TLS_1_0, `TLS_1_0 -> 0 | `TLS_1_0, _ -> -1 | _, `TLS_1_0 -> 1
4646+ | `TLS_1_1, `TLS_1_1 -> 0 | `TLS_1_1, _ -> -1 | _, `TLS_1_1 -> 1
4747+ | `TLS_1_2, `TLS_1_2 -> 0 | `TLS_1_2, _ -> -1 | _, `TLS_1_2 -> 1
4848+ | `TLS_1_3, `TLS_1_3 -> 0
4949+5050+let next = function
5151+ | `TLS_1_0 -> Some `TLS_1_1
5252+ | `TLS_1_1 -> Some `TLS_1_2
5353+ | `TLS_1_2 -> Some `TLS_1_3
5454+ | `TLS_1_3 -> None
5555+5656+let all_versions (min, max) =
5757+ let rec gen curr =
5858+ if compare_tls_version max curr >= 0 then
5959+ match next curr with
6060+ | None -> [curr]
6161+ | Some c -> curr :: gen c
6262+ else
6363+ []
6464+ in
6565+ List.rev (gen min)
6666+6767+let tls_version_of_pair = function
6868+ | (3, 1) -> Some `TLS_1_0
6969+ | (3, 2) -> Some `TLS_1_1
7070+ | (3, 3) -> Some `TLS_1_2
7171+ | (3, 4) -> Some `TLS_1_3
7272+ | _ -> None
7373+7474+type tls_any_version = [
7575+ | tls_version
7676+ | `SSL_3
7777+ | `TLS_1_X of int
7878+]
7979+8080+let pp_tls_any_version ppf = function
8181+ | #tls_version as v -> pp_tls_version ppf v
8282+ | `SSL_3 -> Fmt.string ppf "SSL3"
8383+ | `TLS_1_X x -> Fmt.pf ppf "TLS1.%u" x
8484+8585+let any_version_to_version = function
8686+ | #tls_version as v -> Some v
8787+ | _ -> None
8888+8989+let version_eq a b =
9090+ match a with
9191+ | #tls_version as x -> compare_tls_version x b = 0
9292+ | _ -> false
9393+9494+let version_ge a b =
9595+ match a with
9696+ | #tls_version as x -> compare_tls_version x b >= 0
9797+ | `SSL_3 -> false
9898+ | `TLS_1_X _ -> true
9999+100100+let tls_any_version_of_pair x =
101101+ match tls_version_of_pair x with
102102+ | Some v -> Some v
103103+ | None ->
104104+ match x with
105105+ | (3, 0) -> Some `SSL_3
106106+ | (3, x) -> Some (`TLS_1_X x)
107107+ | _ -> None
108108+109109+let pair_of_tls_any_version = function
110110+ | #tls_version as x -> pair_of_tls_version x
111111+ | `SSL_3 -> (3, 0)
112112+ | `TLS_1_X m -> (3, m)
113113+114114+let max_protocol_version (_, hi) = hi
115115+let min_protocol_version (lo, _) = lo
116116+117117+type tls_hdr = {
118118+ content_type : content_type;
119119+ version : tls_any_version;
120120+}
121121+122122+let pp_tls_hdr ppf { content_type ; version } =
123123+ Fmt.pf ppf "content type: %a version: %a" pp_content_type content_type
124124+ pp_tls_any_version version
125125+126126+module SessionID = struct
127127+ type t = string
128128+ let compare = String.compare
129129+ let hash t = Hashtbl.hash t
130130+ let equal = String.equal
131131+end
132132+133133+module PreSharedKeyID = struct
134134+ type t = string
135135+ let compare = String.compare
136136+ let hash t = Hashtbl.hash t
137137+ let equal = String.equal
138138+end
139139+140140+type psk_identity = (string * int32) * string
141141+142142+let binders_len psks =
143143+ let binder_len (_, binder) =
144144+ String.length binder + 1 (* binder len *)
145145+ in
146146+ 2 (* binder len *) + List.fold_left (+) 0 (List.map binder_len psks)
147147+148148+type group = [
149149+ | `FFDHE2048
150150+ | `FFDHE3072
151151+ | `FFDHE4096
152152+ | `FFDHE6144
153153+ | `FFDHE8192
154154+ | `X25519
155155+ | `P256
156156+ | `P384
157157+ | `P521
158158+]
159159+160160+let pp_group ppf = function
161161+ | `FFDHE2048 -> Fmt.string ppf "FFDHE2048"
162162+ | `FFDHE3072 -> Fmt.string ppf "FFDHE3072"
163163+ | `FFDHE4096 -> Fmt.string ppf "FFDHE4096"
164164+ | `FFDHE6144 -> Fmt.string ppf "FFDHE6144"
165165+ | `FFDHE8192 -> Fmt.string ppf "FFDHE8192"
166166+ | `X25519 -> Fmt.string ppf "X25519"
167167+ | `P256 -> Fmt.string ppf "P256"
168168+ | `P384 -> Fmt.string ppf "P384"
169169+ | `P521 -> Fmt.string ppf "P521"
170170+171171+let named_group_to_group = function
172172+ | FFDHE2048 -> Some `FFDHE2048
173173+ | FFDHE3072 -> Some `FFDHE3072
174174+ | FFDHE4096 -> Some `FFDHE4096
175175+ | FFDHE6144 -> Some `FFDHE6144
176176+ | FFDHE8192 -> Some `FFDHE8192
177177+ | X25519 -> Some `X25519
178178+ | SECP256R1 -> Some `P256
179179+ | SECP384R1 -> Some `P384
180180+ | SECP521R1 -> Some `P521
181181+ | _ -> None
182182+183183+let group_to_named_group = function
184184+ | `FFDHE2048 -> FFDHE2048
185185+ | `FFDHE3072 -> FFDHE3072
186186+ | `FFDHE4096 -> FFDHE4096
187187+ | `FFDHE6144 -> FFDHE6144
188188+ | `FFDHE8192 -> FFDHE8192
189189+ | `X25519 -> X25519
190190+ | `P256 -> SECP256R1
191191+ | `P384 -> SECP384R1
192192+ | `P521 -> SECP521R1
193193+194194+let group_to_impl = function
195195+ | `FFDHE2048 -> `Finite_field Mirage_crypto_pk.Dh.Group.ffdhe2048
196196+ | `FFDHE3072 -> `Finite_field Mirage_crypto_pk.Dh.Group.ffdhe3072
197197+ | `FFDHE4096 -> `Finite_field Mirage_crypto_pk.Dh.Group.ffdhe4096
198198+ | `FFDHE6144 -> `Finite_field Mirage_crypto_pk.Dh.Group.ffdhe6144
199199+ | `FFDHE8192 -> `Finite_field Mirage_crypto_pk.Dh.Group.ffdhe8192
200200+ | `X25519 -> `X25519
201201+ | `P256 -> `P256
202202+ | `P384 -> `P384
203203+ | `P521 -> `P521
204204+205205+type signature_algorithm = [
206206+ | `RSA_PKCS1_MD5
207207+ | `RSA_PKCS1_SHA1
208208+ | `RSA_PKCS1_SHA224
209209+ | `RSA_PKCS1_SHA256
210210+ | `RSA_PKCS1_SHA384
211211+ | `RSA_PKCS1_SHA512
212212+ | `ECDSA_SECP256R1_SHA1
213213+ | `ECDSA_SECP256R1_SHA256
214214+ | `ECDSA_SECP384R1_SHA384
215215+ | `ECDSA_SECP521R1_SHA512
216216+ | `RSA_PSS_RSAENC_SHA256
217217+ | `RSA_PSS_RSAENC_SHA384
218218+ | `RSA_PSS_RSAENC_SHA512
219219+ | `ED25519
220220+(* | `ED448
221221+ | `RSA_PSS_PSS_SHA256
222222+ | `RSA_PSS_PSS_SHA384
223223+ | `RSA_PSS_PSS_SHA512 *)
224224+]
225225+226226+let hash_of_signature_algorithm = function
227227+ | `RSA_PKCS1_MD5 -> `MD5
228228+ | `RSA_PKCS1_SHA1 -> `SHA1
229229+ | `RSA_PKCS1_SHA224 -> `SHA224
230230+ | `RSA_PKCS1_SHA256 -> `SHA256
231231+ | `RSA_PKCS1_SHA384 -> `SHA384
232232+ | `RSA_PKCS1_SHA512 -> `SHA512
233233+ | `RSA_PSS_RSAENC_SHA256 -> `SHA256
234234+ | `RSA_PSS_RSAENC_SHA384 -> `SHA384
235235+ | `RSA_PSS_RSAENC_SHA512 -> `SHA512
236236+ | `ECDSA_SECP256R1_SHA1 -> `SHA1
237237+ | `ECDSA_SECP256R1_SHA256 -> `SHA256
238238+ | `ECDSA_SECP384R1_SHA384 -> `SHA384
239239+ | `ECDSA_SECP521R1_SHA512 -> `SHA512
240240+ | `ED25519 -> `SHA512
241241+242242+let signature_scheme_of_signature_algorithm = function
243243+ | `RSA_PKCS1_MD5 -> `RSA_PKCS1
244244+ | `RSA_PKCS1_SHA1 -> `RSA_PKCS1
245245+ | `RSA_PKCS1_SHA224 -> `RSA_PKCS1
246246+ | `RSA_PKCS1_SHA256 -> `RSA_PKCS1
247247+ | `RSA_PKCS1_SHA384 -> `RSA_PKCS1
248248+ | `RSA_PKCS1_SHA512 -> `RSA_PKCS1
249249+ | `RSA_PSS_RSAENC_SHA256 -> `RSA_PSS
250250+ | `RSA_PSS_RSAENC_SHA384 -> `RSA_PSS
251251+ | `RSA_PSS_RSAENC_SHA512 -> `RSA_PSS
252252+ | `ECDSA_SECP256R1_SHA1 -> `ECDSA
253253+ | `ECDSA_SECP256R1_SHA256 -> `ECDSA
254254+ | `ECDSA_SECP384R1_SHA384 -> `ECDSA
255255+ | `ECDSA_SECP521R1_SHA512 -> `ECDSA
256256+ | `ED25519 -> `ED25519
257257+258258+let pp_signature_algorithm ppf sa =
259259+ let h = hash_of_signature_algorithm sa
260260+ and ss = signature_scheme_of_signature_algorithm sa
261261+ in
262262+ let pp_signature_scheme ppf = function
263263+ | `RSA_PKCS1 -> Fmt.string ppf "RSA-PKCS1"
264264+ | `RSA_PSS -> Fmt.string ppf "RSA-PSS"
265265+ | `ECDSA -> Fmt.string ppf "ECDSA"
266266+ | `ED25519 -> Fmt.string ppf "ED25519"
267267+ in
268268+ match ss with
269269+ | `ED25519 -> Fmt.pf ppf "%a" pp_signature_scheme ss
270270+ | `ECDSA ->
271271+ let group_to_string = function
272272+ | `ECDSA_SECP256R1_SHA1 -> "SECP256R1"
273273+ | `ECDSA_SECP256R1_SHA256 -> "SECP256R1"
274274+ | `ECDSA_SECP384R1_SHA384 -> "SECP384R1"
275275+ | `ECDSA_SECP521R1_SHA512 -> "SECP521R1"
276276+ | _ -> assert false
277277+ in
278278+ Fmt.pf ppf "%a %s %a" pp_signature_scheme ss (group_to_string sa) pp_hash h
279279+ | _ -> Fmt.pf ppf "%a %a" pp_signature_scheme ss pp_hash h
280280+281281+let rsa_sigalg = function
282282+ | `RSA_PSS_RSAENC_SHA256 | `RSA_PSS_RSAENC_SHA384 | `RSA_PSS_RSAENC_SHA512
283283+ | `RSA_PKCS1_SHA256 | `RSA_PKCS1_SHA384 | `RSA_PKCS1_SHA512
284284+ | `RSA_PKCS1_SHA224 | `RSA_PKCS1_SHA1 | `RSA_PKCS1_MD5 -> true
285285+ | `ECDSA_SECP256R1_SHA1 | `ECDSA_SECP256R1_SHA256 | `ECDSA_SECP384R1_SHA384
286286+ | `ECDSA_SECP521R1_SHA512 | `ED25519 -> false
287287+288288+let tls13_sigalg = function
289289+ | `RSA_PSS_RSAENC_SHA256 | `RSA_PSS_RSAENC_SHA384 | `RSA_PSS_RSAENC_SHA512
290290+ | `ECDSA_SECP256R1_SHA256 | `ECDSA_SECP384R1_SHA384
291291+ | `ECDSA_SECP521R1_SHA512 | `ED25519 -> true
292292+ | `RSA_PKCS1_SHA256 | `RSA_PKCS1_SHA384 | `RSA_PKCS1_SHA512
293293+ | `RSA_PKCS1_SHA224 | `RSA_PKCS1_SHA1 | `RSA_PKCS1_MD5
294294+ | `ECDSA_SECP256R1_SHA1 -> false
295295+296296+let pk_matches_sa pk sa =
297297+ match pk, sa with
298298+ | `RSA _, _ -> rsa_sigalg sa
299299+ | `ED25519 _, `ED25519
300300+ | `P256 _, (`ECDSA_SECP256R1_SHA1 | `ECDSA_SECP256R1_SHA256)
301301+ | `P384 _, `ECDSA_SECP384R1_SHA384
302302+ | `P521 _, `ECDSA_SECP521R1_SHA512 -> true
303303+ | _ -> false
304304+305305+type client_extension = [
306306+ | `Hostname of [`host] Domain_name.t
307307+ | `MaxFragmentLength of max_fragment_length
308308+ | `SupportedGroups of Packet.named_group list
309309+ | `SecureRenegotiation of string
310310+ | `Padding of int
311311+ | `SignatureAlgorithms of signature_algorithm list
312312+ | `ExtendedMasterSecret
313313+ | `ALPN of string list
314314+ | `KeyShare of (Packet.named_group * string) list
315315+ | `EarlyDataIndication
316316+ | `PreSharedKeys of psk_identity list
317317+ | `SupportedVersions of tls_any_version list
318318+ | `PostHandshakeAuthentication
319319+ | `Cookie of string
320320+ | `PskKeyExchangeModes of psk_key_exchange_mode list
321321+ | `ECPointFormats
322322+ | `UnknownExtension of (int * string)
323323+]
324324+325325+type server13_extension = [
326326+ | `KeyShare of (group * string)
327327+ | `PreSharedKey of int
328328+ | `SelectedVersion of tls_version (* only used internally in writer!! *)
329329+]
330330+331331+type server_extension = [
332332+ server13_extension
333333+ | `Hostname
334334+ | `MaxFragmentLength of max_fragment_length
335335+ | `SecureRenegotiation of string
336336+ | `ExtendedMasterSecret
337337+ | `ALPN of string
338338+ | `ECPointFormats
339339+ | `UnknownExtension of (int * string)
340340+]
341341+342342+type encrypted_extension = [
343343+ | `Hostname
344344+ | `MaxFragmentLength of max_fragment_length
345345+ | `SupportedGroups of group list
346346+ | `ALPN of string
347347+ | `EarlyDataIndication
348348+ | `UnknownExtension of (int * string)
349349+]
350350+351351+type hello_retry_extension = [
352352+ | `SelectedGroup of group (* only used internally in writer!! *)
353353+ | `Cookie of string
354354+ | `SelectedVersion of tls_version (* only used internally in writer!! *)
355355+ | `UnknownExtension of (int * string)
356356+]
357357+358358+type client_hello = {
359359+ client_version : tls_any_version;
360360+ client_random : string;
361361+ sessionid : SessionID.t option;
362362+ ciphersuites : any_ciphersuite list;
363363+ extensions : client_extension list
364364+}
365365+366366+type server_hello = {
367367+ server_version : tls_version;
368368+ server_random : string;
369369+ sessionid : SessionID.t option;
370370+ ciphersuite : ciphersuite;
371371+ extensions : server_extension list
372372+}
373373+374374+type dh_parameters = {
375375+ dh_p : string;
376376+ dh_g : string;
377377+ dh_Ys : string;
378378+}
379379+380380+type hello_retry = {
381381+ retry_version : tls_version ;
382382+ ciphersuite : ciphersuite13 ;
383383+ sessionid : SessionID.t option ;
384384+ selected_group : group ;
385385+ extensions : hello_retry_extension list
386386+}
387387+388388+type session_ticket_extension = [
389389+ | `EarlyDataIndication of int32
390390+ | `UnknownExtension of int * string
391391+]
392392+393393+type session_ticket = {
394394+ lifetime : int32 ;
395395+ age_add : int32 ;
396396+ nonce : string ;
397397+ ticket : string ;
398398+ extensions : session_ticket_extension list
399399+}
400400+401401+type certificate_request_extension = [
402402+ (* | `StatusRequest *)
403403+ | `SignatureAlgorithms of signature_algorithm list
404404+ (* | `SignedCertificateTimestamp *)
405405+ | `CertificateAuthorities of X509.Distinguished_name.t list
406406+ (* | `OidFilters *)
407407+ (* | `SignatureAlgorithmsCert *)
408408+ | `UnknownExtension of (int * string)
409409+]
410410+411411+type tls_handshake =
412412+ | HelloRequest
413413+ | HelloRetryRequest of hello_retry
414414+ | EncryptedExtensions of encrypted_extension list
415415+ | ServerHelloDone
416416+ | ClientHello of client_hello
417417+ | ServerHello of server_hello
418418+ | Certificate of string
419419+ | ServerKeyExchange of string
420420+ | CertificateRequest of string
421421+ | ClientKeyExchange of string
422422+ | CertificateVerify of string
423423+ | Finished of string
424424+ | SessionTicket of session_ticket
425425+ | KeyUpdate of key_update_request_type
426426+ | EndOfEarlyData
427427+428428+let pp_handshake ppf = function
429429+ | HelloRequest -> Fmt.string ppf "HelloRequest"
430430+ | HelloRetryRequest _ -> Fmt.string ppf "HelloRetryRequest"
431431+ | EncryptedExtensions _ -> Fmt.string ppf "EncryptedExtensions"
432432+ | ServerHelloDone -> Fmt.string ppf "ServerHelloDone"
433433+ | ClientHello _ -> Fmt.string ppf "ClientHello"
434434+ | ServerHello _ -> Fmt.string ppf "ServerHello"
435435+ | Certificate _ -> Fmt.string ppf "Certificate"
436436+ | ServerKeyExchange _ -> Fmt.string ppf "ServerKeyExchange"
437437+ | CertificateRequest _ -> Fmt.string ppf "CertificateRequest"
438438+ | ClientKeyExchange _ -> Fmt.string ppf "ClientKeyExchange"
439439+ | CertificateVerify _ -> Fmt.string ppf "CertificateVerify"
440440+ | Finished _ -> Fmt.string ppf "Finished"
441441+ | SessionTicket _ -> Fmt.string ppf "SessionTicket"
442442+ | KeyUpdate _ -> Fmt.string ppf "KeyUpdate"
443443+ | EndOfEarlyData -> Fmt.string ppf "EndOfEarlyData"
444444+445445+let src = Logs.Src.create "tls.tracing" ~doc:"TLS tracing"
446446+module Tracing = struct
447447+ include (val Logs.src_log src : Logs.LOG)
448448+ let cs ~tag buf = debug (fun m -> m "%s@.%a" tag (Ohex.pp_hexdump ()) buf)
449449+ let hs ~tag hs = debug (fun m -> m "%s %a" tag pp_handshake hs)
450450+end
451451+452452+type tls_alert = alert_level * alert_type
453453+454454+(** the master secret of a TLS connection *)
455455+type master_secret = string
456456+457457+type psk13 = {
458458+ identifier : string ;
459459+ obfuscation : int32 ;
460460+ secret : string ;
461461+ lifetime : int32 ;
462462+ early_data : int32 ;
463463+ issued_at : Ptime.t ;
464464+ (* origin : [ `Resumption | `External ] (* using different labels for binder_key *) *)
465465+}
466466+467467+type epoch_state = [ `ZeroRTT | `Established ]
468468+469469+(** information about an open session *)
470470+type epoch_data = {
471471+ side : [ `Client | `Server ] ;
472472+ state : epoch_state ;
473473+ protocol_version : tls_version ;
474474+ ciphersuite : Ciphersuite.ciphersuite ;
475475+ peer_random : string ;
476476+ peer_certificate_chain : X509.Certificate.t list ;
477477+ peer_certificate : X509.Certificate.t option ;
478478+ peer_name : [`host] Domain_name.t option ;
479479+ trust_anchor : X509.Certificate.t option ;
480480+ received_certificates : X509.Certificate.t list ;
481481+ own_random : string ;
482482+ own_certificate : X509.Certificate.t list ;
483483+ own_private_key : X509.Private_key.t option ;
484484+ own_name : [`host] Domain_name.t option ;
485485+ master_secret : master_secret ;
486486+ exporter_master_secret : master_secret ;
487487+ session_id : SessionID.t ;
488488+ extended_ms : bool ;
489489+ alpn_protocol : string option ;
490490+ tls_unique : string option ;
491491+}
492492+493493+let supports_key_usage ?(not_present = false) usage cert =
494494+ match X509.Extension.(find Key_usage (X509.Certificate.extensions cert)) with
495495+ | None -> not_present
496496+ | Some (_, kus) -> List.mem usage kus
497497+498498+let supports_extended_key_usage ?(not_present = false) usage cert =
499499+ match X509.Extension.(find Ext_key_usage (X509.Certificate.extensions cert)) with
500500+ | None -> not_present
501501+ | Some (_, kus) -> List.mem usage kus
+159
vendor/opam/tls/lib/crypto.ml
···11+open Mirage_crypto
22+33+open Ciphersuite
44+55+(* on-the-wire dh_params <-> (group, pub_message) *)
66+let dh_params_pack { Mirage_crypto_pk.Dh.p; gg ; _ } message =
77+ let cs_of_z = Mirage_crypto_pk.Z_extra.to_octets_be ?size:None in
88+ { Core.dh_p = cs_of_z p ; dh_g = cs_of_z gg ; dh_Ys = message }
99+1010+and dh_params_unpack { Core.dh_p ; dh_g ; dh_Ys } =
1111+ let z_of_cs = Mirage_crypto_pk.Z_extra.of_octets_be ?bits:None in
1212+ match Mirage_crypto_pk.Dh.group ~p:(z_of_cs dh_p) ~gg:(z_of_cs dh_g) () with
1313+ | Ok dh -> Ok (dh, dh_Ys)
1414+ | Error _ as e -> e
1515+1616+module Ciphers = struct
1717+1818+ (* I'm not sure how to get rid of this type, but would welcome a solution *)
1919+ (* only used as result of get_block, which is called by get_cipher below *)
2020+ type keyed = | K_CBC : 'k State.cbc_cipher * (string -> 'k) -> keyed
2121+2222+ let get_block = function
2323+ | TRIPLE_DES_EDE_CBC ->
2424+ K_CBC ( (module DES.CBC : Block.CBC with type key = DES.CBC.key),
2525+ DES.CBC.of_secret )
2626+2727+ | AES_128_CBC ->
2828+ K_CBC ( (module AES.CBC : Block.CBC with type key = AES.CBC.key),
2929+ AES.CBC.of_secret )
3030+3131+ | AES_256_CBC ->
3232+ K_CBC ( (module AES.CBC : Block.CBC with type key = AES.CBC.key),
3333+ AES.CBC.of_secret )
3434+3535+ type aead_keyed = | K_AEAD : 'k State.aead_cipher * (string -> 'k) * bool -> aead_keyed
3636+ let get_aead =
3737+ function
3838+ | AES_128_CCM | AES_256_CCM ->
3939+ K_AEAD ((module AES.CCM16 : AEAD with type key = AES.CCM16.key),
4040+ AES.CCM16.of_secret, true)
4141+ | AES_128_GCM | AES_256_GCM ->
4242+ K_AEAD ((module AES.GCM : AEAD with type key = AES.GCM.key),
4343+ AES.GCM.of_secret, true)
4444+ | CHACHA20_POLY1305 ->
4545+ K_AEAD ((module Chacha20 : AEAD with type key = Chacha20.key),
4646+ Chacha20.of_secret, false)
4747+4848+ let get_aead_cipher ~secret ~nonce aead_cipher =
4949+ match get_aead aead_cipher with
5050+ | K_AEAD (cipher, sec, explicit_nonce) ->
5151+ let cipher_secret = sec secret in
5252+ State.(AEAD { cipher ; cipher_secret ; nonce ; explicit_nonce })
5353+5454+ let get_cipher ~secret ~hmac_secret ~iv_mode ~nonce = function
5555+ | `Block (cipher, hmac) ->
5656+ ( match get_block cipher with
5757+ | K_CBC (cipher, sec) ->
5858+ let cipher_secret = sec secret in
5959+ State.(CBC { cipher ; cipher_secret ; iv_mode ; hmac ; hmac_secret })
6060+ )
6161+6262+ | `AEAD cipher -> get_aead_cipher ~secret ~nonce cipher
6363+end
6464+6565+let sequence_buf seq =
6666+ let buf = Bytes.create 8 in
6767+ Bytes.set_int64_be buf 0 seq ;
6868+ Bytes.unsafe_to_string buf
6969+7070+let aead_nonce nonce seq =
7171+ let s =
7272+ let l = String.length nonce in
7373+ let buf = Bytes.make l '\x00' in
7474+ Bytes.set_int64_be buf (l - 8) seq;
7575+ Bytes.unsafe_to_string buf
7676+ in
7777+ Uncommon.xor nonce s
7878+7979+let adata_1_3 len =
8080+ (* additional data in TLS 1.3 is using the header (RFC 8446 Section 5.2):
8181+ - APPLICATION_TYPE
8282+ - 0x03 0x03 (for TLS version 1.2 -- binary representation is 0x03 0x03)
8383+ - <length in 16 bit>
8484+ *)
8585+ let buf = Bytes.create 5 in
8686+ Bytes.set_uint8 buf 0 (Packet.content_type_to_int Packet.APPLICATION_DATA) ;
8787+ Bytes.set_uint8 buf 1 3;
8888+ Bytes.set_uint8 buf 2 3;
8989+ Bytes.set_uint16_be buf 3 len ;
9090+ Bytes.unsafe_to_string buf
9191+9292+let pseudo_header seq ty (v_major, v_minor) v_length =
9393+ let buf = Bytes.create 13 in
9494+ Bytes.set_int64_be buf 0 seq;
9595+ Bytes.set_uint8 buf 8 (Packet.content_type_to_int ty);
9696+ Bytes.set_uint8 buf 9 v_major;
9797+ Bytes.set_uint8 buf 10 v_minor;
9898+ Bytes.set_uint16_be buf 11 v_length;
9999+ Bytes.unsafe_to_string buf
100100+101101+(* MAC used in TLS *)
102102+let mac hash key pseudo_hdr data =
103103+ let module H = (val Digestif.module_of_hash' hash) in
104104+ H.(to_raw_string (hmacv_string ~key [ pseudo_hdr ; data ]))
105105+106106+let cbc_block (type a) cipher =
107107+ let module C = (val cipher : Block.CBC with type key = a) in C.block_size
108108+109109+(* crazy CBC padding and unpadding for TLS *)
110110+let cbc_pad block data =
111111+ (* 1 is the padding length, encoded as 8 bit at the end of the fragment *)
112112+ let len = 1 + String.length data in
113113+ (* we might want to add additional blocks of padding *)
114114+ let padding_length = block - (len mod block) in
115115+ (* 1 is again padding length field *)
116116+ let cstruct_len = padding_length + 1 in
117117+ String.make cstruct_len (Char.unsafe_chr padding_length)
118118+119119+let cbc_unpad data =
120120+ let len = String.length data in
121121+ let padlen = String.get_uint8 data (pred len) in
122122+123123+ let rec check = function
124124+ | i when i > padlen -> true
125125+ | i -> (String.get_uint8 data (len - padlen - 1 + i) = padlen) && check (succ i) in
126126+127127+ try
128128+ if check 0 then Some (String.sub data 0 (len - padlen - 1)) else None
129129+ with Invalid_argument _ -> None
130130+131131+let tag_len (type a) cipher =
132132+ let module C = (val cipher : AEAD with type key = a) in
133133+ C.tag_size
134134+135135+let encrypt_aead (type a) ~cipher ~key ~nonce ?adata data =
136136+ let module C = (val cipher : AEAD with type key = a) in
137137+ C.authenticate_encrypt ~key ~nonce ?adata data
138138+139139+let decrypt_aead (type a) ~cipher ~key ~nonce ?adata data =
140140+ let module C = (val cipher : AEAD with type key = a) in
141141+ C.authenticate_decrypt ~key ~nonce ?adata data
142142+143143+let encrypt_cbc (type a) ~cipher ~key ~iv data =
144144+ let module C = (val cipher : Block.CBC with type key = a) in
145145+ let message = C.encrypt ~key ~iv (data ^ cbc_pad C.block_size data) in
146146+ (message, C.next_iv ~iv message)
147147+148148+let decrypt_cbc (type a) ~cipher ~key ~iv data =
149149+ let module C = (val cipher : Block.CBC with type key = a) in
150150+ try
151151+ let message = C.decrypt ~key ~iv data in
152152+ match cbc_unpad message with
153153+ | Some res -> Some (res, C.next_iv ~iv data)
154154+ | None -> None
155155+ with
156156+ (* This bails out immediately on mis-alignment, making it very timeable.
157157+ * However, decryption belongs to the outermost level and this operation's
158158+ * timing does not leak information ala padding oracle and friends. *)
159159+ | Invalid_argument _ -> None
···11+open Core
22+open State
33+44+type state = State.state
55+66+type error = State.error
77+type fatal = State.fatal
88+type failure = State.failure
99+1010+let alert_of_authentication_failure = function
1111+ | `LeafCertificateExpired _ -> Packet.CERTIFICATE_EXPIRED
1212+ | _ -> Packet.BAD_CERTIFICATE
1313+1414+let alert_of_error = function
1515+ | `NoConfiguredVersions _ -> Packet.PROTOCOL_VERSION
1616+ | `NoConfiguredCiphersuite _ -> Packet.HANDSHAKE_FAILURE
1717+ | `NoConfiguredSignatureAlgorithm _ -> Packet.HANDSHAKE_FAILURE
1818+ | `AuthenticationFailure err -> alert_of_authentication_failure err
1919+ | `NoMatchingCertificateFound _ -> Packet.UNRECOGNIZED_NAME
2020+ | `CouldntSelectCertificate -> Packet.HANDSHAKE_FAILURE
2121+2222+let alert_of_fatal = function
2323+ | `Protocol_version _ -> Packet.PROTOCOL_VERSION
2424+ | `Unexpected _ -> Packet.UNEXPECTED_MESSAGE
2525+ | `Decode _ -> Packet.DECODE_ERROR
2626+ | `Handshake _ -> Packet.HANDSHAKE_FAILURE
2727+ | `Bad_mac -> Packet.BAD_RECORD_MAC
2828+ | `Record_overflow _ -> Packet.RECORD_OVERFLOW
2929+ | `Unsupported_extension -> Packet.UNSUPPORTED_EXTENSION
3030+ | `Bad_certificate _ -> Packet.BAD_CERTIFICATE
3131+ | `Missing_extension _ -> Packet.MISSING_EXTENSION
3232+ | `Inappropriate_fallback -> Packet.INAPPROPRIATE_FALLBACK
3333+ | `No_application_protocol -> Packet.NO_APPLICATION_PROTOCOL
3434+3535+let alert_of_failure = function
3636+ | `Error x -> Packet.FATAL, alert_of_error x
3737+ | `Fatal x -> Packet.FATAL, alert_of_fatal x
3838+ | `Alert _ -> Packet.WARNING, Packet.CLOSE_NOTIFY
3939+4040+let pp_failure = State.pp_failure
4141+4242+let string_of_failure = Fmt.to_to_string pp_failure
4343+4444+type ret =
4545+ (state * [ `Eof ] option
4646+ * [ `Response of string option ]
4747+ * [ `Data of string option ],
4848+ failure * [ `Response of string ]) result
4949+5050+let new_state config role =
5151+ let handshake_state = match role with
5252+ | `Client -> Client ClientInitial
5353+ | `Server -> Server AwaitClientHello
5454+ in
5555+ let version = max_protocol_version Config.(config.protocol_versions) in
5656+ let handshake = {
5757+ session = [] ;
5858+ protocol_version = version ;
5959+ early_data_left = 0l ;
6060+ machina = handshake_state ;
6161+ config = config ;
6262+ hs_fragment = "" ;
6363+ }
6464+ in
6565+ {
6666+ handshake = handshake ;
6767+ decryptor = None ;
6868+ encryptor = None ;
6969+ fragment = "" ;
7070+ read_closed = false ;
7171+ write_closed = false ;
7272+ }
7373+7474+type raw_record = tls_hdr * string
7575+7676+let pp_raw_record ppf (hdr, data) =
7777+ Fmt.pf ppf "%a (%u bytes data)" pp_tls_hdr hdr (String.length data)
7878+7979+let pp_frame ppf (ty, data) =
8080+ Fmt.pf ppf "%a (%u bytes data)" Packet.pp_content_type ty
8181+ (String.length data)
8282+8383+(* well-behaved pure encryptor *)
8484+let encrypt (version : tls_version) (st : crypto_state) ty buf off len =
8585+ match st with
8686+ | None -> (st, ty, String.sub buf off len)
8787+ | Some ctx ->
8888+ match version with
8989+ | `TLS_1_3 ->
9090+ (match ctx.cipher_st with
9191+ | AEAD c ->
9292+ let buf =
9393+ let b = Bytes.create (len + 1) in
9494+ Bytes.set_uint8 b len (Packet.content_type_to_int ty);
9595+ Bytes.blit_string buf off b 0 len;
9696+ Bytes.unsafe_to_string b
9797+ in
9898+ let nonce = Crypto.aead_nonce c.nonce ctx.sequence in
9999+ let adata = Crypto.adata_1_3 (String.length buf + Crypto.tag_len c.cipher) in
100100+ let buf = Crypto.encrypt_aead ~cipher:c.cipher ~adata ~key:c.cipher_secret ~nonce buf in
101101+ (Some { ctx with sequence = Int64.succ ctx.sequence }, Packet.APPLICATION_DATA, buf)
102102+ | _ -> assert false)
103103+ | _ ->
104104+ let pseudo_hdr =
105105+ let seq = ctx.sequence
106106+ and ver = pair_of_tls_version version
107107+ in
108108+ Crypto.pseudo_header seq ty ver len
109109+ in
110110+ let c_st, enc =
111111+ match ctx.cipher_st with
112112+ | CBC c ->
113113+ let enc iv =
114114+ (* TODO only until digestif goes beyond 1.2.0 (feedable hmac) *)
115115+ let data = String.sub buf off len in
116116+ let signature = Crypto.mac c.hmac c.hmac_secret pseudo_hdr buf in
117117+ let to_encrypt = data ^ signature in
118118+ Crypto.encrypt_cbc ~cipher:c.cipher ~key:c.cipher_secret ~iv to_encrypt
119119+ in
120120+ ( match c.iv_mode with
121121+ | Random_iv ->
122122+ let iv = Mirage_crypto_rng.generate (Crypto.cbc_block c.cipher) in
123123+ let m, _ = enc iv in
124124+ (CBC c, iv ^ m)
125125+ | Iv iv ->
126126+ let m, iv' = enc iv in
127127+ (CBC { c with iv_mode = Iv iv' }, m) )
128128+ | AEAD c ->
129129+ let buf = String.sub buf off len in
130130+ if c.explicit_nonce then
131131+ let explicit_nonce = Crypto.sequence_buf ctx.sequence in
132132+ let nonce = c.nonce ^ explicit_nonce
133133+ in
134134+ let msg =
135135+ Crypto.encrypt_aead ~cipher:c.cipher ~key:c.cipher_secret ~nonce ~adata:pseudo_hdr buf
136136+ in
137137+ (AEAD c, explicit_nonce ^ msg)
138138+ else
139139+ (* RFC 7905: no explicit nonce, instead TLS 1.3 construction is adapted *)
140140+ let nonce = Crypto.aead_nonce c.nonce ctx.sequence in
141141+ let msg =
142142+ Crypto.encrypt_aead ~cipher:c.cipher ~key:c.cipher_secret ~nonce ~adata:pseudo_hdr buf
143143+ in
144144+ (AEAD c, msg)
145145+ in
146146+ (Some { sequence = Int64.succ ctx.sequence ; cipher_st = c_st }, ty, enc)
147147+148148+(* well-behaved pure decryptor *)
149149+let verify_mac sequence mac mac_k ty ver decrypted =
150150+ let macstart =
151151+ let module H = (val Digestif.module_of_hash' mac) in
152152+ String.length decrypted - H.digest_size
153153+ in
154154+ let* () = guard (macstart >= 0) (`Fatal (`Decode "MAC underflow")) in
155155+ let (body, mmac) = split_str decrypted macstart in
156156+ let cmac =
157157+ let ver = pair_of_tls_version ver in
158158+ let hdr = Crypto.pseudo_header sequence ty ver (String.length body) in
159159+ Crypto.mac mac mac_k hdr body in
160160+ let* () = guard (String.equal cmac mmac) (`Fatal `Bad_mac) in
161161+ Ok body
162162+163163+164164+let decrypt ?(trial = false) (version : tls_version) (st : crypto_state) ty buf =
165165+166166+ let compute_mac seq mac mac_k buf = verify_mac seq mac mac_k ty version buf in
167167+ (* hmac is computed in this failure branch from the encrypted data, in the
168168+ successful branch it is decrypted - padding (which is smaller equal than
169169+ encrypted data) *)
170170+ (* This comment is borrowed from miTLS, but applies here as well: *)
171171+ (* We implement standard mitigation for padding oracles. Still, we note a
172172+ small timing leak here: The time to verify the mac is linear in the
173173+ plaintext length. *)
174174+ (* defense against http://lasecwww.epfl.ch/memo/memo_ssl.shtml 1) in
175175+ https://www.openssl.org/~bodo/tls-cbc.txt *)
176176+ let mask_decrypt_failure seq mac mac_k =
177177+ let* _ = compute_mac seq mac mac_k buf in
178178+ Error (`Fatal `Bad_mac)
179179+ in
180180+181181+ let dec ctx =
182182+ let seq = ctx.sequence in
183183+ match ctx.cipher_st with
184184+ | CBC c ->
185185+ let dec iv buf =
186186+ match Crypto.decrypt_cbc ~cipher:c.cipher ~key:c.cipher_secret ~iv buf with
187187+ | None ->
188188+ mask_decrypt_failure seq c.hmac c.hmac_secret
189189+ | Some (dec, iv') ->
190190+ let* msg = compute_mac seq c.hmac c.hmac_secret dec in
191191+ Ok (msg, iv')
192192+ in
193193+ ( match c.iv_mode with
194194+ | Iv iv ->
195195+ let* msg, iv' = dec iv buf in
196196+ Ok (CBC { c with iv_mode = Iv iv' }, msg)
197197+ | Random_iv ->
198198+ if String.length buf < Crypto.cbc_block c.cipher then
199199+ Error (`Fatal (`Decode "MAC underflow"))
200200+ else
201201+ let iv, buf = split_str buf (Crypto.cbc_block c.cipher) in
202202+ let* msg, _ = dec iv buf in
203203+ Ok (CBC c, msg) )
204204+205205+ | AEAD c ->
206206+ if c.explicit_nonce then
207207+ let explicit_nonce_len = 8 in
208208+ if String.length buf < explicit_nonce_len then
209209+ Error (`Fatal (`Decode "MAC underflow"))
210210+ else
211211+ let explicit_nonce, buf = split_str buf explicit_nonce_len in
212212+ let adata =
213213+ let ver = pair_of_tls_version version in
214214+ Crypto.pseudo_header seq ty ver (String.length buf - Crypto.tag_len c.cipher)
215215+ and nonce = c.nonce ^ explicit_nonce
216216+ in
217217+ match Crypto.decrypt_aead ~cipher:c.cipher ~key:c.cipher_secret ~nonce ~adata buf with
218218+ | None -> Error (`Fatal `Bad_mac)
219219+ | Some x -> Ok (AEAD c, x)
220220+ else
221221+ (* RFC 7905: no explicit nonce, instead TLS 1.3 construction is adapted *)
222222+ let adata =
223223+ let ver = pair_of_tls_version version in
224224+ Crypto.pseudo_header seq ty ver (String.length buf - Crypto.tag_len c.cipher)
225225+ and nonce = Crypto.aead_nonce c.nonce seq
226226+ in
227227+ (match Crypto.decrypt_aead ~adata ~cipher:c.cipher ~key:c.cipher_secret ~nonce buf with
228228+ | None -> Error (`Fatal `Bad_mac)
229229+ | Some x -> Ok (AEAD c, x))
230230+ in
231231+ match st, version with
232232+ | None, _ when ty = Packet.APPLICATION_DATA ->
233233+ (* the server can end up in the situation:
234234+ CH [+early_data +key_share] ; APP_DATA ---->
235235+ <--- HRR [+key_share] (does not install a decryptor,
236236+ early data now disallowed)
237237+ CH [+key_share] ----->
238238+ the APP_DATA above cannot be decrypted or used, so we drop it.
239239+ *)
240240+ Ok (None, "", Packet.APPLICATION_DATA)
241241+ | None, _ -> Ok (st, buf, ty)
242242+ | Some ctx, `TLS_1_3 ->
243243+ (match ty with
244244+ | Packet.CHANGE_CIPHER_SPEC -> Ok (st, buf, ty)
245245+ | Packet.APPLICATION_DATA ->
246246+ (match ctx.cipher_st with
247247+ | AEAD c ->
248248+ let nonce = Crypto.aead_nonce c.nonce ctx.sequence in
249249+ let unpad x =
250250+ let rec eat = function
251251+ | -1 -> Error (`Fatal (`Unexpected (`Message "missing content type")))
252252+ | idx -> match String.get_uint8 x idx with
253253+ | 0 -> eat (pred idx)
254254+ | n -> match Packet.int_to_content_type n with
255255+ | Some ct -> Ok (String.sub x 0 idx, ct)
256256+ | None -> Error (`Fatal (`Unexpected (`Content_type n)))
257257+ in
258258+ eat (pred (String.length x))
259259+ in
260260+ let adata = Crypto.adata_1_3 (String.length buf) in
261261+ (match Crypto.decrypt_aead ~adata ~cipher:c.cipher ~key:c.cipher_secret ~nonce buf with
262262+ | None ->
263263+ if trial then
264264+ Ok (Some ctx, "", Packet.APPLICATION_DATA)
265265+ else
266266+ Error (`Fatal `Bad_mac)
267267+ | Some x ->
268268+ let* data, ty = unpad x in
269269+ Ok (Some { ctx with sequence = Int64.succ ctx.sequence }, data, ty))
270270+ | _ -> Error (`Fatal (`Handshake (`Message "unexpected cipher state (must be AEAD)"))))
271271+ | ct ->
272272+ let msg = "unexpected content type (TLS 1.3, encrypted) " ^ Packet.content_type_to_string ct in
273273+ Error (`Fatal (`Handshake (`Message msg))))
274274+ | Some ctx, _ ->
275275+ let* st', msg = dec ctx in
276276+ let ctx' = { cipher_st = st' ; sequence = Int64.succ ctx.sequence } in
277277+ Ok (Some ctx', msg, ty)
278278+279279+(* party time *)
280280+let rec separate_records : string -> ((tls_hdr * string) list * string, failure) result
281281+= fun buf ->
282282+ match Reader.parse_record buf with
283283+ | Ok (`Fragment b) -> Ok ([], b)
284284+ | Ok (`Record (packet, fragment)) ->
285285+ let* tl, frag = separate_records fragment in
286286+ Ok (packet :: tl, frag)
287287+ | Error e ->
288288+ Tracing.cs ~tag:"buf-in" buf ;
289289+ Error (`Fatal e)
290290+291291+let encrypt_records encryptor version records =
292292+ let rec crypt st acc = function
293293+ | [] -> st, List.rev acc
294294+ | (ty, buf) :: rest ->
295295+ let bufl = String.length buf in
296296+ let rec doit st acc off =
297297+ if bufl - off >= 1 lsl 14 then
298298+ let len = 1 lsl 14 in
299299+ let st, ty, buf = encrypt version st ty buf off len in
300300+ (doit [@tailcall]) st ((ty, buf) :: acc) (off + len)
301301+ else
302302+ let st, ty, buf = encrypt version st ty buf off (bufl - off) in
303303+ st, (ty, buf) :: acc
304304+ in
305305+ let st, res = doit st [] 0 in
306306+ (crypt [@tailcall]) st (res @ acc) rest
307307+ in
308308+ crypt encryptor [] records
309309+310310+module Alert = struct
311311+ (* The alert protocol:
312312+ - receiving a close_notify leads to eof (never read() any further data)
313313+ - any fatal alert leads to sending a close_notify and state is closed
314314+ *)
315315+316316+ open Packet
317317+318318+ let make ?level typ = (ALERT, Writer.assemble_alert ?level typ)
319319+320320+ let close_notify = make ~level:WARNING CLOSE_NOTIFY
321321+322322+ let handle buf =
323323+ let* alert = map_reader_error (Reader.parse_alert buf) in
324324+ let _, a_type = alert in
325325+ Tracing.debug (fun m -> m "alert-in %a" pp_alert alert) ;
326326+ match a_type with
327327+ | CLOSE_NOTIFY | USER_CANCELED -> Ok true
328328+ | _ -> Error (`Alert a_type)
329329+end
330330+331331+let hs_can_handle_appdata s =
332332+ (* When is a TLS session up for some application data?
333333+ - initial handshake must be finished!
334334+ - renegotiation must not be in progress
335335+ --> thus only ok for Established
336336+ - but ok if server sent a HelloRequest and can get first some appdata then ClientHello
337337+ --> or converse: client sent ClientHello, waiting for ServerHello *)
338338+ (* turns out, rules in 1.3 are slightly different -- server may send appdata after its first flight!
339339+ this means in any observable state! (apart from when a HRR was sent) *)
340340+ match s.machina with
341341+ | Server13 AwaitClientHelloHRR13 -> false
342342+ | Server Established | Server AwaitClientHelloRenegotiate | Server13 _
343343+ | Client Established | Client AwaitServerHelloRenegotiate _ | Client13 Established13 -> true
344344+ | _ -> false
345345+346346+let early_data s =
347347+ match s.machina with
348348+ | Server13 AwaitClientHelloHRR13
349349+ | Server13 (AwaitEndOfEarlyData13 _) | Server13 (AwaitClientFinished13 _)
350350+ | Server13 (AwaitClientCertificate13 _) | Server13 (AwaitClientCertificateVerify13 _) -> true
351351+ | _ -> false
352352+353353+let rec separate_handshakes buf =
354354+ match Reader.parse_handshake_frame buf with
355355+ | None, rest -> [], rest
356356+ | Some hs, rest ->
357357+ let rt, frag = separate_handshakes rest in
358358+ hs :: rt, frag
359359+360360+let handle_change_cipher_spec = function
361361+ | Client cs -> Handshake_client.handle_change_cipher_spec cs
362362+ | Server ss -> Handshake_server.handle_change_cipher_spec ss
363363+ (* D.4: the client may send a CCS before its second flight
364364+ (before second ClientHello or encrypted handshake flight)
365365+ the server may send it immediately after its first handshake message
366366+ (ServerHello or HelloRetryRequest) *)
367367+ | Client13 (AwaitServerEncryptedExtensions13 _)
368368+ | Client13 (AwaitServerHello13 _)
369369+ | Server13 AwaitClientHelloHRR13
370370+ | Server13 (AwaitClientCertificate13 _)
371371+ | Server13 (AwaitClientFinished13 _) -> (fun s _ -> Ok (s, []))
372372+ | _ -> (fun _ _ -> Error (`Fatal (`Unexpected (`Message "change cipher spec"))))
373373+374374+and handle_handshake = function
375375+ | Client cs -> Handshake_client.handle_handshake cs
376376+ | Server ss -> Handshake_server.handle_handshake ss
377377+ | Client13 cs -> Handshake_client13.handle_handshake cs
378378+ | Server13 ss -> Handshake_server13.handle_handshake ss
379379+380380+let non_empty cs =
381381+ if String.length cs = 0 then None else Some cs
382382+383383+let handle_packet hs buf = function
384384+(* RFC 5246 -- 6.2.1.:
385385+ Implementations MUST NOT send zero-length fragments of Handshake,
386386+ Alert, or ChangeCipherSpec content types. Zero-length fragments of
387387+ Application data MAY be sent as they are potentially useful as a
388388+ traffic analysis countermeasure.
389389+ *)
390390+391391+ | Packet.ALERT ->
392392+ let* eof = Alert.handle buf in
393393+ Ok (hs, [], None, eof)
394394+395395+ | Packet.APPLICATION_DATA ->
396396+ if hs_can_handle_appdata hs || (early_data hs && String.length hs.hs_fragment = 0) then
397397+ (Tracing.cs ~tag:"application-data-in" buf;
398398+ Ok (hs, [], non_empty buf, false))
399399+ else
400400+ Error (`Fatal (`Unexpected (`Message "application data")))
401401+402402+ | Packet.CHANGE_CIPHER_SPEC ->
403403+ let* hs, items = handle_change_cipher_spec hs.machina hs buf in
404404+ Ok (hs, items, None, false)
405405+406406+ | Packet.HANDSHAKE ->
407407+ let hss, hs_fragment = separate_handshakes (hs.hs_fragment ^ buf) in
408408+ let hs = { hs with hs_fragment } in
409409+ let* hs, items =
410410+ List.fold_left (fun acc raw ->
411411+ let* hs, items = acc in
412412+ let* hs', items' = handle_handshake hs.machina hs raw in
413413+ Ok (hs', items @ items'))
414414+ (Ok (hs, [])) hss
415415+ in
416416+ Ok (hs, items, None, false)
417417+418418+let decrement_early_data hs ty buf =
419419+ let bytes left cipher =
420420+ let count = String.length buf - fst (Ciphersuite.kn_13 (Ciphersuite.privprot13 cipher)) in
421421+ let left' = Int32.sub left (Int32.of_int count) in
422422+ if left' < 0l then
423423+ Error (`Fatal (`Unexpected (`Message "too many 0RTT bytes")))
424424+ else
425425+ Ok left'
426426+ in
427427+ if ty = Packet.APPLICATION_DATA && early_data hs then
428428+ let cipher = match hs.session with
429429+ | `TLS13 sd::_ -> sd.ciphersuite13
430430+ | _ -> `AES_128_GCM_SHA256
431431+ (* TODO assert and ensure that all early_data states have a cipher *)
432432+ in
433433+ let* early_data_left = bytes hs.early_data_left cipher in
434434+ Ok { hs with early_data_left }
435435+ else
436436+ Ok hs
437437+438438+(* the main thingy *)
439439+let handle_raw_record state (hdr, buf as record : raw_record) =
440440+441441+ Tracing.debug (fun m -> m "record-in %a" pp_raw_record record) ;
442442+ let hs = state.handshake in
443443+ let version = hs.protocol_version in
444444+ let* () =
445445+ match hs.machina, version with
446446+ | Client (AwaitServerHello _), _ -> Ok ()
447447+ | Server AwaitClientHello, _ -> Ok ()
448448+ | Server13 AwaitClientHelloHRR13, _ -> Ok ()
449449+ | _, `TLS_1_3 ->
450450+ guard (hdr.version = `TLS_1_2)
451451+ (`Fatal (`Protocol_version (`Bad_record hdr.version)))
452452+ | _, v ->
453453+ guard (version_eq hdr.version v)
454454+ (`Fatal (`Protocol_version (`Bad_record hdr.version)))
455455+ in
456456+ let trial = match hs.machina with
457457+ | Server13 (AwaitEndOfEarlyData13 _) | Server13 Established13 -> false
458458+ | Server13 _ -> hs.early_data_left > 0l && String.length hs.hs_fragment = 0
459459+ | _ -> false
460460+ in
461461+ let* dec_st, dec, ty = decrypt ~trial version state.decryptor hdr.content_type buf in
462462+ let* handshake = decrement_early_data hs ty buf in
463463+ Tracing.debug (fun m -> m "frame-in %a" pp_frame (ty, dec)) ;
464464+ let* handshake, items, data, read_closed = handle_packet handshake dec ty in
465465+ let encryptor, decryptor, encs =
466466+ List.fold_left (fun (enc, dec, es) -> function
467467+ | `Change_enc enc' -> (Some enc', dec, es)
468468+ | `Change_dec dec' -> (enc, Some dec', es)
469469+ | `Record r ->
470470+ Tracing.debug (fun m -> m "frame-out %a" pp_frame r) ;
471471+ let (enc', encbuf) = encrypt_records enc handshake.protocol_version [r] in
472472+ (enc', dec, es @ encbuf))
473473+ (state.encryptor, dec_st, [])
474474+ items
475475+ in
476476+ List.iter (fun f -> Tracing.debug (fun m -> m "record-out %a" pp_frame f)) encs ;
477477+ let read_closed = read_closed || state.read_closed in
478478+ let state' = { state with handshake ; encryptor ; decryptor ; read_closed } in
479479+ Ok (state', encs, data)
480480+481481+let maybe_app a b = match a, b with
482482+ | Some x, Some y -> Some (x ^ y)
483483+ | Some x, None -> Some x
484484+ | None , Some y -> Some y
485485+ | None , None -> None
486486+487487+let assemble_records (version : tls_version) rs =
488488+ let version = match version with `TLS_1_3 -> `TLS_1_2 | x -> x in
489489+ String.concat "" (List.map (Writer.assemble_hdr version) rs)
490490+491491+(* main entry point *)
492492+let handle_tls state buf =
493493+ Tracing.cs ~tag:"wire-in" buf ;
494494+495495+ let rec handle_records st = function
496496+ | [] -> Ok (st, [], None)
497497+ | r::rs ->
498498+ let* st, raw_rs, data = handle_raw_record st r in
499499+ let* st', raw_rs', data' = handle_records st rs in
500500+ Ok (st', raw_rs @ raw_rs', maybe_app data data')
501501+ in
502502+ match
503503+ let* in_records, fragment = separate_records (state.fragment ^ buf) in
504504+ let* state', out_records, data = handle_records state in_records in
505505+ let version = state'.handshake.protocol_version in
506506+ let resp = match out_records with
507507+ | [] -> None
508508+ | _ ->
509509+ let out = assemble_records version out_records in
510510+ Tracing.cs ~tag:"wire-out" out ;
511511+ Some out
512512+ in
513513+ Ok ({ state' with fragment }, resp, data)
514514+ with
515515+ | Ok (state, resp, data) ->
516516+ let res =
517517+ if state.read_closed then begin
518518+ Tracing.debug (fun m -> m "eof-out") ;
519519+ Some `Eof
520520+ end else
521521+ None
522522+ in
523523+ (* Tracing.sexpf ~tag:"state-out" ~f:sexp_of_state state ; *)
524524+ Ok (state, res, `Response resp, `Data data)
525525+ | Error x ->
526526+ let version = state.handshake.protocol_version in
527527+ let level, alert = alert_of_failure x in
528528+ let record = Alert.make ~level alert in
529529+ let _, enc = encrypt_records state.encryptor version [record] in
530530+ let resp = assemble_records version enc in
531531+ Tracing.debug (fun m -> m "fail-alert-out %a" Packet.pp_alert (Packet.FATAL, alert)) ;
532532+ Tracing.debug (fun m -> m "failure %a" pp_failure x) ;
533533+ Error (x, `Response resp)
534534+535535+let send_records (st : state) records =
536536+ let version = st.handshake.protocol_version in
537537+ List.iter (fun f -> Tracing.debug (fun m -> m "frame-out %a" pp_frame f)) records ;
538538+ let (encryptor, encs) =
539539+ encrypt_records st.encryptor version records in
540540+ List.iter (fun f -> Tracing.debug (fun m -> m "record-out %a" pp_frame f)) encs ;
541541+ let data = assemble_records version encs in
542542+ Tracing.cs ~tag:"wire-out" data ;
543543+ ({ st with encryptor }, data)
544544+545545+let handshake_in_progress s = match s.handshake.machina with
546546+ | Client Established | Server Established -> false
547547+ | Client13 Established13 | Server13 Established13 -> false
548548+ | _ -> true
549549+550550+(* entry for user data *)
551551+let send_application_data st css =
552552+ if st.write_closed || not (hs_can_handle_appdata st.handshake) then
553553+ None
554554+ else begin
555555+ List.iter (fun cs -> Tracing.cs ~tag:"application-data-out" cs) css ;
556556+ let datas = match st.encryptor with
557557+ (* Mitigate implicit IV in CBC mode: prepend empty fragment *)
558558+ | Some { cipher_st = CBC { iv_mode = Iv _ ; _ } ; _ } -> "" :: css
559559+ | _ -> css
560560+ in
561561+ let ty = Packet.APPLICATION_DATA in
562562+ let data = List.map (fun cs -> (ty, cs)) datas in
563563+ Some (send_records st data)
564564+ end
565565+566566+let send_close_notify st =
567567+ let st = { st with write_closed = true } in
568568+ send_records st [Alert.close_notify]
569569+570570+let reneg ?authenticator ?acceptable_cas ?cert st =
571571+ if st.write_closed || st.read_closed then
572572+ (* this is a full handshake (with messages from both sides), thus if either
573573+ direction has closed the flow, the reneg won't succeed *)
574574+ None
575575+ else
576576+ let config = st.handshake.config in
577577+ let config = Option.fold ~none:config ~some:(Config.with_authenticator config) authenticator in
578578+ let config = Option.fold ~none:config ~some:(Config.with_acceptable_cas config) acceptable_cas in
579579+ let config = Option.fold ~none:config ~some:(Config.with_own_certificates config) cert in
580580+ let hs = { st.handshake with config } in
581581+ match hs.machina with
582582+ | Server Established ->
583583+ ( match Handshake_server.hello_request hs with
584584+ | Ok (handshake, [`Record hr]) -> Some (send_records { st with handshake } [hr])
585585+ | _ -> None )
586586+ | Client Established ->
587587+ ( match Handshake_client.answer_hello_request hs with
588588+ | Ok (handshake, [`Record ch]) -> Some (send_records { st with handshake } [ch])
589589+ | _ -> None )
590590+ | _ -> None
591591+592592+let key_update ?(request = true) state =
593593+ if state.write_closed then
594594+ Error (`Fatal (`Unexpected (`Message "write half already closed")))
595595+ else
596596+ let* state', out = Handshake_common.output_key_update ~request state in
597597+ let _, outbuf = send_records state [out] in
598598+ Ok (state', outbuf)
599599+600600+let client config =
601601+ let config = Config.of_client config in
602602+ let state = new_state config `Client in
603603+ let dch, _version, secrets = Handshake_client.default_client_hello config in
604604+ let ciphers, extensions = match config.Config.protocol_versions with
605605+ (* from RFC 5746 section 3.3:
606606+ Both the SSLv3 and TLS 1.0/TLS 1.1 specifications require
607607+ implementations to ignore data following the ClientHello (i.e.,
608608+ extensions) if they do not understand it. However, some SSLv3 and
609609+ TLS 1.0 implementations incorrectly fail the handshake in such a
610610+ case. This means that clients that offer the "renegotiation_info"
611611+ extension may encounter handshake failures. In order to enhance
612612+ compatibility with such servers, this document defines a second
613613+ signaling mechanism via a special Signaling Cipher Suite Value (SCSV)
614614+ "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", with code point {0x00, 0xFF}.
615615+ This SCSV is not a true cipher suite (it does not correspond to any
616616+ valid set of algorithms) and cannot be negotiated. Instead, it has
617617+ the same semantics as an empty "renegotiation_info" extension, as
618618+ described in the following sections. Because SSLv3 and TLS
619619+ implementations reliably ignore unknown cipher suites, the SCSV may
620620+ be safely sent to any server. *)
621621+ | (_, `TLS_1_0) -> ([Packet.TLS_EMPTY_RENEGOTIATION_INFO_SCSV], [])
622622+ | (`TLS_1_3, _) -> ([], [])
623623+ | _ -> ([], [`SecureRenegotiation ""])
624624+ in
625625+626626+ let client_hello =
627627+ { dch with
628628+ ciphersuites = dch.ciphersuites @ ciphers ;
629629+ extensions = dch.extensions @ extensions }
630630+ in
631631+632632+ let client_hello, ch, raw =
633633+ match config.Config.cached_ticket, config.Config.ticket_cache with
634634+ | None, _ | _, None ->
635635+ let ch = ClientHello client_hello in
636636+ client_hello, ch, Writer.assemble_handshake ch
637637+ | Some (psk, epoch), Some cache ->
638638+ let kex = `PskKeyExchangeModes [ Packet.PSK_KE_DHE ] in
639639+ (* what next!? *)
640640+ let now = cache.Config.timestamp () in
641641+ (* TODO check lifetime! *)
642642+ let obf_age =
643643+ let span = Ptime.Span.to_float_s (Ptime.diff now psk.issued_at) in
644644+ (* _in milliseconds_ *)
645645+ let ms = int_of_float (span *. 1000.) in
646646+ Int32.add psk.obfuscation (Int32.of_int ms)
647647+ in
648648+ let cipher = match Ciphersuite.ciphersuite_to_ciphersuite13 epoch.ciphersuite with
649649+ | None -> assert false
650650+ | Some c -> c
651651+ in
652652+ (* if all goes well, we can compute the binder key and embed into ch! *)
653653+ let early_secret = Handshake_crypto13.(derive (empty cipher) psk.secret) in
654654+ let binder_key = Handshake_crypto13.derive_secret early_secret "res binder" "" in
655655+656656+ let hash =
657657+ let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 cipher)) in
658658+ String.make H.digest_size '\x00'
659659+ in
660660+ let incomplete_psks = [ (psk.identifier, obf_age), hash ] in
661661+ let ch' = { client_hello with extensions = client_hello.extensions @ [ kex ; `PreSharedKeys incomplete_psks ] } in
662662+ let ch'_raw = Writer.assemble_handshake (ClientHello ch') in
663663+664664+ let binders_len = binders_len incomplete_psks in
665665+ let ch_part = String.(sub ch'_raw 0 (length ch'_raw - binders_len)) in
666666+ let binder = Handshake_crypto13.finished early_secret.hash binder_key ch_part in
667667+ let blen = String.length binder in
668668+ let prefix = Bytes.create 3 in
669669+ Bytes.set_uint16_be prefix 0 (blen + 1) ;
670670+ Bytes.set_uint8 prefix 2 blen ;
671671+ let raw = String.concat "" [ ch_part ; Bytes.unsafe_to_string prefix ; binder ] in
672672+673673+ let psks = [(psk.identifier, obf_age), binder] in
674674+ let client_hello' = { client_hello with extensions = client_hello.extensions @ [ kex ; `PreSharedKeys psks ] } in
675675+ let ch' = ClientHello client_hello' in
676676+ client_hello', ch', raw
677677+ in
678678+679679+ let machina = AwaitServerHello (client_hello, secrets, [raw]) in
680680+681681+ (* from RFC5246, appendix E.1
682682+ TLS clients that wish to negotiate with older servers MAY send any
683683+ value {03,XX} as the record layer version number. Typical values
684684+ would be {03,00}, the lowest version number supported by the client,
685685+ and the value of ClientHello.client_version. No single value will
686686+ guarantee interoperability with all old servers, but this is a
687687+ complex topic beyond the scope of this document. *)
688688+ let version = min_protocol_version Config.(config.protocol_versions) in
689689+ let handshake = {
690690+ state.handshake with
691691+ machina = Client machina ;
692692+ protocol_version = version
693693+ } in
694694+ let state = { state with handshake } in
695695+696696+ Tracing.hs ~tag:"handshake-out" ch ;
697697+ send_records state [(Packet.HANDSHAKE, raw)]
698698+699699+let server config = new_state Config.(of_server config) `Server
700700+701701+let epoch state =
702702+ Option.to_result ~none:() (epoch_of_hs state.handshake)
703703+704704+let export_key_material (e : epoch_data) ?context label length =
705705+ match e.protocol_version with
706706+ | `TLS_1_3 ->
707707+ let hash =
708708+ let cipher = Option.get (Ciphersuite.ciphersuite_to_ciphersuite13 e.ciphersuite) in
709709+ Ciphersuite.hash13 cipher
710710+ in
711711+ let module H = (val Digestif.module_of_hash' hash) in
712712+ let ems = e.exporter_master_secret in
713713+ let prk =
714714+ let ctx = H.(to_raw_string (digest_string "")) in
715715+ Handshake_crypto13.derive_secret_no_hash hash ems ~ctx label
716716+ in
717717+ let ctx = Option.value ~default:"" context in
718718+ Handshake_crypto13.derive_secret_no_hash
719719+ hash prk ~ctx:H.(to_raw_string (digest_string ctx))
720720+ ~length "exporter"
721721+ | #tls_before_13 as v ->
722722+ let seed =
723723+ let base =
724724+ match e.side with
725725+ | `Server -> e.peer_random ^ e.own_random
726726+ | `Client -> e.own_random ^ e.peer_random
727727+ in
728728+ match context with
729729+ | None -> base
730730+ | Some data ->
731731+ let len = Bytes.create 2 in
732732+ Bytes.set_uint16_be len 0 (String.length data);
733733+ String.concat "" [ base ; Bytes.unsafe_to_string len ; data ]
734734+ in
735735+ Handshake_crypto.pseudo_random_function v e.ciphersuite
736736+ length e.master_secret label seed
737737+738738+let channel_binding e = function
739739+ | `Tls_exporter ->
740740+ Ok (export_key_material e "EXPORTER-Channel-Binding" 32)
741741+ | `Tls_server_endpoint ->
742742+ let ( let* ) = Result.bind in
743743+ let* cert =
744744+ match e.side, e.peer_certificate, e.own_certificate with
745745+ | `Client, Some cert, _ -> Ok cert
746746+ | `Server, _, cert :: _ -> Ok cert
747747+ | `Client, _, _ -> Error (`Msg "no certificate received from the server")
748748+ | `Server, _, _ -> Error (`Msg "certificate not available")
749749+ in
750750+ let* sigalg =
751751+ Option.to_result ~none:(`Msg "unknown signature algorithm in certificate")
752752+ (Option.map snd (X509.Certificate.signature_algorithm cert))
753753+ in
754754+ let hash = match sigalg with `MD5 | `SHA1 -> `SHA256 | x -> x in
755755+ Ok (X509.Certificate.fingerprint hash cert)
756756+ | `Tls_unique ->
757757+ match e.protocol_version, e.tls_unique with
758758+ | `TLS_1_3, _ ->
759759+ Error (`Msg "tls-unique not defined for TLS 1.3")
760760+ | _, None -> Error (`Msg "couldn't find a tls-unique in the session data")
761761+ | _, Some data -> Ok data
+172
vendor/opam/tls/lib/engine.mli
···11+(** Transport layer security
22+33+ [TLS] is an implementation of
44+ {{:https://en.wikipedia.org/wiki/Transport_Layer_Security}transport
55+ layer security} in OCaml. TLS is a widely used security protocol
66+ which establishes an end-to-end secure channel (with optional
77+ (mutual) authentication) between two endpoints. It uses TCP/IP as
88+ transport. This library supports all four versions of TLS:
99+ {{:https://tools.ietf.org/html/rfc8446}1.3, RFC8446},
1010+ {{:https://tools.ietf.org/html/rfc5246}1.2, RFC5246},
1111+ {{:https://tools.ietf.org/html/rfc4346}1.1, RFC4346}, and
1212+ {{:https://tools.ietf.org/html/rfc2246}1.0, RFC2246}. SSL, the
1313+ previous protocol definition, is not supported.
1414+1515+ TLS is algorithmically agile: protocol version, key exchange
1616+ algorithm, symmetric cipher, and message authentication code are
1717+ negotiated upon connection.
1818+1919+ This library implements several extensions of TLS,
2020+ {{:https://tools.ietf.org/html/rfc3268}AES ciphers},
2121+ {{:https://tools.ietf.org/html/rfc4366}TLS extensions} (such as
2222+ server name indication, SNI),
2323+ {{:https://tools.ietf.org/html/rfc5746}Renegotiation extension},
2424+ {{:https://tools.ietf.org/html/rfc7627}Session Hash and Extended
2525+ Master Secret Extension}.
2626+2727+ This library does not contain insecure cipher suites (such as
2828+ single DES, export ciphers, ...). It does not expose the server
2929+ time in the server random, requires secure renegotiation.
3030+3131+ This library consists of a core, implemented in a purely
3232+ functional matter ({!Engine}, this module), and effectful parts:
3333+ {!Tls_lwt} and {!Tls_mirage}.
3434+3535+ {e %%VERSION%%} *)
3636+3737+3838+(** {1 Abstract state type} *)
3939+4040+(** The abstract type of a TLS state. *)
4141+type state
4242+4343+(** {1 Constructors} *)
4444+4545+(** [client client] is [tls * out] where [tls] is the initial state,
4646+ and [out] the initial client hello *)
4747+val client : Config.client -> (state * string)
4848+4949+(** [server server] is [tls] where [tls] is the initial server
5050+ state *)
5151+val server : Config.server -> state
5252+5353+(** {1 Protocol failures} *)
5454+5555+(** failures which can be mitigated by reconfiguration *)
5656+type error = [
5757+ | `AuthenticationFailure of X509.Validation.validation_error
5858+ | `NoConfiguredCiphersuite of Ciphersuite.ciphersuite list
5959+ | `NoConfiguredVersions of Core.tls_version list
6060+ | `NoConfiguredSignatureAlgorithm of Core.signature_algorithm list
6161+ | `NoMatchingCertificateFound of string
6262+ | `CouldntSelectCertificate
6363+]
6464+6565+(** failures from received garbage or lack of features *)
6666+type fatal = [
6767+ | `Protocol_version of [
6868+ | `None_supported of Core.tls_any_version list
6969+ | `Unknown_record of int * int
7070+ | `Bad_record of Core.tls_any_version
7171+ ]
7272+ | `Unexpected of [
7373+ | `Content_type of int
7474+ | `Message of string
7575+ | `Handshake of Core.tls_handshake
7676+ ]
7777+ | `Decode of string
7878+ | `Handshake of [
7979+ | `Message of string
8080+ | `Fragments
8181+ | `BadDH of string
8282+ | `BadECDH of Mirage_crypto_ec.error
8383+ ]
8484+ | `Bad_certificate of string
8585+ | `Missing_extension of string
8686+ | `Bad_mac
8787+ | `Record_overflow of int
8888+ | `Unsupported_extension
8989+ | `Inappropriate_fallback
9090+ | `No_application_protocol
9191+]
9292+9393+(** type of failures *)
9494+type failure = [
9595+ | `Error of error
9696+ | `Fatal of fatal
9797+ | `Alert of Packet.alert_type
9898+]
9999+100100+(** [alert_of_failure failure] is [alert], the TLS alert type for this failure. *)
101101+val alert_of_failure : failure -> Packet.alert_level * Packet.alert_type
102102+103103+(** [string_of_failure failure] is [string], the string representation of the [failure]. *)
104104+val string_of_failure : failure -> string
105105+106106+(** [pp_failure failure] pretty-prints failure. *)
107107+val pp_failure : failure Fmt.t
108108+109109+(** {1 Protocol handling} *)
110110+111111+(** result type of {!handle_tls}: either failed to handle the incoming
112112+ buffer ([`Fail]) with {!failure} and potentially a message to send
113113+ to the other endpoint, or sucessful operation ([`Ok]) with a new
114114+ {!state}, an end of file ([`Eof]), or an incoming ([`Alert]).
115115+ Possibly some [`Response] to the other endpoint is needed, and
116116+ potentially some [`Data] for the application was received. *)
117117+type ret =
118118+ (state * [ `Eof ] option
119119+ * [ `Response of string option ]
120120+ * [ `Data of string option ],
121121+ failure * [ `Response of string ]) result
122122+123123+(** [handle_tls state buffer] is [ret], depending on incoming [state]
124124+ and [buffer], the result is the appropriate {!ret} *)
125125+val handle_tls : state -> string -> ret
126126+127127+(** [handshake_in_progrss state] is a predicate which indicates whether there
128128+ is a handshake in progress or scheduled. *)
129129+val handshake_in_progress : state -> bool
130130+131131+(** [send_application_data tls outs] is [Some (tls', out)] where
132132+ [tls'] is the new tls state, and [out] the cstruct to send over the
133133+ wire (encrypted [outs]) when the TLS session is ready. When the TLS
134134+ session is not ready it is [None]. *)
135135+val send_application_data : state -> string list -> (state * string) option
136136+137137+(** [send_close_notify tls] is [tls' * out] where [tls'] is the new
138138+ tls state, and out the (possible encrypted) close notify alert. *)
139139+val send_close_notify : state -> state * string
140140+141141+(** [reneg ~authenticator ~acceptable_cas ~cert tls] initiates a renegotation on
142142+ [tls], using the provided [authenticator]. It is [tls' * out] where [tls']
143143+ is the new tls state, and [out] either a client hello or hello request
144144+ (depending on which communication endpoint [tls] is). *)
145145+val reneg : ?authenticator:X509.Authenticator.t ->
146146+ ?acceptable_cas:X509.Distinguished_name.t list -> ?cert:Config.own_cert ->
147147+ state -> (state * string) option
148148+149149+(** [key_update ~request state] initiates a KeyUpdate (TLS 1.3 only). If
150150+ [request] is provided and [true] (the default), the KeyUpdate message
151151+ contains a request that the peer should update their traffic key as well. *)
152152+val key_update : ?request:bool -> state -> (state * string, failure) result
153153+154154+(** {1 Session information} *)
155155+156156+(** [epoch state] is [epoch], which contains the session
157157+ information. If there's no established session yet, an error is returned. *)
158158+val epoch : state -> (Core.epoch_data, unit) result
159159+160160+(** [export_key_material epoch_data ?context label length] is the RFC 5705
161161+ exported key material of [length] bytes using [label] and, if provided,
162162+ [context]. *)
163163+val export_key_material : Core.epoch_data -> ?context:string -> string -> int ->
164164+ string
165165+166166+(** [channel_binding epoch_data mode] is the RFC 5929 and RFC 9266 specified
167167+ channel binding. Please note that [`Tls_unique] will error for TLS 1.3
168168+ sessions, and [`Tls_exporter] is not recommended for TLS < 1.3 sessions
169169+ (unless the uniqueness is ensured via another path). *)
170170+val channel_binding : Core.epoch_data ->
171171+ [ `Tls_exporter | `Tls_unique | `Tls_server_endpoint ] ->
172172+ (string, [ `Msg of string ]) result
vendor/opam/tls/lib/explorator.ml
This is a binary file and will not be displayed.
+544
vendor/opam/tls/lib/handshake_client.ml
···11+open Core
22+open State
33+open Handshake_common
44+open Config
55+66+let state_version state = match state.protocol_version with
77+ | #tls_before_13 as v -> v
88+ | _ -> assert false
99+1010+let default_client_hello config =
1111+ let host = match config.peer_name with
1212+ | None -> []
1313+ | Some x -> [`Hostname x]
1414+ in
1515+ let version = max_protocol_version config.protocol_versions in
1616+ let ecc_groups = match List.filter Config.elliptic_curve config.groups with
1717+ | [] -> []
1818+ | xs -> [ `ECPointFormats ; `SupportedGroups (List.map group_to_named_group xs) ]
1919+ in
2020+ let extensions, secrets = match version with
2121+ | `TLS_1_0 | `TLS_1_1 -> (ecc_groups, [])
2222+ | `TLS_1_2 ->
2323+ (`SignatureAlgorithms config.signature_algorithms :: ecc_groups, [])
2424+ | `TLS_1_3 ->
2525+ let sig_alg = config.signature_algorithms (* TODO: filter deprecated ones *)
2626+ and groups = List.map group_to_named_group config.groups
2727+ and secrets, keyshares =
2828+ (* OTOH, we could send all the keyshares (but this is pretty substantial size) *)
2929+ (* instead we pick the first two groups and send keyshares *)
3030+ let rec gen c gs acc = match c with
3131+ | 0 -> List.rev acc
3232+ | _ -> match gs with
3333+ | [] -> List.rev acc (* TODO log? complain? *)
3434+ | g::gs' ->
3535+ let priv, share = Handshake_crypto13.dh_gen_key g in
3636+ let acc' = ((g, priv),(group_to_named_group g, share)) :: acc in
3737+ gen (pred c) gs' acc'
3838+ in
3939+ List.split (gen 2 config.groups [])
4040+ in
4141+ let all = all_versions config.protocol_versions in
4242+ let supported_versions = List.map (fun x -> (x :> tls_any_version)) all in
4343+ let point_format =
4444+ if min_protocol_version config.protocol_versions = `TLS_1_3 then
4545+ []
4646+ else
4747+ [ `ECPointFormats ]
4848+ in
4949+ let exts =
5050+ point_format @ [`SignatureAlgorithms sig_alg ; `SupportedGroups groups ; `KeyShare keyshares ; `SupportedVersions supported_versions ]
5151+ in
5252+ (exts, secrets)
5353+ in
5454+ let alpn = match config.alpn_protocols with
5555+ | [] -> []
5656+ | protocols -> [`ALPN protocols]
5757+ in
5858+ let sessionid =
5959+ match config.use_reneg, config.cached_session with
6060+ | _, Some { session_id ; extended_ms ; _ } when extended_ms && not (String.length session_id = 0) -> Some session_id
6161+ | false, Some { session_id ; _ } when not (String.length session_id = 0) -> Some session_id
6262+ | _ -> None
6363+ in
6464+ let ch = {
6565+ client_version = (version :> tls_any_version) ;
6666+ client_random = Mirage_crypto_rng.generate 32 ;
6767+ sessionid = sessionid ;
6868+ ciphersuites = List.map Ciphersuite.ciphersuite_to_any_ciphersuite config.ciphers ;
6969+ extensions = `ExtendedMasterSecret :: host @ extensions @ alpn
7070+ }
7171+ in
7272+ (ch, version, secrets)
7373+7474+let common_server_hello_validation config reneg (sh : server_hello) (ch : client_hello) =
7575+ let validate_reneg data =
7676+ let err = `Fatal (`Handshake (`Message "invalid renegotiation")) in
7777+ match reneg, data with
7878+ | Some (cvd, svd), Some x -> guard (String.equal (cvd ^ svd) x) err
7979+ | Some _, None -> Error err
8080+ | None, Some x -> guard (String.length x = 0) err
8181+ | None, None -> Ok ()
8282+ in
8383+ let* () =
8484+ guard (List.mem sh.ciphersuite config.ciphers)
8585+ (`Error (`NoConfiguredCiphersuite [sh.ciphersuite]))
8686+ in
8787+ let* () =
8888+ guard (server_hello_valid sh &&
8989+ server_exts_subset_of_client sh.extensions ch.extensions)
9090+ (`Fatal `Unsupported_extension)
9191+ in
9292+ let* () =
9393+ match get_alpn_protocol sh with
9494+ | None -> Ok ()
9595+ | Some x ->
9696+ guard (List.mem x config.alpn_protocols) (`Fatal `Unsupported_extension)
9797+ in
9898+ validate_reneg (get_secure_renegotiation sh.extensions)
9999+100100+let common_server_hello_machina state (sh : server_hello) (ch : client_hello) raw log =
101101+ let cipher = sh.ciphersuite in
102102+ let session_id = Option.value ~default:"" sh.sessionid in
103103+ let extended_ms =
104104+ List.mem `ExtendedMasterSecret ch.extensions &&
105105+ List.mem `ExtendedMasterSecret sh.extensions
106106+ in
107107+ let alpn_protocol = get_alpn_protocol sh in
108108+ let session =
109109+ let session = empty_session in
110110+ let common_session_data = {
111111+ session.common_session_data with
112112+ client_random = ch.client_random ;
113113+ server_random = sh.server_random ;
114114+ alpn_protocol ;
115115+ } in {
116116+ session with
117117+ common_session_data ;
118118+ ciphersuite = cipher ;
119119+ session_id ;
120120+ extended_ms ;
121121+ client_version = ch.client_version ;
122122+ }
123123+ in
124124+ let state = { state with protocol_version = sh.server_version } in
125125+ match Ciphersuite.ciphersuite_kex cipher with
126126+ | #Ciphersuite.key_exchange_algorithm_dhe ->
127127+ let machina = Client (AwaitCertificate_DHE (session, log @ [raw])) in
128128+ Ok ({ state with machina }, [])
129129+ | `RSA ->
130130+ let machina = Client (AwaitCertificate_RSA (session, log @ [raw])) in
131131+ Ok ({ state with machina }, [])
132132+133133+let answer_server_hello state (ch : client_hello) sh secrets raw log =
134134+ let validate_version requested (lo, _) server_version =
135135+ guard (version_ge requested server_version && server_version >= lo)
136136+ (`Error (`NoConfiguredVersions [ server_version ]))
137137+ in
138138+139139+ let cfg = state.config in
140140+ let* () = common_server_hello_validation cfg None sh ch in
141141+ let* () = validate_version ch.client_version state.config.protocol_versions sh.server_version in
142142+143143+ let* () =
144144+ if max_protocol_version state.config.protocol_versions = `TLS_1_3 then
145145+ let* () =
146146+ guard (not (Utils.sub_equal ~off:24 ~len:8 Packet.downgrade12 sh.server_random))
147147+ (`Fatal (`Handshake (`Message "random contains downgrade TLS 1.2")))
148148+ in
149149+ guard (not (Utils.sub_equal ~off:24 ~len:8 Packet.downgrade11 sh.server_random))
150150+ (`Fatal (`Handshake (`Message "random contains downgrade TLS 1.1")))
151151+ else
152152+ Ok ()
153153+ in
154154+155155+ let epoch_matches (epoch : epoch_data) =
156156+ epoch.ciphersuite = sh.ciphersuite &&
157157+ epoch.protocol_version = sh.server_version &&
158158+ Option.fold ~none:false ~some:(SessionID.equal epoch.session_id) sh.sessionid &&
159159+ (not cfg.use_reneg ||
160160+ (List.mem `ExtendedMasterSecret sh.extensions && epoch.extended_ms))
161161+ in
162162+163163+ Tracing.debug (fun m -> m "version %a" pp_tls_version sh.server_version) ;
164164+ trace_cipher sh.ciphersuite ;
165165+166166+ let state = { state with protocol_version = sh.server_version } in
167167+ match sh.server_version with
168168+ | #tls13 ->
169169+ Handshake_client13.answer_server_hello state ch sh secrets raw (String.concat "" log)
170170+ | #tls_before_13 as v ->
171171+ match state.config.cached_session with
172172+ | Some epoch when epoch_matches epoch ->
173173+ let session =
174174+ let session = session_of_epoch epoch in
175175+ let common_session_data = {
176176+ session.common_session_data with
177177+ client_random = ch.client_random ;
178178+ server_random = sh.server_random ;
179179+ client_auth = match epoch.own_certificate with [] -> false | _ -> true ;
180180+ } in
181181+ { session with
182182+ common_session_data ;
183183+ client_version = ch.client_version ;
184184+ }
185185+ in
186186+ let client_ctx, server_ctx =
187187+ Handshake_crypto.initialise_crypto_ctx v session
188188+ in
189189+ let machina = AwaitServerChangeCipherSpecResume (session, client_ctx, server_ctx, log @ [raw]) in
190190+ Ok ({ state with machina = Client machina }, [])
191191+ | _ -> common_server_hello_machina state sh ch raw log
192192+193193+let answer_server_hello_renegotiate state session (ch : client_hello) sh raw log =
194194+ let* () = common_server_hello_validation state.config (Some session.renegotiation) sh ch in
195195+ let* () =
196196+ guard (state.protocol_version = sh.server_version)
197197+ (`Fatal (`Handshake (`Message "invalid renegotiation version")))
198198+ in
199199+ common_server_hello_machina state sh ch raw log
200200+201201+let validate_keyusage certificate kex =
202202+ let usage = Ciphersuite.required_usage kex in
203203+ let* cert =
204204+ Option.to_result ~none:(`Fatal (`Bad_certificate "none received")) certificate
205205+ in
206206+ let* () =
207207+ guard (supports_key_usage ~not_present:true usage cert)
208208+ (`Fatal (`Bad_certificate "key usage"))
209209+ in
210210+ guard
211211+ (supports_extended_key_usage `Server_auth cert ||
212212+ supports_extended_key_usage ~not_present:true `Any cert)
213213+ (`Fatal (`Bad_certificate "extended key usage"))
214214+215215+let answer_certificate_RSA state (session : session_data) cs raw log =
216216+ let cfg = state.config in
217217+ let* peer_certificate, received_certificates, peer_certificate_chain, trust_anchor =
218218+ validate_chain cfg.authenticator cs cfg.ip cfg.peer_name
219219+ in
220220+ let* () = validate_keyusage peer_certificate `RSA in
221221+ let session =
222222+ let common_session_data = { session.common_session_data with received_certificates ; peer_certificate ; peer_certificate_chain ; trust_anchor } in
223223+ { session with common_session_data }
224224+ in
225225+ let* version =
226226+ match session.client_version with
227227+ | `TLS_1_3 -> Ok `TLS_1_2
228228+ | #tls_before_13 as v -> Ok v
229229+ | _ -> assert false
230230+ in
231231+ let buf = Bytes.create (2 + 46) in
232232+ let _ver = Writer.assemble_protocol_version ~buf version in
233233+ Mirage_crypto_rng.generate_into buf ~off:2 46;
234234+ let premaster = Bytes.unsafe_to_string buf in
235235+ let* k = peer_key peer_certificate in
236236+ match k with
237237+ | `RSA key ->
238238+ let kex = Mirage_crypto_pk.Rsa.PKCS1.encrypt ~key premaster in
239239+ let kex = Writer.assemble_client_dh_key_exchange kex in
240240+ let machina =
241241+ AwaitCertificateRequestOrServerHelloDone
242242+ (session, kex, premaster, log @ [raw])
243243+ in
244244+ Ok ({ state with machina = Client machina }, [])
245245+ | _ -> Error (`Fatal (`Bad_certificate "not an RSA certificate"))
246246+247247+let answer_certificate_DHE state (session : session_data) cs raw log =
248248+ let cfg = state.config in
249249+ let* peer_certificate, received_certificates, peer_certificate_chain, trust_anchor =
250250+ validate_chain cfg.authenticator cs cfg.ip cfg.peer_name
251251+ in
252252+ let* () = validate_keyusage peer_certificate `FFDHE in
253253+ let session =
254254+ let common_session_data = { session.common_session_data with received_certificates ; peer_certificate ; peer_certificate_chain ; trust_anchor } in
255255+ { session with common_session_data }
256256+ in
257257+ let machina = AwaitServerKeyExchange_DHE (session, log @ [raw]) in
258258+ Ok ({ state with machina = Client machina }, [])
259259+260260+let answer_server_key_exchange_DHE state (session : session_data) kex raw log =
261261+ let* group, shared, raw_dh_params, leftover =
262262+ if Ciphersuite.ecdhe session.ciphersuite then
263263+ let* g, share, raw, left =
264264+ map_reader_error (Reader.parse_ec_parameters kex)
265265+ in
266266+ Ok (`Ec g, share, raw, left)
267267+ else
268268+ let unpack_dh dh_params =
269269+ Result.map_error
270270+ (function `Msg m -> `Fatal (`Decode m))
271271+ (Crypto.dh_params_unpack dh_params)
272272+ in
273273+ let* dh_params, raw_dh_params, leftover =
274274+ map_reader_error (Reader.parse_dh_parameters kex)
275275+ in
276276+ let* group, shared = unpack_dh dh_params in
277277+ let* () =
278278+ guard (Mirage_crypto_pk.Dh.modulus_size group >= Config.min_dh_size)
279279+ (`Fatal (`Handshake (`BadDH "too small")))
280280+ in
281281+ Ok (`Finite_field group, shared, raw_dh_params, leftover)
282282+ in
283283+284284+ let sigdata =
285285+ String.concat "" [
286286+ session.common_session_data.client_random ;
287287+ session.common_session_data.server_random ;
288288+ raw_dh_params
289289+ ]
290290+ in
291291+ let* () =
292292+ verify_digitally_signed state.protocol_version
293293+ state.config.signature_algorithms leftover sigdata
294294+ session.common_session_data.peer_certificate
295295+ in
296296+297297+ let* pms, kex =
298298+ let open Mirage_crypto_ec in
299299+ let map_ecdh_error =
300300+ Result.map_error (fun e -> `Fatal (`Handshake (`BadECDH e)))
301301+ in
302302+ match group with
303303+ | `Finite_field g ->
304304+ let secret, client_share = Mirage_crypto_pk.Dh.gen_key g in
305305+ let* pms =
306306+ Option.to_result
307307+ ~none:(`Fatal (`Handshake (`BadDH "invalid FF")))
308308+ (Mirage_crypto_pk.Dh.shared secret shared)
309309+ in
310310+ Ok (pms, Writer.assemble_client_dh_key_exchange client_share)
311311+ | `Ec `P256 ->
312312+ let secret, client_share = P256.Dh.gen_key () in
313313+ let* pms = map_ecdh_error (P256.Dh.key_exchange secret shared) in
314314+ Ok (pms, Writer.assemble_client_ec_key_exchange client_share)
315315+ | `Ec `P384 ->
316316+ let secret, client_share = P384.Dh.gen_key () in
317317+ let* pms = map_ecdh_error (P384.Dh.key_exchange secret shared) in
318318+ Ok (pms, Writer.assemble_client_ec_key_exchange client_share)
319319+ | `Ec `P521 ->
320320+ let secret, client_share = P521.Dh.gen_key () in
321321+ let* pms = map_ecdh_error (P521.Dh.key_exchange secret shared) in
322322+ Ok (pms, Writer.assemble_client_ec_key_exchange client_share)
323323+ | `Ec `X25519 ->
324324+ let secret, client_share = X25519.gen_key () in
325325+ let* pms = map_ecdh_error (X25519.key_exchange secret shared) in
326326+ Ok (pms, Writer.assemble_client_ec_key_exchange client_share)
327327+ in
328328+ let machina =
329329+ AwaitCertificateRequestOrServerHelloDone
330330+ (session, kex, pms, log @ [raw])
331331+ in
332332+ Ok ({ state with machina = Client machina }, [])
333333+334334+let answer_certificate_request state (session : session_data) cr kex pms raw log =
335335+ let cfg = state.config in
336336+ let* _types, sigalgs, _cas =
337337+ match state_version state with
338338+ | `TLS_1_0 | `TLS_1_1 ->
339339+ let* types, cas =
340340+ map_reader_error (Reader.parse_certificate_request cr)
341341+ in
342342+ Ok (types, None, cas)
343343+ | `TLS_1_2 ->
344344+ let* types, sigalgs, cas =
345345+ map_reader_error (Reader.parse_certificate_request_1_2 cr)
346346+ in
347347+ Ok (types, Some sigalgs, cas)
348348+ in
349349+ (* TODO: respect _types and _cas, multiple client certificates *)
350350+ let own_certificate, own_private_key =
351351+ match cfg.own_certificates with
352352+ | `Single (chain, priv) -> (chain, Some priv)
353353+ | _ -> ([], None)
354354+ in
355355+ let session =
356356+ let common_session_data = {
357357+ session.common_session_data with
358358+ own_certificate ;
359359+ own_private_key ;
360360+ client_auth = true
361361+ } in
362362+ { session with common_session_data }
363363+ in
364364+ let machina = AwaitServerHelloDone (session, sigalgs, kex, pms, log @ [raw]) in
365365+ Ok ({ state with machina = Client machina }, [])
366366+367367+let answer_server_hello_done state (session : session_data) sigalgs kex premaster raw log =
368368+ let kex = ClientKeyExchange kex in
369369+ let ckex = Writer.assemble_handshake kex in
370370+371371+ let* msgs, raw_msgs, raws, cert_verify =
372372+ match session.common_session_data.client_auth, session.common_session_data.own_private_key with
373373+ | true, Some p ->
374374+ let cs = List.map X509.Certificate.encode_der session.common_session_data.own_certificate in
375375+ let cert = Certificate (Writer.assemble_certificates cs) in
376376+ let ccert = Writer.assemble_handshake cert in
377377+ let to_sign = log @ [ raw ; ccert ; ckex ] in
378378+ let data = String.concat "" to_sign in
379379+ let ver = state.protocol_version
380380+ and my_sigalgs = state.config.signature_algorithms in
381381+ let* signature = signature ver data sigalgs my_sigalgs p in
382382+ let cert_verify = CertificateVerify signature in
383383+ let ccert_verify = Writer.assemble_handshake cert_verify in
384384+ Ok ([ cert ; kex ; cert_verify ],
385385+ [ ccert ; ckex ; ccert_verify ],
386386+ to_sign, Some ccert_verify)
387387+ | true, None ->
388388+ let cert = Certificate (Writer.assemble_certificates []) in
389389+ let ccert = Writer.assemble_handshake cert in
390390+ Ok ([cert ; kex], [ccert ; ckex], log @ [ raw ; ccert ; ckex ], None)
391391+ | false, _ ->
392392+ Ok ([kex], [ckex], log @ [ raw ; ckex ], None)
393393+ in
394394+395395+ let to_fin = raws @ Option.to_list cert_verify in
396396+397397+ let master_secret =
398398+ Handshake_crypto.derive_master_secret (state_version state) session premaster raws
399399+ in
400400+ let session =
401401+ let common_session_data = { session.common_session_data with master_secret } in
402402+ { session with common_session_data }
403403+ in
404404+ let client_ctx, server_ctx =
405405+ Handshake_crypto.initialise_crypto_ctx (state_version state) session
406406+ in
407407+408408+ let checksum = Handshake_crypto.finished (state_version state) session.ciphersuite master_secret "client finished" to_fin in
409409+ let fin = Finished checksum in
410410+ let raw_fin = Writer.assemble_handshake fin in
411411+ let session = { session with tls_unique = checksum } in
412412+ let ps = to_fin @ [raw_fin] in
413413+414414+ let session =
415415+ let common_session_data = { session.common_session_data with master_secret } in
416416+ { session with common_session_data }
417417+ in
418418+ let machina = AwaitServerChangeCipherSpec (session, server_ctx, checksum, ps)
419419+ and ccst, ccs = change_cipher_spec in
420420+421421+ List.iter (Tracing.hs ~tag:"handshake-out") msgs;
422422+ Tracing.cs ~tag:"change-cipher-spec-out" ccs ;
423423+ Tracing.cs ~tag:"master-secret" master_secret;
424424+ Tracing.hs ~tag:"handshake-out" fin;
425425+426426+ Ok ({ state with machina = Client machina },
427427+ List.map (fun x -> `Record (Packet.HANDSHAKE, x)) raw_msgs @
428428+ [ `Record (ccst, ccs);
429429+ `Change_enc client_ctx;
430430+ `Record (Packet.HANDSHAKE, raw_fin)])
431431+432432+let answer_server_finished state (session : session_data) client_verify fin log =
433433+ let computed =
434434+ Handshake_crypto.finished (state_version state) session.ciphersuite session.common_session_data.master_secret "server finished" log
435435+ in
436436+ let* () =
437437+ guard (String.equal computed fin)
438438+ (`Fatal (`Handshake (`Message "couldn't verify finished")))
439439+ in
440440+ let* () =
441441+ guard (String.length state.hs_fragment = 0) (`Fatal (`Handshake `Fragments))
442442+ in
443443+ let machina = Established
444444+ and session = { session with renegotiation = (client_verify, computed) } in
445445+ Ok ({ state with machina = Client machina ; session = `TLS session :: state.session }, [])
446446+447447+let answer_server_finished_resume state (session : session_data) fin raw log =
448448+ let client, server =
449449+ let checksum = Handshake_crypto.finished (state_version state) session.ciphersuite session.common_session_data.master_secret in
450450+ (checksum "client finished" (log @ [raw]), checksum "server finished" log)
451451+ in
452452+ let* () =
453453+ guard (String.equal server fin)
454454+ (`Fatal (`Handshake (`Message "couldn't verify finished")))
455455+ in
456456+ let session = { session with tls_unique = server } in
457457+ let* () =
458458+ guard (String.length state.hs_fragment = 0)
459459+ (`Fatal (`Handshake `Fragments))
460460+ in
461461+ let machina = Established
462462+ and session = { session with renegotiation = (client, server) }
463463+ in
464464+ let finished = Finished client in
465465+ let raw_finished = Writer.assemble_handshake finished in
466466+ Tracing.hs ~tag:"handshake-out" finished ;
467467+ Ok ({ state with machina = Client machina ; session = `TLS session :: state.session },
468468+ [`Record (Packet.HANDSHAKE, raw_finished)])
469469+470470+let answer_hello_request state =
471471+ let produce_client_hello session config exts =
472472+ let dch, _, _ = default_client_hello config in
473473+ let ch = { dch with extensions = dch.extensions @ exts ; sessionid = None } in
474474+ let raw = Writer.assemble_handshake (ClientHello ch) in
475475+ let machina = AwaitServerHelloRenegotiate (session, ch, [raw]) in
476476+ Tracing.hs ~tag:"handshake-out" (ClientHello ch) ;
477477+ ({ state with machina = Client machina }, [`Record (Packet.HANDSHAKE, raw)])
478478+ in
479479+480480+ match state.config.use_reneg, state.session with
481481+ | true , `TLS x :: _ ->
482482+ let ext = `SecureRenegotiation (fst x.renegotiation) in
483483+ Ok (produce_client_hello x state.config [ext])
484484+ | true , _ -> Error (`Fatal (`Handshake (`Message "couldn't find session")))
485485+ | false, _ ->
486486+ let no_reneg = Writer.assemble_alert ~level:Packet.WARNING Packet.NO_RENEGOTIATION in
487487+ Tracing.debug (fun m -> m "alert-out (warning, no_renegotiation)") ;
488488+ Ok (state, [`Record (Packet.ALERT, no_reneg)])
489489+490490+let handle_change_cipher_spec cs state packet =
491491+ let* () = map_reader_error (Reader.parse_change_cipher_spec packet) in
492492+ match cs with
493493+ | AwaitServerChangeCipherSpec (session, server_ctx, client_verify, log) ->
494494+ let* () =
495495+ guard (String.length state.hs_fragment = 0)
496496+ (`Fatal (`Handshake `Fragments))
497497+ in
498498+ let machina = AwaitServerFinished (session, client_verify, log) in
499499+ Tracing.cs ~tag:"change-cipher-spec-in" packet ;
500500+ Ok ({ state with machina = Client machina }, [`Change_dec server_ctx])
501501+ | AwaitServerChangeCipherSpecResume (session, client_ctx, server_ctx, log) ->
502502+ let* () =
503503+ guard (String.length state.hs_fragment = 0)
504504+ (`Fatal (`Handshake `Fragments))
505505+ in
506506+ let ccs = change_cipher_spec in
507507+ let machina = AwaitServerFinishedResume (session, log) in
508508+ Tracing.cs ~tag:"change-cipher-spec-in" packet ;
509509+ Tracing.cs ~tag:"change-cipher-spec-out" packet ;
510510+ Ok ({ state with machina = Client machina },
511511+ [`Record ccs ; `Change_enc client_ctx; `Change_dec server_ctx])
512512+ | _ -> Error (`Fatal (`Unexpected (`Message "change cipher spec")))
513513+514514+let handle_handshake cs hs buf =
515515+ let* handshake = map_reader_error (Reader.parse_handshake buf) in
516516+ Tracing.hs ~tag:"handshake-in" handshake ;
517517+ match cs, handshake with
518518+ | AwaitServerHello (ch, secrets, log), ServerHello sh ->
519519+ answer_server_hello hs ch sh secrets buf log
520520+ | AwaitServerHello (ch, secrets, log), HelloRetryRequest hrr ->
521521+ Handshake_client13.answer_hello_retry_request hs ch hrr secrets buf (String.concat "" log)
522522+ | AwaitServerHelloRenegotiate (session, ch, log), ServerHello sh ->
523523+ answer_server_hello_renegotiate hs session ch sh buf log
524524+ | AwaitCertificate_RSA (session, log), Certificate cs ->
525525+ let* cs = map_reader_error (Reader.parse_certificates cs) in
526526+ answer_certificate_RSA hs session cs buf log
527527+ | AwaitCertificate_DHE (session, log), Certificate cs ->
528528+ let* cs = map_reader_error (Reader.parse_certificates cs) in
529529+ answer_certificate_DHE hs session cs buf log
530530+ | AwaitServerKeyExchange_DHE (session, log), ServerKeyExchange kex ->
531531+ answer_server_key_exchange_DHE hs session kex buf log
532532+ | AwaitCertificateRequestOrServerHelloDone (session, kex, pms, log), CertificateRequest cr ->
533533+ answer_certificate_request hs session cr kex pms buf log
534534+ | AwaitCertificateRequestOrServerHelloDone (session, kex, pms, log), ServerHelloDone ->
535535+ answer_server_hello_done hs session None kex pms buf log
536536+ | AwaitServerHelloDone (session, sigalgs, kex, pms, log), ServerHelloDone ->
537537+ answer_server_hello_done hs session sigalgs kex pms buf log
538538+ | AwaitServerFinished (session, client_verify, log), Finished fin ->
539539+ answer_server_finished hs session client_verify fin log
540540+ | AwaitServerFinishedResume (session, log), Finished fin ->
541541+ answer_server_finished_resume hs session fin buf log
542542+ | Established, HelloRequest ->
543543+ answer_hello_request hs
544544+ | _, hs -> Error (`Fatal (`Unexpected (`Handshake hs)))
···11+open State
22+open Core
33+open Handshake_common
44+open Config
55+66+let answer_server_hello state ch (sh : server_hello) secrets raw log =
77+ (* assume SH valid, version 1.3, extensions are subset *)
88+ match Ciphersuite.ciphersuite_to_ciphersuite13 sh.ciphersuite with
99+ | None -> Error (`Fatal (`Handshake (`Message "not a TLS 1.3 ciphersuite")))
1010+ | Some cipher ->
1111+ let* () =
1212+ guard (List.mem cipher (ciphers13 state.config))
1313+ (`Fatal (`Handshake (`Message "not a configured ciphersuite")))
1414+ in
1515+ let* () = guard (String.length state.hs_fragment = 0) (`Fatal (`Handshake `Fragments)) in
1616+1717+ (* TODO: PSK *)
1818+ (* TODO: early_secret elsewhere *)
1919+ match Utils.map_find ~f:(function `KeyShare ks -> Some ks | _ -> None) sh.extensions with
2020+ | None -> Error (`Fatal (`Handshake (`Message "missing key share extension")))
2121+ | Some (g, share) ->
2222+ match List.find_opt (fun (g', _) -> g = g') secrets with
2323+ | None -> Error (`Fatal (`Handshake (`Message "couldn't find our secret for the key share")))
2424+ | Some (_, secret) ->
2525+ let* shared = Handshake_crypto13.dh_shared secret share in
2626+ let hlen =
2727+ let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 cipher)) in
2828+ H.digest_size
2929+ in
3030+ let* psk, resumed =
3131+ match
3232+ Utils.map_find ~f:(function `PreSharedKey idx -> Some idx | _ -> None) sh.extensions,
3333+ state.config.Config.cached_ticket
3434+ with
3535+ | None, _ | _, None -> Ok (String.make hlen '\x00', false)
3636+ | Some idx, Some (psk, _epoch) ->
3737+ let* () = guard (idx = 0) (`Fatal (`Handshake (`Message "resumed pre-shared idx not 0"))) in
3838+ Ok (psk.secret, true)
3939+ in
4040+ let early_secret = Handshake_crypto13.(derive (empty cipher) psk) in
4141+ let hs_secret = Handshake_crypto13.derive early_secret shared in
4242+ let log = log ^ raw in
4343+ let server_hs_secret, server_ctx, client_hs_secret, client_ctx =
4444+ Handshake_crypto13.hs_ctx hs_secret log in
4545+ let master_secret =
4646+ Handshake_crypto13.derive hs_secret (String.make hlen '\x00')
4747+ in
4848+ let session =
4949+ let base = empty_session13 cipher in
5050+ let common_session_data13 =
5151+ { base.common_session_data13 with
5252+ server_random = sh.server_random ;
5353+ client_random = ch.client_random ;
5454+ master_secret = master_secret.secret }
5555+ in
5656+ { base with master_secret ; common_session_data13 ; resumed }
5757+ in
5858+ let st = AwaitServerEncryptedExtensions13 (session, server_hs_secret, client_hs_secret, log) in
5959+ Ok ({ state with machina = Client13 st ; protocol_version = `TLS_1_3 },
6060+ [ `Change_enc client_ctx ; `Change_dec server_ctx ])
6161+6262+(* called from handshake_client.ml *)
6363+let answer_hello_retry_request state (ch : client_hello) hrr _secrets raw log =
6464+ (* when is a HRR invalid / what do we need to check?
6565+ -> we advertised the group and cipher
6666+ -> TODO we did advertise such a keyshare already (does it matter?)
6767+ *)
6868+ let* () =
6969+ guard (`TLS_1_3 = hrr.retry_version)
7070+ (`Fatal (`Handshake (`Message "hello retry request with a version <> 1.3")))
7171+ in
7272+ let* () =
7373+ guard (List.mem hrr.selected_group state.config.groups)
7474+ (`Fatal (`Handshake (`Message "hello retry request with group we didn't advertise")))
7575+ in
7676+ let* () =
7777+ guard (List.mem hrr.ciphersuite (ciphers13 state.config))
7878+ (`Fatal (`Handshake (`Message "hello retet request with ciphersuite we didn't advertise"))) in
7979+ (* generate a fresh keyshare *)
8080+ let secret, keyshare =
8181+ let g = hrr.selected_group in
8282+ let priv, share = Handshake_crypto13.dh_gen_key g in
8383+ (g, priv), (group_to_named_group g, share)
8484+ in
8585+ (* append server extensions (i.e. cookie!) *)
8686+ let cookie = match Utils.map_find ~f:(function `Cookie c -> Some c | _ -> None) hrr.extensions with
8787+ | None -> []
8888+ | Some c -> [ `Cookie c ]
8989+ in
9090+ (* use the same extensions as in original CH, apart from PSK!? and early_data *)
9191+ let other_exts = List.filter (function `KeyShare _ -> false | _ -> true) ch.extensions in
9292+ let new_ch = { ch with extensions = `KeyShare [keyshare] :: other_exts @ cookie} in
9393+ let new_ch_raw = Writer.assemble_handshake (ClientHello new_ch) in
9494+ let ch0_data =
9595+ let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 hrr.ciphersuite)) in
9696+ H.(to_raw_string (digest_string log))
9797+ in
9898+ let ch0_hdr = Writer.assemble_message_hash (String.length ch0_data) in
9999+ let st = AwaitServerHello13 (new_ch, [secret], String.concat "" [ ch0_hdr ; ch0_data ; raw ; new_ch_raw ]) in
100100+101101+ Tracing.hs ~tag:"handshake-out" (ClientHello new_ch);
102102+ Ok ({ state with machina = Client13 st ; protocol_version = `TLS_1_3 }, [`Record (Packet.HANDSHAKE, new_ch_raw)])
103103+104104+let answer_encrypted_extensions state (session : session_data13) server_hs_secret client_hs_secret ee raw log =
105105+ (* TODO we now know: - hostname - early_data (preserve this in session!!) *)
106106+ (* next message is either CertificateRequest or Certificate (or finished if PSK) *)
107107+ let alpn_protocol = Utils.map_find ~f:(function `ALPN proto -> Some proto | _ -> None) ee in
108108+ let session =
109109+ let common_session_data13 = { session.common_session_data13 with alpn_protocol } in
110110+ { session with common_session_data13 }
111111+ in
112112+ let st =
113113+ if session.resumed then
114114+ AwaitServerFinished13 (session, server_hs_secret, client_hs_secret, None, log ^ raw)
115115+ else
116116+ AwaitServerCertificateRequestOrCertificate13 (session, server_hs_secret, client_hs_secret, log ^ raw)
117117+ in
118118+ Ok ({ state with machina = Client13 st }, [])
119119+120120+let answer_certificate state (session : session_data13) server_hs_secret client_hs_secret sigalgs certs raw log =
121121+ (* certificates are (cs, ext) list - ext being statusrequest or signed_cert_timestamp *)
122122+ let certs = List.map fst certs in
123123+ let* peer_certificate, received_certificates, peer_certificate_chain, trust_anchor =
124124+ validate_chain state.config.authenticator certs state.config.ip state.config.peer_name
125125+ in
126126+ let session =
127127+ let common_session_data13 = {
128128+ session.common_session_data13 with
129129+ received_certificates ; peer_certificate_chain ; peer_certificate ; trust_anchor
130130+ } in
131131+ { session with common_session_data13 }
132132+ in
133133+ let st = AwaitServerCertificateVerify13 (session, server_hs_secret, client_hs_secret, sigalgs, log ^ raw) in
134134+ Ok ({ state with machina = Client13 st }, [])
135135+136136+let answer_certificate_verify (state : handshake_state) (session : session_data13) server_hs_secret client_hs_secret sigalgs cv raw log =
137137+ let tbs =
138138+ let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 session.ciphersuite13)) in
139139+ H.(to_raw_string (digest_string log))
140140+ in
141141+ let* () =
142142+ verify_digitally_signed state.protocol_version
143143+ ~context_string:"TLS 1.3, server CertificateVerify"
144144+ state.config.signature_algorithms cv tbs
145145+ session.common_session_data13.peer_certificate
146146+ in
147147+ let st = AwaitServerFinished13 (session, server_hs_secret, client_hs_secret, sigalgs, log ^ raw) in
148148+ Ok ({ state with machina = Client13 st }, [])
149149+150150+let answer_certificate_request (state : handshake_state) (session : session_data13) server_hs_secret client_hs_secret extensions raw log =
151151+ (* TODO respect extensions (CA, OIDfilter)! *)
152152+ let session =
153153+ let common_session_data13 = { session.common_session_data13 with client_auth = true } in
154154+ { session with common_session_data13 }
155155+ in
156156+ let sigalgs = Utils.map_find ~f:(function `SignatureAlgorithms s -> Some s | _ -> None) extensions in
157157+ let st = AwaitServerCertificate13 (session, server_hs_secret, client_hs_secret, sigalgs, log ^ raw) in
158158+ Ok ({ state with machina = Client13 st }, [])
159159+160160+let answer_finished state (session : session_data13) server_hs_secret client_hs_secret sigalgs fin raw log =
161161+ let hash = Ciphersuite.hash13 session.ciphersuite13 in
162162+ let f_data = Handshake_crypto13.finished hash server_hs_secret log in
163163+ let* () = guard (String.equal fin f_data) (`Fatal (`Handshake (`Message "couldn't verify finished"))) in
164164+ let* () = guard (String.length state.hs_fragment = 0) (`Fatal (`Handshake `Fragments)) in
165165+ let log = log ^ raw in
166166+ let server_app_secret, server_app_ctx, client_app_secret, client_app_ctx =
167167+ Handshake_crypto13.app_ctx session.master_secret log
168168+ in
169169+ let exporter_master_secret = Handshake_crypto13.exporter session.master_secret log in
170170+171171+ let* c_cv, log =
172172+ if session.common_session_data13.client_auth then
173173+ let own_certificate, own_private_key =
174174+ match state.config.Config.own_certificates with
175175+ | `Single (chain, priv) -> (chain, Some priv)
176176+ | _ -> ([], None)
177177+ in
178178+ let certificate =
179179+ let cs = List.map X509.Certificate.encode_der own_certificate in
180180+ Certificate (Writer.assemble_certificates_1_3 "" cs)
181181+ in
182182+ let cert_raw = Writer.assemble_handshake certificate in
183183+ Tracing.hs ~tag:"handshake-out" certificate ;
184184+ let log = log ^ cert_raw in
185185+ match own_private_key with
186186+ | None ->
187187+ Ok ([cert_raw], log)
188188+ | Some priv ->
189189+ let tbs =
190190+ let module H = (val Digestif.module_of_hash' hash) in
191191+ H.(to_raw_string (digest_string log))
192192+ in
193193+ let* signed =
194194+ signature `TLS_1_3 ~context_string:"TLS 1.3, client CertificateVerify"
195195+ tbs sigalgs state.config.Config.signature_algorithms priv
196196+ in
197197+ let cv = CertificateVerify signed in
198198+ Tracing.hs ~tag:"handshake-out" cv ;
199199+ let cv_raw = Writer.assemble_handshake cv in
200200+ Ok ([ cert_raw ; cv_raw ], log ^ cv_raw)
201201+ else
202202+ Ok ([], log)
203203+ in
204204+205205+ let myfin = Handshake_crypto13.finished hash client_hs_secret log in
206206+ let mfin = Writer.assemble_handshake (Finished myfin) in
207207+208208+ let resumption_secret = Handshake_crypto13.resumption session.master_secret (log ^ mfin) in
209209+ let session = { session with resumption_secret ; exporter_master_secret ; client_app_secret ; server_app_secret } in
210210+ let machina = Client13 Established13 in
211211+212212+ Tracing.hs ~tag:"handshake-out" (Finished myfin);
213213+214214+ Ok ({ state with machina ; session = `TLS13 session :: state.session },
215215+ List.map (fun data -> `Record (Packet.HANDSHAKE, data)) c_cv @
216216+ [ `Record (Packet.HANDSHAKE, mfin) ;
217217+ `Change_dec server_app_ctx ; `Change_enc client_app_ctx ])
218218+219219+let answer_session_ticket state st =
220220+ (match state.config.ticket_cache with
221221+ | None -> ()
222222+ | Some cache ->
223223+ (* looks like we'll need the resumption secret in the state (we can compute once finished is done)! *)
224224+ match state.session with
225225+ | `TLS13 session :: _ ->
226226+ let epoch = epoch_of_session false state.config.Config.peer_name `TLS_1_3 (`TLS13 session) in
227227+ let secret = Handshake_crypto13.res_secret
228228+ (Ciphersuite.hash13 session.ciphersuite13)
229229+ session.resumption_secret st.nonce
230230+ in
231231+ let issued_at = cache.timestamp () in
232232+ let early_data = match Utils.map_find ~f:(function `EarlyDataIndication x -> Some x | _ -> None) st.extensions with
233233+ | None -> 0l
234234+ | Some x -> x
235235+ in
236236+ let psk = { identifier = st.ticket ; obfuscation = st.age_add ; secret ; lifetime = st.lifetime ; early_data ; issued_at } in
237237+ cache.ticket_granted psk epoch
238238+ | _ -> ());
239239+ Ok (state, [])
240240+241241+let handle_key_update state req =
242242+ match state.session with
243243+ | `TLS13 session :: _ ->
244244+ let* () = guard (String.length state.hs_fragment = 0) (`Fatal (`Handshake `Fragments)) in
245245+ let server_app_secret, server_ctx =
246246+ Handshake_crypto13.app_secret_n_1 session.master_secret session.server_app_secret
247247+ in
248248+ let session' = { session with server_app_secret } in
249249+ let session', out = match req with
250250+ | Packet.UPDATE_NOT_REQUESTED -> session', []
251251+ | Packet.UPDATE_REQUESTED ->
252252+ let client_app_secret, client_ctx =
253253+ Handshake_crypto13.app_secret_n_1 session.master_secret session.client_app_secret
254254+ in
255255+ let ku = KeyUpdate Packet.UPDATE_NOT_REQUESTED in
256256+ Tracing.hs ~tag:"handshake-out" ku ;
257257+ let ku_raw = Writer.assemble_handshake ku in
258258+ { session' with client_app_secret },
259259+ [ `Record (Packet.HANDSHAKE, ku_raw); `Change_enc client_ctx ]
260260+ in
261261+ let session = `TLS13 session' :: state.session in
262262+ let state' = { state with machina = Server13 Established13 ; session } in
263263+ Ok (state', `Change_dec server_ctx :: out)
264264+ | _ -> Error (`Fatal (`Handshake (`Message "couldn't find an earlier session")))
265265+266266+let handle_handshake cs hs buf =
267267+ let open Reader in
268268+ let* handshake = map_reader_error (parse_handshake buf) in
269269+ Tracing.hs ~tag:"handshake-in" handshake;
270270+ match cs, handshake with
271271+ | AwaitServerHello13 (ch, secrets, log), ServerHello sh ->
272272+ answer_server_hello hs ch sh secrets buf log
273273+ | AwaitServerEncryptedExtensions13 (sd, es, ss, log), EncryptedExtensions ee ->
274274+ answer_encrypted_extensions hs sd es ss ee buf log
275275+ | AwaitServerCertificateRequestOrCertificate13 (sd, es, ss, log), CertificateRequest cr ->
276276+ let* ctx, exts = map_reader_error (parse_certificate_request_1_3 cr) in
277277+ (* during handshake, context must be empty! *)
278278+ let* () =
279279+ guard (ctx = None)
280280+ (`Fatal (`Handshake (`Message "certificate request context must be empty")))
281281+ in
282282+ answer_certificate_request hs sd es ss exts buf log
283283+ | AwaitServerCertificateRequestOrCertificate13 (sd, es, ss, log), Certificate cs ->
284284+ let* con, cs = map_reader_error (parse_certificates_1_3 cs) in
285285+ (* during handshake, context must be empty! and we'll not get any new certificate from server *)
286286+ let* () =
287287+ guard (String.length con = 0)
288288+ (`Fatal (`Handshake (`Message "certificate context must be empty")))
289289+ in
290290+ answer_certificate hs sd es ss None cs buf log
291291+ | AwaitServerCertificate13 (sd, es, ss, sigalgs, log), Certificate cs ->
292292+ let* con, cs = map_reader_error (parse_certificates_1_3 cs) in
293293+ (* during handshake, context must be empty! and we'll not get any new certificate from server *)
294294+ let* () =
295295+ guard (String.length con = 0)
296296+ (`Fatal (`Handshake (`Message "certificate context must be empty")))
297297+ in
298298+ answer_certificate hs sd es ss sigalgs cs buf log
299299+ | AwaitServerCertificateVerify13 (sd, es, ss, sigalgs, log), CertificateVerify cv ->
300300+ answer_certificate_verify hs sd es ss sigalgs cv buf log
301301+ | AwaitServerFinished13 (sd, es, ss, sigalgs, log), Finished fin ->
302302+ answer_finished hs sd es ss sigalgs fin buf log
303303+ | Established13, SessionTicket se -> answer_session_ticket hs se
304304+ | Established13, CertificateRequest _ ->
305305+ Error (`Fatal (`Unexpected (`Handshake handshake))) (* TODO send out C, CV, F *)
306306+ | Established13, KeyUpdate req -> handle_key_update hs req
307307+ | _, hs -> Error (`Fatal (`Unexpected (`Handshake hs)))
+550
vendor/opam/tls/lib/handshake_common.ml
···11+open Core
22+open State
33+44+let src = Logs.Src.create "handshake" ~doc:"TLS handshake"
55+module Log = (val Logs.src_log src : Logs.LOG)
66+77+let trace_cipher cipher =
88+ Tracing.debug (fun m -> m "%a" Ciphersuite.pp_ciphersuite cipher)
99+1010+let empty = function [] -> true | _ -> false
1111+1212+let change_cipher_spec =
1313+ (Packet.CHANGE_CIPHER_SPEC, Writer.assemble_change_cipher_spec)
1414+1515+let hostname (h : client_hello) : [ `host ] Domain_name.t option =
1616+ Utils.map_find ~f:(function `Hostname s -> Some s | _ -> None) h.extensions
1717+1818+let groups (h : client_hello) =
1919+ match Utils.map_find ~f:(function `SupportedGroups g -> Some g | _ -> None) h.extensions with
2020+ | Some xs ->
2121+ List.fold_left (fun acc g ->
2222+ match named_group_to_group g with Some g -> g :: acc | _ -> acc)
2323+ [] xs
2424+ | None -> []
2525+2626+let rec find_matching host certs =
2727+ match certs with
2828+ | (s::_, _) as chain ::xs ->
2929+ if X509.Certificate.supports_hostname s host then
3030+ Some chain
3131+ else
3232+ find_matching host xs
3333+ | _::xs -> find_matching host xs (* this should never happen! *)
3434+ | [] -> None
3535+3636+let agreed_cert certs ?f ?signature_algorithms hostname =
3737+ let match_host ?default host certs =
3838+ match find_matching host certs with
3939+ | Some x -> Ok x
4040+ | None ->
4141+ Option.to_result
4242+ ~none:(`Error (`NoMatchingCertificateFound (Domain_name.to_string host)))
4343+ default
4444+ in
4545+ let filter = function
4646+ | ([], _) -> false (* cannot happen, TODO: adapt types to avoid this case *)
4747+ | (s :: _, _) ->
4848+ match f with
4949+ | None -> true
5050+ | Some f -> f s
5151+ in
5252+ let filter_sigalg c =
5353+ match signature_algorithms with
5454+ | None -> true
5555+ | Some s -> List.exists (pk_matches_sa (snd c)) s
5656+ in
5757+ match certs, hostname with
5858+ | `None, _ -> Error (`Error `CouldntSelectCertificate)
5959+ | `Single c, _ ->
6060+ if filter c && filter_sigalg c then Ok c else Error (`Error `CouldntSelectCertificate)
6161+ | `Multiple_default (c, _), None ->
6262+ if filter c && filter_sigalg c then Ok c else Error (`Error `CouldntSelectCertificate)
6363+ | `Multiple_default (c, cs), Some h ->
6464+ let default = if filter c && filter_sigalg c then Some c else None in
6565+ begin match default, List.filter (fun c -> filter c && filter_sigalg c) cs with
6666+ | Some d, cs -> match_host ~default:d h cs
6767+ | None, c :: cs -> match_host ~default:c h (c::cs)
6868+ | None, [] -> Error (`Error `CouldntSelectCertificate)
6969+ end
7070+ | `Multiple cs, None ->
7171+ begin match List.filter (fun c -> filter c && filter_sigalg c) cs with
7272+ | cert :: _ -> Ok cert
7373+ | _ -> Error (`Error `CouldntSelectCertificate)
7474+ end
7575+ | `Multiple cs, Some h ->
7676+ match List.filter (fun c -> filter c && filter_sigalg c) cs with
7777+ | [ cert ] -> Ok cert
7878+ | c :: cs -> match_host ~default:c h (c :: cs)
7979+ | [] -> Error (`Error `CouldntSelectCertificate)
8080+8181+let get_secure_renegotiation exts =
8282+ Utils.map_find
8383+ exts
8484+ ~f:(function `SecureRenegotiation data -> Some data | _ -> None)
8585+8686+let get_alpn_protocols (ch : client_hello) =
8787+ Utils.map_find ~f:(function `ALPN protocols -> Some protocols | _ -> None) ch.extensions
8888+8989+let alpn_protocol config ch =
9090+ match config.Config.alpn_protocols, get_alpn_protocols ch with
9191+ | _, None | [], _ -> Ok None
9292+ | configured, Some client -> match Utils.first_match client configured with
9393+ | Some proto -> Ok (Some proto)
9494+ | None ->
9595+ (* RFC7301 Section 3.2:
9696+ In the event that the server supports no protocols that the client
9797+ advertises, then the server SHALL respond with a fatal
9898+ "no_application_protocol" alert. *)
9999+ Error (`Fatal `No_application_protocol)
100100+101101+let get_alpn_protocol (sh : server_hello) =
102102+ Utils.map_find ~f:(function `ALPN protocol -> Some protocol | _ -> None) sh.extensions
103103+104104+let empty_common_session_data = {
105105+ server_random = "" ;
106106+ client_random = "" ;
107107+ peer_certificate_chain = [] ;
108108+ peer_certificate = None ;
109109+ trust_anchor = None ;
110110+ received_certificates = [] ;
111111+ own_certificate = [] ;
112112+ own_private_key = None ;
113113+ own_name = None ;
114114+ client_auth = false ;
115115+ master_secret = "" ;
116116+ alpn_protocol = None ;
117117+}
118118+119119+let empty_session = {
120120+ common_session_data = empty_common_session_data ;
121121+ client_version = `TLS_1_2 ;
122122+ ciphersuite = `DHE_RSA_WITH_AES_256_CBC_SHA ;
123123+ group = Some `FFDHE2048 ;
124124+ renegotiation = "", "" ;
125125+ session_id = "" ;
126126+ extended_ms = false ;
127127+ tls_unique = "" ;
128128+}
129129+130130+let empty_session13 cipher = {
131131+ common_session_data13 = empty_common_session_data ;
132132+ ciphersuite13 = cipher ;
133133+ master_secret = Handshake_crypto13.empty cipher ;
134134+ exporter_master_secret = "" ;
135135+ resumption_secret = "" ;
136136+ state = `Established ;
137137+ resumed = false ;
138138+ client_app_secret = "" ;
139139+ server_app_secret = "" ;
140140+}
141141+142142+let common_session_data_of_epoch (epoch : epoch_data) common_session_data =
143143+ {
144144+ common_session_data with
145145+ peer_certificate = epoch.peer_certificate ;
146146+ trust_anchor = epoch.trust_anchor ;
147147+ own_certificate = epoch.own_certificate ;
148148+ own_private_key = epoch.own_private_key ;
149149+ received_certificates = epoch.received_certificates ;
150150+ peer_certificate_chain = epoch.peer_certificate_chain ;
151151+ master_secret = epoch.master_secret ;
152152+ own_name = epoch.own_name ;
153153+ alpn_protocol = epoch.alpn_protocol ;
154154+ }
155155+156156+let session_of_epoch (epoch : epoch_data) : session_data =
157157+ let empty = empty_session in
158158+ let common_session_data = common_session_data_of_epoch epoch empty.common_session_data in
159159+ { empty with
160160+ common_session_data ;
161161+ ciphersuite = epoch.ciphersuite ;
162162+ session_id = epoch.session_id ;
163163+ extended_ms = epoch.extended_ms ;
164164+ }
165165+166166+let session13_of_epoch cipher (epoch : epoch_data) : session_data13 =
167167+ let empty = empty_session13 cipher in
168168+ let common_session_data13 = common_session_data_of_epoch epoch empty.common_session_data13 in
169169+ { empty with
170170+ common_session_data13 ;
171171+ ciphersuite13 = cipher ;
172172+ state = epoch.state ;
173173+ exporter_master_secret = epoch.exporter_master_secret ;
174174+ }
175175+176176+let supported_protocol_version (min, max) v =
177177+ if compare_tls_version min v > 0 then
178178+ None
179179+ else if compare_tls_version v max > 0 then
180180+ None
181181+ else
182182+ Some v
183183+184184+let to_client_ext_type = function
185185+ | `Hostname _ -> `Hostname
186186+ | `MaxFragmentLength _ -> `MaxFragmentLength
187187+ | `SupportedGroups _ -> `SupportedGroups
188188+ | `ECPointFormats -> `ECPointFormats
189189+ | `SecureRenegotiation _ -> `SecureRenegotiation
190190+ | `Padding _ -> `Padding
191191+ | `SignatureAlgorithms _ -> `SignatureAlgorithms
192192+ | `UnknownExtension _ -> `UnknownExtension
193193+ | `ExtendedMasterSecret -> `ExtendedMasterSecret
194194+ | `ALPN _ -> `ALPN
195195+ | `KeyShare _ -> `KeyShare
196196+ | `EarlyDataIndication -> `EarlyDataIndication
197197+ | `PreSharedKeys _ -> `PreSharedKey
198198+ | `Draft _ -> `Draft
199199+ | `SupportedVersions _ -> `SupportedVersion
200200+ | `PostHandshakeAuthentication -> `PostHandshakeAuthentication
201201+ | `Cookie _ -> `Cookie
202202+ | `PskKeyExchangeModes _ -> `PskKeyExchangeMode
203203+204204+let to_server_ext_type = function
205205+ | `Hostname -> `Hostname
206206+ | `MaxFragmentLength _ -> `MaxFragmentLength
207207+ | `ECPointFormats -> `ECPointFormats
208208+ | `SecureRenegotiation _ -> `SecureRenegotiation
209209+ | `UnknownExtension _ -> `UnknownExtension
210210+ | `ExtendedMasterSecret -> `ExtendedMasterSecret
211211+ | `ALPN _ -> `ALPN
212212+ | `KeyShare _ -> `KeyShare
213213+ | `EarlyDataIndication -> `EarlyDataIndication
214214+ | `PreSharedKey _ -> `PreSharedKey
215215+ | `Draft _ -> `Draft
216216+ | `SelectedVersion _ -> `SupportedVersion
217217+218218+let extension_types t exts = List.(
219219+ exts |> map t
220220+ |> filter @@ function `UnknownExtension -> false | _ -> true
221221+ )
222222+223223+(* a server hello may only contain extensions which are also in the client hello *)
224224+(* RFC5246, 7.4.7.1
225225+ An extension type MUST NOT appear in the ServerHello unless the same
226226+ extension type appeared in the corresponding ClientHello. If a
227227+ client receives an extension type in ServerHello that it did not
228228+ request in the associated ClientHello, it MUST abort the handshake
229229+ with an unsupported_extension fatal alert. *)
230230+let server_exts_subset_of_client sexts cexts =
231231+ let (sexts', cexts') =
232232+ (extension_types to_server_ext_type sexts, extension_types to_client_ext_type cexts) in
233233+ Utils.List_set.subset sexts' (`Cookie :: cexts')
234234+235235+module Group = struct
236236+ type t = Packet.named_group
237237+ let compare = Stdlib.compare
238238+end
239239+240240+module GroupSet = Set.Make(Group)
241241+242242+(* Set.of_list appeared only in 4.02, for 4.01 compatibility *)
243243+let of_list xs = List.fold_right GroupSet.add xs GroupSet.empty
244244+245245+let client_hello_valid version (ch : client_hello) =
246246+ (* match ch.version with
247247+ | TLS_1_0 ->
248248+ if List.mem TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA ch.ciphersuites then
249249+ return ()
250250+ else
251251+ fail HANDSHAKE_FAILURE
252252+ | TLS_1_1 ->
253253+ if List.mem TLS_RSA_WITH_3DES_EDE_CBC_SHA ch.ciphersuites then
254254+ return ()
255255+ else
256256+ fail HANDSHAKE_FAILURE
257257+ | TLS_1_2 ->
258258+ if List.mem TLS_RSA_WITH_AES_128_CBC_SHA ch.ciphersuites then
259259+ return ()
260260+ else
261261+ fail HANDSHAKE_FAILURE *)
262262+ let sig_alg =
263263+ Utils.map_find
264264+ ~f:(function `SignatureAlgorithms sa -> Some sa | _ -> None)
265265+ ch.extensions
266266+ and key_share =
267267+ Utils.map_find
268268+ ~f:(function `KeyShare ks -> Some ks | _ -> None)
269269+ ch.extensions
270270+ and groups =
271271+ Utils.map_find
272272+ ~f:(function `SupportedGroups gs -> Some gs | _ -> None)
273273+ ch.extensions
274274+ in
275275+276276+ let version_good = match version with
277277+ | `TLS_1_2 | `TLS_1_X _ -> Ok ()
278278+ | `TLS_1_3 ->
279279+ ( let good_sig_alg =
280280+ List.exists (fun sa -> List.mem sa Config.supported_signature_algorithms)
281281+ in
282282+ match sig_alg with
283283+ | None -> Error (`Fatal (`Missing_extension "signature algorithms"))
284284+ | Some sig_alg when good_sig_alg sig_alg ->
285285+ ( match key_share, groups with
286286+ | None, _ -> Error (`Fatal (`Missing_extension "key share"))
287287+ | _, None -> Error (`Fatal (`Missing_extension "supported group"))
288288+ | Some ks, Some gs ->
289289+ match
290290+ Utils.List_set.is_proper_set gs,
291291+ Utils.List_set.is_proper_set (List.map fst ks),
292292+ GroupSet.subset (of_list (List.map fst ks)) (of_list gs)
293293+ with
294294+ | true, true, true -> Ok ()
295295+ | false, _, _ -> Error (`Fatal (`Handshake (`Message "supported group is not a set")))
296296+ | _, false, _ -> Error (`Fatal (`Handshake (`Message "key share is not a set")))
297297+ | _, _, false -> Error (`Fatal (`Handshake (`Message "key share is not a subset of supported group")) ))
298298+ | Some _ -> Error (`Fatal (`Handshake (`Message "no good signature algorithms")))
299299+ )
300300+ | `SSL_3 | `TLS_1_0 | `TLS_1_1 -> Ok ()
301301+ in
302302+303303+ let share_ciphers =
304304+ match
305305+ Utils.first_match (List.filter_map Ciphersuite.any_ciphersuite_to_ciphersuite ch.ciphersuites) Config.Ciphers.supported
306306+ with
307307+ | None -> false
308308+ | Some _ -> true
309309+ in
310310+ match
311311+ not (empty ch.ciphersuites),
312312+ share_ciphers,
313313+ Utils.List_set.is_proper_set (extension_types to_client_ext_type ch.extensions)
314314+ with
315315+ | true, true, true -> version_good
316316+ | false, _, _ -> Error (`Fatal (`Handshake (`Message "ciphersuites is empty")))
317317+ | _, false, _ -> Error (`Fatal (`Handshake (`Message "no supported ciphersuite")))
318318+ | _, _, false -> Error (`Fatal (`Handshake (`Message "extensions is not a set")))
319319+320320+321321+let server_hello_valid (sh : server_hello) =
322322+ (* let open Ciphersuite in *)
323323+ Utils.List_set.is_proper_set (extension_types to_server_ext_type sh.extensions)
324324+ (* TODO:
325325+ - EC stuff must be present if EC ciphersuite chosen
326326+ *)
327327+328328+let to_sign_1_3 context_string =
329329+ (* input is prepended by 64 * 0x20 (to avoid cross-version attacks) *)
330330+ (* input for signature now contains also a context string *)
331331+ let len = match context_string with
332332+ | None -> 64 + 1
333333+ | Some v -> 64 + String.length v + 1 in
334334+ let buf = Bytes.create len in
335335+ Bytes.fill buf 0 64 '\x20';
336336+ begin match context_string with
337337+ | None -> ()
338338+ | Some v -> Bytes.blit_string v 0 buf 64 (String.length v) end;
339339+ Bytes.set buf (Bytes.length buf - 1) '\x00';
340340+ Bytes.unsafe_to_string buf
341341+342342+let signature version ?context_string data client_sig_algs signature_algorithms (private_key : X509.Private_key.t) =
343343+ match version with
344344+ | `TLS_1_0 | `TLS_1_1 ->
345345+ let* signed =
346346+ match private_key with
347347+ | `RSA key ->
348348+ begin try
349349+ let data =
350350+ Digestif.(MD5.(to_raw_string (digest_string data)) ^
351351+ SHA1.(to_raw_string (digest_string data)))
352352+ in
353353+ Ok (Mirage_crypto_pk.Rsa.PKCS1.sig_encode ~key data)
354354+ with Mirage_crypto_pk.Rsa.Insufficient_key ->
355355+ Error (`Fatal (`Bad_certificate "RSA key too small"))
356356+ end
357357+ | k ->
358358+ (* not passing ~scheme: only non-RSA keys sig scheme is trivial *)
359359+ Result.map_error
360360+ (function `Msg m -> `Fatal (`Handshake (`Message ("signing failed: " ^ m))))
361361+ (X509.Private_key.sign `SHA1 k (`Message data))
362362+ in
363363+ Ok (Writer.assemble_digitally_signed signed)
364364+ | `TLS_1_2 ->
365365+ let* sig_alg =
366366+ match client_sig_algs with
367367+ | None ->
368368+ Ok (match private_key with
369369+ | `RSA _ -> `RSA_PKCS1_SHA1
370370+ | `ED25519 _ -> `ED25519
371371+ | _ -> `ECDSA_SECP256R1_SHA1)
372372+ | Some client_algos ->
373373+ Option.to_result
374374+ ~none:(`Error (`NoConfiguredSignatureAlgorithm client_algos))
375375+ (Utils.first_match client_algos (List.filter (pk_matches_sa private_key) signature_algorithms))
376376+ in
377377+ let scheme = signature_scheme_of_signature_algorithm sig_alg
378378+ and hash = hash_of_signature_algorithm sig_alg
379379+ in
380380+ let* signature =
381381+ Result.map_error (function `Msg m -> `Fatal (`Handshake (`Message ("signing failed: " ^ m))))
382382+ (X509.Private_key.sign hash ~scheme private_key (`Message data))
383383+ in
384384+ Ok (Writer.assemble_digitally_signed_1_2 sig_alg signature)
385385+ | `TLS_1_3 ->
386386+ let to_sign =
387387+ let prefix = to_sign_1_3 context_string in
388388+ prefix ^ data
389389+ in
390390+ let* sig_alg =
391391+ let* client_algos =
392392+ (* 8446 4.2.3 "client MUST send signatureAlgorithms" *)
393393+ Option.to_result
394394+ ~none:(`Error (`NoConfiguredSignatureAlgorithm []))
395395+ client_sig_algs
396396+ in
397397+ let sa = List.filter tls13_sigalg signature_algorithms in
398398+ let sa = List.filter (pk_matches_sa private_key) sa in
399399+ Option.to_result
400400+ ~none:(`Error (`NoConfiguredSignatureAlgorithm client_algos))
401401+ (Utils.first_match client_algos sa)
402402+ in
403403+ let scheme = signature_scheme_of_signature_algorithm sig_alg
404404+ and hash = hash_of_signature_algorithm sig_alg
405405+ in
406406+ let* signature =
407407+ Result.map_error (function `Msg m -> `Fatal (`Handshake (`Message ("signing failed: " ^ m))))
408408+ (X509.Private_key.sign hash ~scheme private_key (`Message to_sign))
409409+ in
410410+ Ok (Writer.assemble_digitally_signed_1_2 sig_alg signature)
411411+412412+let peer_key = function
413413+ | None -> Error (`Fatal (`Bad_certificate "none received"))
414414+ | Some cert -> Ok (X509.Certificate.public_key cert)
415415+416416+let verify_digitally_signed version ?context_string sig_algs data signature_data certificate =
417417+ let* pubkey = peer_key certificate in
418418+ match version with
419419+ | `TLS_1_0 | `TLS_1_1 ->
420420+ let* signature = map_reader_error (Reader.parse_digitally_signed data) in
421421+ begin match pubkey with
422422+ | `RSA key ->
423423+ let* raw =
424424+ Option.to_result
425425+ ~none:(`Fatal (`Handshake (`Message "couldn't decode PKCS1")))
426426+ (Mirage_crypto_pk.Rsa.PKCS1.sig_decode ~key signature)
427427+ in
428428+ let computed =
429429+ Digestif.(MD5.(to_raw_string (digest_string signature_data)) ^
430430+ SHA1.(to_raw_string (digest_string signature_data)))
431431+ in
432432+ guard (String.equal raw computed)
433433+ (`Fatal (`Handshake (`Message "RSA PKCS1 raw <> computed")))
434434+ | key ->
435435+ Result.map_error
436436+ (function `Msg m -> `Fatal (`Handshake (`Message ("signature verification failed: " ^ m))))
437437+ (X509.Public_key.verify `SHA1 ~signature key (`Message signature_data))
438438+ end
439439+ | `TLS_1_2 ->
440440+ let* sig_alg, signature =
441441+ map_reader_error (Reader.parse_digitally_signed_1_2 data)
442442+ in
443443+ let* () =
444444+ guard (List.mem sig_alg sig_algs)
445445+ (`Error (`NoConfiguredSignatureAlgorithm sig_algs))
446446+ in
447447+ let hash = hash_of_signature_algorithm sig_alg
448448+ and scheme = signature_scheme_of_signature_algorithm sig_alg
449449+ in
450450+ Result.map_error
451451+ (function `Msg m -> `Fatal (`Handshake (`Message ("signature verification failed: " ^ m))))
452452+ (X509.Public_key.verify hash ~scheme ~signature pubkey (`Message signature_data))
453453+ | `TLS_1_3 ->
454454+ let* sig_alg, signature =
455455+ map_reader_error (Reader.parse_digitally_signed_1_2 data)
456456+ in
457457+ let* () =
458458+ guard (List.mem sig_alg sig_algs)
459459+ (`Error (`NoConfiguredSignatureAlgorithm sig_algs))
460460+ in
461461+ let hash = hash_of_signature_algorithm sig_alg
462462+ and scheme = signature_scheme_of_signature_algorithm sig_alg
463463+ and data =
464464+ let prefix = to_sign_1_3 context_string in
465465+ prefix ^ signature_data
466466+ in
467467+ Result.map_error
468468+ (function `Msg m -> `Fatal (`Handshake (`Message ("signature verification failed: " ^ m))))
469469+ (X509.Public_key.verify hash ~scheme ~signature pubkey (`Message data))
470470+471471+let validate_chain authenticator certificates ip hostname =
472472+ let authenticate authenticator host certificates =
473473+ Result.map_error
474474+ (fun err -> `Error (`AuthenticationFailure err))
475475+ (authenticator ?ip ~host certificates)
476476+477477+ and key_size min cs =
478478+ let check c =
479479+ match X509.Certificate.public_key c with
480480+ | `RSA key -> Mirage_crypto_pk.Rsa.pub_bits key >= min
481481+ | _ -> true
482482+ in
483483+ guard (List.for_all check cs) (`Fatal (`Bad_certificate "key too small"))
484484+485485+ and parse_certificates certs =
486486+ let certificates =
487487+ let f cs =
488488+ match X509.Certificate.decode_der cs with
489489+ | Ok c -> Some c
490490+ | Error `Msg msg ->
491491+ Log.warn (fun m -> m "cannot decode certificate %s:@.%a" msg
492492+ (Ohex.pp_hexdump ()) cs);
493493+ None
494494+ in
495495+ List.filter_map f certs
496496+ in
497497+ let* () =
498498+ guard (List.length certs = List.length certificates)
499499+ (`Fatal (`Bad_certificate "couldn't decode some certificates"))
500500+ in
501501+ Ok certificates
502502+ in
503503+504504+ (* RFC5246: must be x509v3, take signaturealgorithms into account! *)
505505+ (* RFC2246/4346: is generally x509v3, signing algorithm for certificate _must_ be same as algorithm for certificate key *)
506506+ let* certs = parse_certificates certificates in
507507+ let server = match certs with
508508+ | s::_ -> Some s
509509+ | [] -> None
510510+ in
511511+ match authenticator with
512512+ | None -> Ok (server, certs, [], None)
513513+ | Some authenticator ->
514514+ let* anchor = authenticate authenticator hostname certs in
515515+ let* () = key_size Config.min_rsa_key_size certs in
516516+ Ok (Option.fold ~none:(server, certs, [], None)
517517+ ~some:(fun (chain, anchor) -> (server, certs, chain, Some anchor))
518518+ anchor)
519519+520520+let output_key_update ~request state =
521521+ let hs = state.handshake in
522522+ match hs.session with
523523+ | `TLS13 session :: _ ->
524524+ let* session', encryptor =
525525+ match hs.machina with
526526+ | Client13 Established13 ->
527527+ let client_app_secret, client_ctx =
528528+ Handshake_crypto13.app_secret_n_1
529529+ session.master_secret session.client_app_secret
530530+ in
531531+ Ok ({ session with client_app_secret }, client_ctx)
532532+ | Server13 Established13 ->
533533+ let server_app_secret, server_ctx =
534534+ Handshake_crypto13.app_secret_n_1
535535+ session.master_secret session.server_app_secret
536536+ in
537537+ Ok ({ session with server_app_secret }, server_ctx)
538538+ | _ -> Error (`Fatal (`Handshake (`Message "invalid state for key update")))
539539+ in
540540+ let handshake = { hs with session = `TLS13 session' :: hs.session } in
541541+ let ku =
542542+ let p =
543543+ Packet.(if request then UPDATE_REQUESTED else UPDATE_NOT_REQUESTED)
544544+ in
545545+ KeyUpdate p
546546+ in
547547+ let out = Writer.assemble_handshake ku in
548548+ Ok ({ state with encryptor = Some encryptor ; handshake },
549549+ (Packet.HANDSHAKE, out))
550550+ | _ -> Error (`Fatal (`Handshake (`Message "no earlier session found")))
+111
vendor/opam/tls/lib/handshake_crypto.ml
···11+open State
22+33+let halve secret =
44+ let size = String.length secret in
55+ let half = size - size / 2 in
66+ String.(sub secret 0 half, sub secret (size - half) half)
77+88+let p_hash (hmac, hmac_n) key seed len =
99+ let rec expand a to_go =
1010+ let res = hmac ~key (a ^ seed) in
1111+ if to_go > hmac_n then
1212+ res ^ expand (hmac ~key a) (to_go - hmac_n)
1313+ else String.sub res 0 to_go
1414+ in
1515+ expand (hmac ~key seed) len
1616+1717+let prf_mac = function
1818+ | `RSA_WITH_AES_256_GCM_SHA384
1919+ | `DHE_RSA_WITH_AES_256_GCM_SHA384
2020+ | `ECDHE_RSA_WITH_AES_256_GCM_SHA384
2121+ | `ECDHE_RSA_WITH_AES_256_CBC_SHA384
2222+ | `ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
2323+ | `ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 -> (module Digestif.SHA384 : Digestif.S)
2424+ | _ -> (module Digestif.SHA256 : Digestif.S)
2525+2626+let pseudo_random_function version cipher len secret label seed =
2727+ let labelled = label ^ seed in
2828+ match version with
2929+ | `TLS_1_1 | `TLS_1_0 ->
3030+ let (s1, s2) = halve secret in
3131+ let md5 = p_hash ((fun ~key s -> Digestif.MD5.(to_raw_string (hmac_string ~key s))), Digestif.MD5.digest_size) s1 labelled len
3232+ and sha = p_hash ((fun ~key s -> Digestif.SHA1.(to_raw_string (hmac_string ~key s))), Digestif.SHA1.digest_size) s2 labelled len in
3333+ Mirage_crypto.Uncommon.xor md5 sha
3434+ | `TLS_1_2 ->
3535+ let module D = (val (prf_mac cipher)) in
3636+ p_hash ((fun ~key s -> D.(to_raw_string (hmac_string ~key s))), D.digest_size) secret labelled len
3737+3838+let key_block version cipher len master_secret seed =
3939+ pseudo_random_function version cipher len master_secret "key expansion" seed
4040+4141+let hash version cipher data =
4242+ match version with
4343+ | `TLS_1_0 | `TLS_1_1 -> Digestif.(MD5.(to_raw_string (digest_string data)) ^ SHA1.(to_raw_string (digest_string data)))
4444+ | `TLS_1_2 ->
4545+ let module H = (val prf_mac cipher) in
4646+ H.(to_raw_string (digest_string data))
4747+4848+let finished version cipher master_secret label ps =
4949+ let data = String.concat "" ps in
5050+ let seed = hash version cipher data in
5151+ pseudo_random_function version cipher 12 master_secret label seed
5252+5353+let divide_keyblock key mac iv buf =
5454+ let c_mac, rt0 = Core.split_str buf mac in
5555+ let s_mac, rt1 = Core.split_str rt0 mac in
5656+ let c_key, rt2 = Core.split_str rt1 key in
5757+ let s_key, rt3 = Core.split_str rt2 key in
5858+ let c_iv , s_iv = Core.split_str rt3 iv
5959+ in
6060+ (c_mac, s_mac, c_key, s_key, c_iv, s_iv)
6161+6262+let derive_master_secret version (session : session_data) premaster log =
6363+ let prf = pseudo_random_function version session.ciphersuite 48 premaster in
6464+ if session.extended_ms then
6565+ let session_hash =
6666+ let data = String.concat "" log in
6767+ hash version session.ciphersuite data
6868+ in
6969+ prf "extended master secret" session_hash
7070+ else
7171+ prf "master secret" (session.common_session_data.client_random ^ session.common_session_data.server_random)
7272+7373+let initialise_crypto_ctx version (session : session_data) =
7474+ let open Ciphersuite in
7575+ let client_random = session.common_session_data.client_random
7676+ and server_random = session.common_session_data.server_random
7777+ and master = session.common_session_data.master_secret
7878+ and cipher = session.ciphersuite
7979+ in
8080+8181+ let pp = ciphersuite_privprot cipher in
8282+8383+ let c_mac, s_mac, c_key, s_key, c_iv, s_iv =
8484+ let iv_l = match version with
8585+ | `TLS_1_0 -> Some ()
8686+ | _ -> None
8787+ in
8888+ let key_len, iv_len, mac_len = Ciphersuite.key_length iv_l pp in
8989+ let kblen = 2 * key_len + 2 * mac_len + 2 * iv_len
9090+ and rand = server_random ^ client_random
9191+ in
9292+ let keyblock = key_block version cipher kblen master rand in
9393+ divide_keyblock key_len mac_len iv_len keyblock
9494+ in
9595+9696+ let context cipher_k iv mac_k =
9797+ let open Crypto.Ciphers in
9898+ let cipher_st =
9999+ let iv_mode = match version with
100100+ | `TLS_1_0 -> Iv iv
101101+ | _ -> Random_iv
102102+ in
103103+ get_cipher ~secret:cipher_k ~hmac_secret:mac_k ~iv_mode ~nonce:iv pp
104104+ and sequence = 0L in
105105+ { cipher_st ; sequence }
106106+ in
107107+108108+ let c_context = context c_key c_iv c_mac
109109+ and s_context = context s_key s_iv s_mac in
110110+111111+ (c_context, s_context)
···11+open Core
22+33+let cdiv (x : int) (y : int) =
44+ if x > 0 && y > 0 then (x + y - 1) / y
55+ else if x < 0 && y < 0 then (x + y + 1) / y
66+ else x / y
77+88+let left_pad_dh group msg =
99+ let bytes = cdiv (Mirage_crypto_pk.Dh.modulus_size group) 8 in
1010+ let padding = String.make (bytes - String.length msg) '\x00' in
1111+ padding ^ msg
1212+1313+let not_all_zero r =
1414+ let* str = r in
1515+ try
1616+ for i = 0 to String.length str - 1 do
1717+ if String.unsafe_get str i != '\x00' then raise_notrace Not_found;
1818+ done;
1919+ Error (`Fatal (`Handshake (`BadDH "all zero")))
2020+ with Not_found -> Ok str
2121+2222+let dh_shared secret share =
2323+ (* RFC 8556, Section 7.4.1 - we need zero-padding on the left *)
2424+ let map_ecdh_error =
2525+ Result.map_error (fun e -> `Fatal (`Handshake (`BadECDH e)))
2626+ in
2727+ let open Mirage_crypto_ec in
2828+ not_all_zero
2929+ (match secret with
3030+ | `Finite_field secret ->
3131+ let group = secret.Mirage_crypto_pk.Dh.group in
3232+ let bits = Mirage_crypto_pk.Dh.modulus_size group in
3333+ let* () =
3434+ (* truncated share, better reject this *)
3535+ guard (String.length share = cdiv bits 8)
3636+ (`Fatal (`Handshake (`BadDH "truncated")))
3737+ in
3838+ let* shared =
3939+ Option.to_result
4040+ ~none:(`Fatal (`Handshake (`BadDH "invalid FF")))
4141+ (Mirage_crypto_pk.Dh.shared secret share)
4242+ in
4343+ Ok (left_pad_dh group shared)
4444+ | `P256 priv -> map_ecdh_error (P256.Dh.key_exchange priv share)
4545+ | `P384 priv -> map_ecdh_error (P384.Dh.key_exchange priv share)
4646+ | `P521 priv -> map_ecdh_error (P521.Dh.key_exchange priv share)
4747+ | `X25519 priv -> map_ecdh_error (X25519.key_exchange priv share))
4848+4949+let dh_gen_key group =
5050+ (* RFC 8556, Section 4.2.8.1 - we need zero-padding on the left *)
5151+ match Core.group_to_impl group with
5252+ | `Finite_field mc_group ->
5353+ let sec, shared = Mirage_crypto_pk.Dh.gen_key mc_group in
5454+ `Finite_field sec, left_pad_dh mc_group shared
5555+ | `P256 ->
5656+ let secret, shared = Mirage_crypto_ec.P256.Dh.gen_key () in
5757+ `P256 secret, shared
5858+ | `P384 ->
5959+ let secret, shared = Mirage_crypto_ec.P384.Dh.gen_key () in
6060+ `P384 secret, shared
6161+ | `P521 ->
6262+ let secret, shared = Mirage_crypto_ec.P521.Dh.gen_key () in
6363+ `P521 secret, shared
6464+ | `X25519 ->
6565+ let secret, shared = Mirage_crypto_ec.X25519.gen_key () in
6666+ `X25519 secret, shared
6767+6868+let trace tag cs = Tracing.cs ~tag:("crypto " ^ tag) cs
6969+7070+let pp_hash_k_n ciphersuite =
7171+ let open Ciphersuite in
7272+ let pp = privprot13 ciphersuite
7373+ and hash = hash13 ciphersuite
7474+ in
7575+ let k, n = kn_13 pp in
7676+ (pp, hash, k, n)
7777+7878+let hkdflabel label context length =
7979+ let lbl = "tls13 " ^ label in
8080+ let len_llen = Bytes.create 3 in
8181+ Bytes.set_uint16_be len_llen 0 length;
8282+ Bytes.set_uint8 len_llen 2 (String.length lbl);
8383+ let clen = String.make 1 (Char.unsafe_chr (String.length context)) in
8484+ let lbl = String.concat ""
8585+ [ Bytes.unsafe_to_string len_llen ;
8686+ lbl ;
8787+ clen ;
8888+ context ]
8989+ in
9090+ trace "hkdflabel" lbl ;
9191+ lbl
9292+9393+let derive_secret_no_hash hash prk ?length ?(ctx = "") label =
9494+ let length = match length with
9595+ | None ->
9696+ let module H = (val Digestif.module_of_hash' hash) in
9797+ H.digest_size
9898+ | Some x -> x
9999+ in
100100+ let info = hkdflabel label ctx length in
101101+ trace "prk" prk ;
102102+ let key = Hkdf.expand ~hash ~prk ~info length in
103103+ trace ("derive_secret: " ^ label) key ;
104104+ key
105105+106106+let derive_secret t label log =
107107+ let module H = (val Digestif.module_of_hash' t.State.hash) in
108108+ let ctx = H.(to_raw_string (digest_string log)) in
109109+ trace "derive secret ctx" ctx ;
110110+ derive_secret_no_hash t.State.hash t.State.secret ~ctx label
111111+112112+let empty cipher = {
113113+ State.secret = "" ;
114114+ cipher ;
115115+ hash = Ciphersuite.hash13 cipher
116116+}
117117+118118+let derive t secret_ikm =
119119+ let salt =
120120+ if String.equal t.State.secret "" then
121121+ ""
122122+ else
123123+ derive_secret t "derived" ""
124124+ in
125125+ trace "derive: secret_ikm" secret_ikm ;
126126+ trace "derive: salt" salt ;
127127+ let secret = Hkdf.extract ~hash:t.State.hash ~salt secret_ikm in
128128+ trace "derive (extracted secret)" secret ;
129129+ { t with State.secret }
130130+131131+let traffic_key cipher prk =
132132+ let _, hash, key_len, iv_len = pp_hash_k_n cipher in
133133+ let key_info = hkdflabel "key" "" key_len in
134134+ let key = Hkdf.expand ~hash ~prk ~info:key_info key_len in
135135+ let iv_info = hkdflabel "iv" "" iv_len in
136136+ let iv = Hkdf.expand ~hash ~prk ~info:iv_info iv_len in
137137+ (key, iv)
138138+139139+let ctx t label secret =
140140+ let secret, nonce = traffic_key t.State.cipher secret in
141141+ trace (label ^ " secret") secret ;
142142+ trace (label ^ " nonce") nonce ;
143143+ let pp = Ciphersuite.privprot13 t.State.cipher in
144144+ { State.sequence = 0L ; cipher_st = Crypto.Ciphers.get_aead_cipher ~secret ~nonce pp }
145145+146146+let early_traffic t log =
147147+ let secret = derive_secret t "c e traffic" log in
148148+ (secret, ctx t "client early traffic" secret)
149149+150150+let hs_ctx t log =
151151+ Tracing.cs ~tag:"hs ctx with sec" t.State.secret ;
152152+ Tracing.cs ~tag:"log is" log ;
153153+ let server_handshake_traffic_secret = derive_secret t "s hs traffic" log
154154+ and client_handshake_traffic_secret = derive_secret t "c hs traffic" log
155155+ in
156156+ (server_handshake_traffic_secret,
157157+ ctx t "server handshake traffic" server_handshake_traffic_secret,
158158+ client_handshake_traffic_secret,
159159+ ctx t "client handshake traffic" client_handshake_traffic_secret)
160160+161161+let app_ctx t log =
162162+ let server_application_traffic_secret = derive_secret t "s ap traffic" log
163163+ and client_application_traffic_secret = derive_secret t "c ap traffic" log
164164+ in
165165+ (server_application_traffic_secret,
166166+ ctx t "server application traffic" server_application_traffic_secret,
167167+ client_application_traffic_secret,
168168+ ctx t "client application traffic" client_application_traffic_secret)
169169+170170+let app_secret_n_1 t app_secret =
171171+ let secret = derive_secret_no_hash t.State.hash app_secret "traffic upd" in
172172+ secret, ctx t "traffic update" secret
173173+174174+let exporter t log = derive_secret t "exp master" log
175175+let resumption t log = derive_secret t "res master" log
176176+177177+let res_secret hash secret nonce =
178178+ derive_secret_no_hash hash secret ~ctx:nonce "resumption"
179179+180180+let finished hash secret data =
181181+ let module H = (val Digestif.module_of_hash' hash) in
182182+ let key = derive_secret_no_hash hash secret "finished" in
183183+ H.(to_raw_string (hmac_string ~key (to_raw_string (digest_string data))))
+662
vendor/opam/tls/lib/handshake_server.ml
···11+open Core
22+open State
33+open Handshake_common
44+open Config
55+66+let state_version state = match state.protocol_version with
77+ | #tls_before_13 as v -> v
88+ | _ -> assert false
99+1010+let hello_request state =
1111+ if state.config.use_reneg then
1212+ let hr = HelloRequest in
1313+ Tracing.hs ~tag:"handshake-out" hr ;
1414+ let state = { state with machina = Server AwaitClientHelloRenegotiate } in
1515+ Ok (state, [`Record (Packet.HANDSHAKE, Writer.assemble_handshake hr)])
1616+ else
1717+ Error (`Fatal (`Handshake (`Message "renegotation is not supported")))
1818+1919+2020+let answer_client_finished state (session : session_data) client_fin raw log =
2121+ let client, server =
2222+ let checksum = Handshake_crypto.finished (state_version state)
2323+ session.ciphersuite session.common_session_data.master_secret
2424+ in
2525+ (checksum "client finished" log, checksum "server finished" (log @ [raw]))
2626+ in
2727+ let* () =
2828+ guard (String.equal client client_fin)
2929+ (`Fatal (`Handshake (`Message "couldn't verify finished")))
3030+ in
3131+ let session = { session with tls_unique = client } in
3232+ let fin = Finished server in
3333+ let fin_raw = Writer.assemble_handshake fin in
3434+ (* we really do not want to have any leftover handshake fragments *)
3535+ let* () =
3636+ guard (String.length state.hs_fragment = 0)
3737+ (`Fatal (`Handshake `Fragments))
3838+ in
3939+ let session = { session with renegotiation = (client, server) }
4040+ and machina = Server Established
4141+ in
4242+ Tracing.hs ~tag:"handshake-out" fin ;
4343+ Ok ({ state with machina ; session = `TLS session :: state.session },
4444+ [`Record (Packet.HANDSHAKE, fin_raw)])
4545+4646+let answer_client_finished_resume state (session : session_data) server_verify client_fin _raw log =
4747+ let client_verify =
4848+ Handshake_crypto.finished (state_version state) session.ciphersuite session.common_session_data.master_secret "client finished" log
4949+ in
5050+ let* () =
5151+ guard (String.equal client_verify client_fin)
5252+ (`Fatal (`Handshake (`Message "couldn't verify finished")))
5353+ in
5454+ (* we really do not want to have any leftover handshake fragments *)
5555+ let* () =
5656+ guard (String.length state.hs_fragment = 0)
5757+ (`Fatal (`Handshake `Fragments))
5858+ in
5959+ let session = { session with renegotiation = (client_verify, server_verify) }
6060+ and machina = Server Established
6161+ in
6262+ Ok ({ state with machina ; session = `TLS session :: state.session }, [])
6363+6464+let establish_master_secret state (session : session_data) premastersecret raw log =
6565+ let log = log @ [raw] in
6666+ let master_secret = Handshake_crypto.derive_master_secret
6767+ (state_version state) session premastersecret log
6868+ in
6969+ let session =
7070+ let common_session_data = { session.common_session_data with master_secret } in
7171+ { session with common_session_data }
7272+ in
7373+ let client_ctx, server_ctx =
7474+ Handshake_crypto.initialise_crypto_ctx (state_version state) session
7575+ in
7676+ let machina =
7777+ match session.common_session_data.peer_certificate with
7878+ | None -> AwaitClientChangeCipherSpec (session, server_ctx, client_ctx, log)
7979+ | Some _ -> AwaitClientCertificateVerify (session, server_ctx, client_ctx, log)
8080+ in
8181+ Tracing.cs ~tag:"master-secret" master_secret ;
8282+ ({ state with machina = Server machina }, [])
8383+8484+let private_key (session : session_data) =
8585+ match session.common_session_data.own_private_key with
8686+ | Some priv -> Ok priv
8787+ | None -> Error (`Fatal (`Handshake (`Message "couldn't locate private key")))
8888+8989+let validate_certs certs authenticator ip (session : session_data) =
9090+ let* peer_certificate, received_certificates, peer_certificate_chain, trust_anchor =
9191+ validate_chain authenticator certs ip None
9292+ in
9393+ let common_session_data = {
9494+ session.common_session_data with
9595+ received_certificates ;
9696+ peer_certificate ;
9797+ peer_certificate_chain ;
9898+ trust_anchor
9999+ } in
100100+ Ok { session with common_session_data }
101101+102102+let answer_client_certificate_RSA state (session : session_data) certs raw log =
103103+ let* session =
104104+ validate_certs certs state.config.authenticator state.config.ip session
105105+ in
106106+ let machina = AwaitClientKeyExchange_RSA (session, log @ [raw]) in
107107+ Ok ({ state with machina = Server machina }, [])
108108+109109+let answer_client_certificate_DHE state (session : session_data) dh_sent certs raw log =
110110+ let* session =
111111+ validate_certs certs state.config.authenticator state.config.ip session
112112+ in
113113+ let machina = AwaitClientKeyExchange_DHE (session, dh_sent, log @ [raw]) in
114114+ Ok ({ state with machina = Server machina }, [])
115115+116116+let answer_client_certificate_verify state (session : session_data) sctx cctx verify raw log =
117117+ let sigdata = String.concat "" log in
118118+ let* () =
119119+ verify_digitally_signed state.protocol_version
120120+ state.config.signature_algorithms verify sigdata
121121+ session.common_session_data.peer_certificate
122122+ in
123123+ let machina = AwaitClientChangeCipherSpec (session, sctx, cctx, log @ [raw]) in
124124+ Ok ({ state with machina = Server machina }, [])
125125+126126+let answer_client_key_exchange_RSA state (session : session_data) kex raw log =
127127+ (* due to bleichenbacher attach, we should use a random pms *)
128128+ (* then we do not leak any decryption or padding errors! *)
129129+ let other = Writer.assemble_protocol_version state.protocol_version ^ Mirage_crypto_rng.generate 46 in
130130+ let validate_premastersecret k =
131131+ (* Client implementations MUST always send the correct version number in
132132+ PreMasterSecret. If ClientHello.client_version is TLS 1.1 or higher,
133133+ server implementations MUST check the version number as described in
134134+ the note below. If the version number is TLS 1.0 or earlier, server
135135+ implementations SHOULD check the version number, but MAY have a
136136+ configuration option to disable the check. Note that if the check
137137+ fails, the PreMasterSecret SHOULD be randomized as described below *)
138138+ (* we do not provide an option to disable the version checking (yet!) *)
139139+ match String.length k = 48, Reader.parse_any_version k with
140140+ | true, Ok c_ver when c_ver = session.client_version -> k
141141+ | _ -> other
142142+ in
143143+144144+ let* k = private_key session in
145145+ match k with
146146+ | `RSA key ->
147147+ let pms = match Mirage_crypto_pk.Rsa.PKCS1.decrypt ~key kex with
148148+ | None -> validate_premastersecret other
149149+ | Some k -> validate_premastersecret k
150150+ in
151151+ Ok (establish_master_secret state session pms raw log)
152152+ | _ -> Error (`Fatal (`Bad_certificate "expected RSA certificate"))
153153+154154+let answer_client_key_exchange_DHE state session secret kex raw log =
155155+ let* pms =
156156+ let open Mirage_crypto_ec in
157157+ let map_ecdh_error =
158158+ Result.map_error (fun e -> `Fatal (`Handshake (`BadECDH e)))
159159+ in
160160+ match secret with
161161+ | `P256 priv ->
162162+ let* share = map_reader_error (Reader.parse_client_ec_key_exchange kex) in
163163+ map_ecdh_error (P256.Dh.key_exchange priv share)
164164+ | `P384 priv ->
165165+ let* share = map_reader_error (Reader.parse_client_ec_key_exchange kex) in
166166+ map_ecdh_error (P384.Dh.key_exchange priv share)
167167+ | `P521 priv ->
168168+ let* share = map_reader_error (Reader.parse_client_ec_key_exchange kex) in
169169+ map_ecdh_error (P521.Dh.key_exchange priv share)
170170+ | `X25519 priv ->
171171+ let* share = map_reader_error (Reader.parse_client_ec_key_exchange kex) in
172172+ map_ecdh_error (X25519.key_exchange priv share)
173173+ | `Finite_field secret ->
174174+ let* share = map_reader_error (Reader.parse_client_dh_key_exchange kex) in
175175+ Option.to_result
176176+ ~none:(`Fatal (`Handshake (`BadDH "invalid FF")))
177177+ (Mirage_crypto_pk.Dh.shared secret share)
178178+ in
179179+ Ok (establish_master_secret state session pms raw log)
180180+181181+let sig_algs (client_hello : client_hello) =
182182+ Utils.map_find
183183+ ~f:(function `SignatureAlgorithms xs -> Some xs | _ -> None)
184184+ client_hello.extensions
185185+186186+let ecc_group configured_groups requested_groups =
187187+ Utils.first_match requested_groups configured_groups
188188+189189+let agreed_cipher cert ecc requested =
190190+ let usage_matches cipher =
191191+ let csusage =
192192+ Ciphersuite.(required_usage @@ ciphersuite_kex cipher)
193193+ in
194194+ supports_key_usage ~not_present:true csusage cert
195195+ in
196196+ let cciphers = List.filter usage_matches requested in
197197+ if ecc then
198198+ cciphers
199199+ else
200200+ List.filter (fun x -> not (Ciphersuite.ecdhe x)) cciphers
201201+202202+let server_hello config (client_hello : client_hello) (session : session_data) version reneg =
203203+ (* RFC 4366: server shall reply with an empty hostname extension *)
204204+ let host = Option.fold ~none:[] ~some:(fun _ -> [`Hostname]) session.common_session_data.own_name
205205+ and server_random =
206206+ let suffix =
207207+ match version, max_protocol_version config.protocol_versions with
208208+ | `TLS_1_2, `TLS_1_3 -> Packet.downgrade12
209209+ | _, `TLS_1_3 -> Packet.downgrade11
210210+ | _ -> ""
211211+ in
212212+ let rst = Mirage_crypto_rng.generate (32 - String.length suffix) in
213213+ rst ^ suffix
214214+ and secren = match reneg with
215215+ | None -> `SecureRenegotiation ""
216216+ | Some (cvd, svd) -> `SecureRenegotiation (cvd ^ svd)
217217+ and ems = if session.extended_ms then
218218+ [`ExtendedMasterSecret]
219219+ else
220220+ []
221221+ and session_id =
222222+ match String.length session.session_id with
223223+ | 0 -> Mirage_crypto_rng.generate 32
224224+ | _ -> session.session_id
225225+ and alpn =
226226+ match session.common_session_data.alpn_protocol with
227227+ | None -> []
228228+ | Some protocol -> [`ALPN protocol]
229229+ and ecpointformat =
230230+ match Utils.map_find ~f:(function `ECPointFormats -> Some () | _ -> None) client_hello.extensions with
231231+ | Some () when Ciphersuite.ecdhe session.ciphersuite -> [ `ECPointFormats ]
232232+ | _ -> []
233233+ in
234234+ let sh = ServerHello
235235+ { server_version = version ;
236236+ server_random = server_random ;
237237+ sessionid = Some session_id ;
238238+ ciphersuite = session.ciphersuite ;
239239+ extensions = secren :: host @ ems @ alpn @ ecpointformat }
240240+ in
241241+ trace_cipher session.ciphersuite ;
242242+ Tracing.debug (fun m -> m "version %a" pp_tls_version version) ;
243243+ Tracing.hs ~tag:"handshake-out" sh ;
244244+ let common_session_data = { session.common_session_data with server_random } in
245245+ (Writer.assemble_handshake sh,
246246+ { session with common_session_data ; session_id })
247247+248248+let answer_client_hello_common state reneg ch raw =
249249+ let process_client_hello ch config =
250250+ let host = hostname ch
251251+ and groups = groups ch
252252+ and cciphers = List.filter_map Ciphersuite.any_ciphersuite_to_ciphersuite ch.ciphersuites
253253+ in
254254+ let configured_ecc_groups, other_groups = List.partition Config.elliptic_curve config.groups in
255255+ let ecc_group = ecc_group configured_ecc_groups groups
256256+ and cciphers = List.filter (fun c -> not (Ciphersuite.ciphersuite_tls13 c)) cciphers
257257+ in
258258+ let cciphers = List.filter (fun c -> List.mem c config.ciphers) cciphers in
259259+ let f =
260260+ (* from the ciphers, figure out:
261261+ - (a) RSA only (b) EC only
262262+ - (c) static RSA only (keyUsage = KeyEncipherment) (d) DHE only (keyUsage = DigitalSignature)
263263+ - (e) from the groups (they indicate the key type!)
264264+ *)
265265+ let kt_filter =
266266+ match List.partition (fun c -> Ciphersuite.ciphersuite_keytype c = `RSA) cciphers with
267267+ | _::_, [] -> begin fun s -> match X509.Certificate.public_key s with `RSA _ -> true | _ -> false end
268268+ | [], _::_ -> begin fun s -> match X509.Certificate.public_key s with `ED25519 _ | `P256 _ | `P384 _ | `P521 _ -> true | _ -> false end
269269+ | _, _ -> begin fun _s -> true end
270270+ in
271271+ let ku_filter =
272272+ match List.partition (fun c -> Ciphersuite.ciphersuite_kex c = `RSA) cciphers with
273273+ | _::_, [] -> supports_key_usage ~not_present:true `Key_encipherment
274274+ | [], _::_ -> supports_key_usage ~not_present:true `Digital_signature
275275+ | _ -> begin fun _ -> true end
276276+ in
277277+ let kt_matches_group s =
278278+ match X509.Certificate.public_key s with
279279+ | `RSA _ -> true
280280+ | `ED25519 _ -> List.mem `X25519 groups
281281+ | `P256 _ -> List.mem `P256 groups
282282+ | `P384 _ -> List.mem `P384 groups
283283+ | `P521 _ -> List.mem `P521 groups
284284+ in
285285+ fun s ->
286286+ kt_filter s && ku_filter s && kt_matches_group s
287287+ in
288288+ let signature_algorithms = sig_algs ch in
289289+ let* cciphers, chain, priv =
290290+ let* r =
291291+ agreed_cert ~f ?signature_algorithms config.own_certificates host
292292+ in
293293+ match r with
294294+ | (c::cs, priv) ->
295295+ let cciphers = agreed_cipher c (ecc_group <> None) cciphers in
296296+ Ok (cciphers, c::cs, Some priv)
297297+ | ([], _) -> Error (`Fatal (`Handshake (`Message "couldn't find certificate chain")))
298298+ in
299299+300300+ let* cipher =
301301+ match Utils.first_match cciphers config.ciphers with
302302+ | Some x -> Ok x
303303+ | None ->
304304+ let* _ =
305305+ Option.to_result
306306+ ~none:(`Fatal (`Handshake (`Message "no supported ciphersuite")))
307307+ (Utils.first_match cciphers Config.Ciphers.supported)
308308+ in
309309+ Error (`Error (`NoConfiguredCiphersuite cciphers))
310310+ in
311311+312312+ let extended_ms = List.mem `ExtendedMasterSecret ch.extensions in
313313+314314+ Log.debug (fun m -> m "cipher %a" Ciphersuite.pp_ciphersuite cipher) ;
315315+316316+ let* alpn_protocol = alpn_protocol config ch in
317317+318318+ let group =
319319+ if Ciphersuite.ecdhe cipher then
320320+ ecc_group
321321+ else match other_groups with
322322+ | [] -> None
323323+ | c::_ -> Some c
324324+ in
325325+ let session =
326326+ let session = empty_session in
327327+ let common_session_data = {
328328+ session.common_session_data with
329329+ client_random = ch.client_random ;
330330+ own_certificate = chain ;
331331+ own_private_key = priv ;
332332+ own_name = host ;
333333+ alpn_protocol = alpn_protocol
334334+ } in
335335+ { session with
336336+ common_session_data ;
337337+ client_version = ch.client_version ;
338338+ ciphersuite = cipher ;
339339+ group = group ;
340340+ extended_ms = extended_ms ;
341341+ }
342342+ in
343343+ Ok session
344344+345345+ and server_cert (session : session_data) =
346346+ match session.common_session_data.own_certificate with
347347+ | [] -> []
348348+ | certs ->
349349+ let cs = List.map X509.Certificate.encode_der certs in
350350+ let cert = Certificate (Writer.assemble_certificates cs) in
351351+ Tracing.hs ~tag:"handshake-out" cert ;
352352+ [ Writer.assemble_handshake cert ]
353353+354354+ and cert_request version config (session : session_data) =
355355+ let open Writer in
356356+ match config.authenticator with
357357+ | None -> Ok ([], session)
358358+ | Some _ ->
359359+ let cas =
360360+ List.map X509.Distinguished_name.encode_der config.acceptable_cas
361361+ and certs =
362362+ [ Packet.RSA_SIGN ; Packet.ECDSA_SIGN ]
363363+ in
364364+ let* data =
365365+ match version with
366366+ | `TLS_1_0 | `TLS_1_1 ->
367367+ Ok (assemble_certificate_request certs cas)
368368+ | `TLS_1_2 ->
369369+ Ok (assemble_certificate_request_1_2 certs config.signature_algorithms cas)
370370+ in
371371+ let certreq = CertificateRequest data in
372372+ Tracing.hs ~tag:"handshake-out" certreq ;
373373+ let common_session_data = { session.common_session_data with client_auth = true } in
374374+ Ok ([ assemble_handshake certreq ], { session with common_session_data })
375375+376376+ and kex_dhe config (session : session_data) version sig_algs =
377377+ let* secret, written =
378378+ match session.group with
379379+ | None -> assert false (* can not happen *)
380380+ | Some g ->
381381+ let open Mirage_crypto_ec in
382382+ match group_to_impl g with
383383+ | `Finite_field g ->
384384+ let secret, msg = Mirage_crypto_pk.Dh.gen_key g in
385385+ let dh_param = Crypto.dh_params_pack g msg in
386386+ let dh_params = Writer.assemble_dh_parameters dh_param in
387387+ Ok (`Finite_field secret, dh_params)
388388+ | `P256 ->
389389+ let secret, shared = P256.Dh.gen_key () in
390390+ let params = Writer.assemble_ec_parameters `P256 shared in
391391+ Ok (`P256 secret, params)
392392+ | `P384 ->
393393+ let secret, shared = P384.Dh.gen_key () in
394394+ let params = Writer.assemble_ec_parameters `P384 shared in
395395+ Ok (`P384 secret, params)
396396+ | `P521 ->
397397+ let secret, shared = P521.Dh.gen_key () in
398398+ let params = Writer.assemble_ec_parameters `P521 shared in
399399+ Ok (`P521 secret, params)
400400+ | `X25519 ->
401401+ let secret, shared = X25519.gen_key () in
402402+ let params = Writer.assemble_ec_parameters `X25519 shared in
403403+ Ok (`X25519 secret, params)
404404+ in
405405+ let data = String.concat "" [
406406+ session.common_session_data.client_random ;
407407+ session.common_session_data.server_random ;
408408+ written
409409+ ]
410410+ in
411411+ let* priv = private_key session in
412412+ let* sgn = signature version data sig_algs config.signature_algorithms priv in
413413+ let kex = ServerKeyExchange (written ^ sgn) in
414414+ let hs = Writer.assemble_handshake kex in
415415+ Tracing.hs ~tag:"handshake-out" kex ;
416416+ Ok (hs, secret)
417417+ in
418418+419419+ let* session = process_client_hello ch state.config in
420420+ let sh, session = server_hello state.config ch session state.protocol_version reneg in
421421+ let certificates = server_cert session
422422+ and hello_done = Writer.assemble_handshake ServerHelloDone
423423+ in
424424+ let* cert_req, session =
425425+ cert_request (state_version state) state.config session
426426+ in
427427+428428+ let* out_recs, machina =
429429+ match Ciphersuite.ciphersuite_kex session.ciphersuite with
430430+ | #Ciphersuite.key_exchange_algorithm_dhe ->
431431+ let* kex, dh =
432432+ kex_dhe state.config session state.protocol_version (sig_algs ch)
433433+ in
434434+ let outs = sh :: certificates @ [ kex ] @ cert_req @ [ hello_done ] in
435435+ let log = raw :: outs in
436436+ let machina =
437437+ if session.common_session_data.client_auth then
438438+ AwaitClientCertificate_DHE (session, dh, log)
439439+ else
440440+ AwaitClientKeyExchange_DHE (session, dh, log)
441441+ in
442442+ Tracing.hs ~tag:"handshake-out" ServerHelloDone ;
443443+ Ok (outs, machina)
444444+ | `RSA ->
445445+ let outs = sh :: certificates @ cert_req @ [ hello_done ] in
446446+ let log = raw :: outs in
447447+ let machina =
448448+ if session.common_session_data.client_auth then
449449+ AwaitClientCertificate_RSA (session, log)
450450+ else
451451+ AwaitClientKeyExchange_RSA (session, log)
452452+ in
453453+ Tracing.hs ~tag:"handshake-out" ServerHelloDone ;
454454+ Ok (outs, machina)
455455+ in
456456+457457+ Ok ({ state with machina = Server machina },
458458+ [`Record (Packet.HANDSHAKE, String.concat "" out_recs)])
459459+460460+(* TODO could benefit from result monadd *)
461461+let agreed_version supported (client_hello : client_hello) =
462462+ let raw_client_versions =
463463+ match List.filter_map (function `SupportedVersions vs -> Some vs | _ -> None) client_hello.extensions with
464464+ | [] -> [client_hello.client_version]
465465+ | [vs] -> vs
466466+ | _ -> invalid_arg "bad supported version extension"
467467+ in
468468+ let supported_versions = List.fold_left (fun acc v ->
469469+ match any_version_to_version v with
470470+ | None -> acc
471471+ | Some v -> v :: acc) [] raw_client_versions
472472+ in
473473+ let client_versions = List.sort_uniq compare_tls_version supported_versions in
474474+ match
475475+ List.fold_left (fun r v ->
476476+ match supported_protocol_version supported v with
477477+ | None -> r
478478+ | Some v -> Some v)
479479+ None client_versions
480480+ with
481481+ | Some x -> Ok x
482482+ | None -> match supported_versions with
483483+ | [] -> Error (`Fatal (`Protocol_version (`None_supported raw_client_versions)))
484484+ | _ -> Error (`Error (`NoConfiguredVersions supported_versions))
485485+486486+let answer_client_hello state (ch : client_hello) raw =
487487+ let ensure_reneg ciphers their_data =
488488+ let reneg_cs = List.mem Packet.TLS_EMPTY_RENEGOTIATION_INFO_SCSV ciphers in
489489+ let err = `Fatal (`Handshake (`Message "invalid renegotiation")) in
490490+ match reneg_cs, their_data with
491491+ | _, Some x -> guard (String.length x = 0) err
492492+ | true, _ -> Ok ()
493493+ | _ -> Error err
494494+495495+ and resume (ch : client_hello) state =
496496+ let epoch_matches (epoch : Core.epoch_data) version ciphers extensions =
497497+ let cciphers = List.filter_map Ciphersuite.any_ciphersuite_to_ciphersuite ciphers in
498498+ List.mem epoch.ciphersuite cciphers &&
499499+ version = epoch.protocol_version &&
500500+ (not state.config.use_reneg ||
501501+ (List.mem `ExtendedMasterSecret extensions && epoch.extended_ms))
502502+ in
503503+504504+ match Option.bind ch.sessionid state.config.session_cache with
505505+ | Some epoch when epoch_matches epoch state.protocol_version ch.ciphersuites ch.extensions ->
506506+ let session =
507507+ let session = session_of_epoch epoch in
508508+ let common_session_data = {
509509+ session.common_session_data with
510510+ client_random = ch.client_random ;
511511+ client_auth = (epoch.peer_certificate <> None) ;
512512+ } in
513513+ { session with common_session_data ; client_version = ch.client_version }
514514+ in
515515+ Some session
516516+ | _ -> None
517517+518518+ and answer_resumption session state =
519519+ let version = state_version state in
520520+ let sh, session = server_hello state.config ch session version None in
521521+ (* we really do not want to have any leftover handshake fragments *)
522522+ let* () =
523523+ guard (String.length state.hs_fragment = 0)
524524+ (`Fatal (`Handshake `Fragments))
525525+ in
526526+ let client_ctx, server_ctx =
527527+ Handshake_crypto.initialise_crypto_ctx version session
528528+ in
529529+ let ccs = change_cipher_spec in
530530+ let log = [ raw ; sh ] in
531531+ let server =
532532+ Handshake_crypto.finished
533533+ version session.ciphersuite session.common_session_data.master_secret "server finished" log
534534+ in
535535+ let session = { session with tls_unique = server } in
536536+ let fin = Finished server in
537537+ let fin_raw = Writer.assemble_handshake fin in
538538+ Tracing.cs ~tag:"change-cipher-spec-out" (snd ccs) ;
539539+ Tracing.hs ~tag:"handshake-out" fin ;
540540+ let machina = AwaitClientChangeCipherSpecResume (session, client_ctx, server, log @ [fin_raw]) in
541541+ Ok ({ state with machina = Server machina },
542542+ [ `Record (Packet.HANDSHAKE, sh) ;
543543+ `Record ccs ;
544544+ `Change_enc server_ctx ;
545545+ `Record (Packet.HANDSHAKE, fin_raw)])
546546+ in
547547+548548+ let process_client_hello config ch version =
549549+ let cciphers = ch.ciphersuites in
550550+ let* () = client_hello_valid version ch in
551551+ let* () =
552552+ guard (not (List.mem Packet.TLS_FALLBACK_SCSV cciphers) ||
553553+ version = max_protocol_version config.protocol_versions)
554554+ (`Fatal `Inappropriate_fallback)
555555+ in
556556+ let theirs = get_secure_renegotiation ch.extensions in
557557+ ensure_reneg cciphers theirs
558558+ in
559559+560560+ let process protocol_version =
561561+ let* () = process_client_hello state.config ch protocol_version in
562562+ let state = { state with protocol_version } in
563563+ (match resume ch state with
564564+ | None -> answer_client_hello_common state None ch raw
565565+ | Some session -> answer_resumption session state)
566566+ in
567567+568568+ let* v = agreed_version state.config.protocol_versions ch in
569569+ match v with
570570+ | `TLS_1_3 -> Handshake_server13.answer_client_hello ~hrr:false state ch raw
571571+ | protocol_version -> process protocol_version
572572+573573+let answer_client_hello_reneg state (ch : client_hello) raw =
574574+ (* ensure reneg allowed and supplied *)
575575+ let ensure_reneg our_data their_data =
576576+ let err = `Fatal (`Handshake (`Message "invalid renegotiation")) in
577577+ match our_data, their_data with
578578+ | (cvd, _), Some x -> guard (String.equal cvd x) err
579579+ | _ -> Error err
580580+ in
581581+582582+ let process_client_hello config oldversion ours ch =
583583+ let* () = client_hello_valid oldversion ch in
584584+ let* version = agreed_version config.protocol_versions ch in
585585+ let* () =
586586+ guard (version = oldversion)
587587+ (`Fatal (`Handshake (`Message "invalid renegotiation version")))
588588+ in
589589+ let theirs = get_secure_renegotiation ch.extensions in
590590+ let* () = ensure_reneg ours theirs in
591591+ Ok version
592592+ in
593593+594594+ let config = state.config in
595595+ match config.use_reneg, state.session with
596596+ | true , `TLS session :: _ ->
597597+ let reneg = session.renegotiation in
598598+ let* _version = process_client_hello config state.protocol_version reneg ch in
599599+ answer_client_hello_common state (Some reneg) ch raw
600600+ | false, _ ->
601601+ let no_reneg = Writer.assemble_alert ~level:Packet.WARNING Packet.NO_RENEGOTIATION in
602602+ Tracing.debug (fun m -> m "alert-out (warning, no_renegotiation)") ;
603603+ Ok (state, [`Record (Packet.ALERT, no_reneg)])
604604+ | true , _ -> Error (`Fatal (`Handshake (`Message "couldn't find an earlier session")))
605605+606606+let handle_change_cipher_spec ss state packet =
607607+ let* () = map_reader_error (Reader.parse_change_cipher_spec packet) in
608608+ match ss with
609609+ | AwaitClientChangeCipherSpec (session, server_ctx, client_ctx, log) ->
610610+ let* () =
611611+ guard (String.length state.hs_fragment = 0)
612612+ (`Fatal (`Handshake `Fragments))
613613+ in
614614+ let ccs = change_cipher_spec in
615615+ let machina = AwaitClientFinished (session, log)
616616+ in
617617+ Tracing.cs ~tag:"change-cipher-spec-in" packet ;
618618+ Tracing.cs ~tag:"change-cipher-spec-out" packet ;
619619+620620+ Ok ({ state with machina = Server machina },
621621+ [`Record ccs; `Change_enc server_ctx; `Change_dec client_ctx])
622622+ | AwaitClientChangeCipherSpecResume (session, client_ctx, server_verify, log) ->
623623+ let* () =
624624+ guard (String.length state.hs_fragment = 0)
625625+ (`Fatal (`Handshake `Fragments))
626626+ in
627627+ let machina = AwaitClientFinishedResume (session, server_verify, log)
628628+ in
629629+ Tracing.cs ~tag:"change-cipher-spec-in" packet ;
630630+631631+ Ok ({ state with machina = Server machina },
632632+ [`Change_dec client_ctx])
633633+ | _ -> Error (`Fatal (`Unexpected (`Message "change cipher spec")))
634634+635635+let handle_handshake ss hs buf =
636636+ let* handshake = map_reader_error (Reader.parse_handshake buf) in
637637+ Tracing.hs ~tag:"handshake-in" handshake;
638638+ match ss, handshake with
639639+ | AwaitClientHello, ClientHello ch ->
640640+ answer_client_hello hs ch buf
641641+ | AwaitClientCertificate_RSA (session, log), Certificate cs ->
642642+ let* cs = map_reader_error (Reader.parse_certificates cs) in
643643+ answer_client_certificate_RSA hs session cs buf log
644644+ | AwaitClientCertificate_DHE (session, dh_sent, log), Certificate cs ->
645645+ let* cs = map_reader_error (Reader.parse_certificates cs) in
646646+ answer_client_certificate_DHE hs session dh_sent cs buf log
647647+ | AwaitClientKeyExchange_RSA (session, log), ClientKeyExchange cs ->
648648+ let* kex = map_reader_error (Reader.parse_client_dh_key_exchange cs) in
649649+ answer_client_key_exchange_RSA hs session kex buf log
650650+ | AwaitClientKeyExchange_DHE (session, dh_sent, log), ClientKeyExchange kex ->
651651+ answer_client_key_exchange_DHE hs session dh_sent kex buf log
652652+ | AwaitClientCertificateVerify (session, sctx, cctx, log), CertificateVerify ver ->
653653+ answer_client_certificate_verify hs session sctx cctx ver buf log
654654+ | AwaitClientFinished (session, log), Finished fin ->
655655+ answer_client_finished hs session fin buf log
656656+ | AwaitClientFinishedResume (session, server_verify, log), Finished fin ->
657657+ answer_client_finished_resume hs session server_verify fin buf log
658658+ | Established, ClientHello ch -> (* client-initiated renegotiation *)
659659+ answer_client_hello_reneg hs ch buf
660660+ | AwaitClientHelloRenegotiate, ClientHello ch -> (* hello-request send, renegotiation *)
661661+ answer_client_hello_reneg hs ch buf
662662+ | _, hs -> Error (`Fatal (`Unexpected (`Handshake hs)))
+6
vendor/opam/tls/lib/handshake_server.mli
···11+open State
22+33+val hello_request : handshake_state -> (handshake_return, failure) result
44+55+val handle_change_cipher_spec : server_handshake_state -> handshake_state -> string -> (handshake_return, failure) result
66+val handle_handshake : server_handshake_state -> handshake_state -> string -> (handshake_return, failure) result
+511
vendor/opam/tls/lib/handshake_server13.ml
···11+open State
22+open Core
33+open Handshake_common
44+55+open Handshake_crypto13
66+77+let answer_client_hello ~hrr state ch raw =
88+ let* () = client_hello_valid `TLS_1_3 ch in
99+ let* () =
1010+ guard (not (hrr && List.mem `EarlyDataIndication ch.extensions))
1111+ (`Fatal (`Handshake (`Message "has 0RTT after hello retry request")))
1212+ in
1313+ Tracing.debug (fun m -> m "version %a" pp_tls_version `TLS_1_3) ;
1414+1515+ let ciphers =
1616+ List.filter_map Ciphersuite.any_ciphersuite_to_ciphersuite13 ch.ciphersuites
1717+ in
1818+1919+ let* groups =
2020+ let* gs =
2121+ Option.to_result
2222+ ~none:(`Fatal (`Missing_extension "supported group"))
2323+ (Utils.map_find ~f:(function `SupportedGroups gs -> Some gs | _ -> None) ch.extensions)
2424+ in
2525+ Ok (List.filter_map Core.named_group_to_group gs)
2626+ in
2727+2828+ let* keyshares =
2929+ let* ks =
3030+ Option.to_result
3131+ ~none:(`Fatal (`Missing_extension "key share"))
3232+ (Utils.map_find ~f:(function `KeyShare ks -> Some ks | _ -> None) ch.extensions)
3333+ in
3434+ List.fold_left (fun acc (g, ks) ->
3535+ let* acc = acc in
3636+ match Core.named_group_to_group g with
3737+ | None -> Ok acc
3838+ | Some g -> Ok ((g, ks) :: acc))
3939+ (Ok []) ks
4040+ in
4141+4242+ let base_server_hello ?epoch cipher extensions =
4343+ let ciphersuite = (cipher :> Ciphersuite.ciphersuite) in
4444+ let sh =
4545+ { server_version = `TLS_1_3 ;
4646+ server_random = Mirage_crypto_rng.generate 32 ;
4747+ sessionid = ch.sessionid ;
4848+ ciphersuite ;
4949+ extensions }
5050+ in
5151+ let session : session_data13 =
5252+ let base = match epoch with None -> empty_session13 cipher | Some e -> session13_of_epoch cipher e in
5353+ let common_session_data13 = {
5454+ base.common_session_data13 with
5555+ server_random = sh.server_random ;
5656+ client_random = ch.client_random ;
5757+ } in
5858+ let resumed = match epoch with None -> false | Some _ -> true in
5959+ { base with common_session_data13 ; ciphersuite13 = cipher ; resumed }
6060+ in
6161+ (sh, session)
6262+ in
6363+ let config = state.config in
6464+ match
6565+ Utils.first_match (List.map fst keyshares) config.Config.groups,
6666+ Utils.first_match ciphers (Config.ciphers13 config)
6767+ with
6868+ | _, None -> Error (`Error (`NoConfiguredCiphersuite ciphers))
6969+ | None, Some cipher ->
7070+ if hrr then
7171+ (* avoid loops CH -> HRR -> CH -> HRR -> ... *)
7272+ Error (`Fatal (`Handshake (`Message "hello retry request already sent, still no supported group")))
7373+ else
7474+ (* no keyshare, looks whether there's a supported group ++ send back HRR *)
7575+ begin match Utils.first_match groups config.Config.groups with
7676+ | None -> Error (`Fatal (`Handshake (`Message "no supported group found")))
7777+ | Some group ->
7878+ let cookie =
7979+ let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 cipher)) in
8080+ H.(to_raw_string (digest_string raw))
8181+ in
8282+ let hrr = { retry_version = `TLS_1_3 ; ciphersuite = cipher ; sessionid = ch.sessionid ; selected_group = group ; extensions = [ `Cookie cookie ] } in
8383+ let hrr_raw = Writer.assemble_handshake (HelloRetryRequest hrr) in
8484+ Tracing.hs ~tag:"handshake-out" (HelloRetryRequest hrr) ;
8585+ (* there is no early data anymore if HRR was sent (see 4.1.2) *)
8686+ (* but the client wouldn't know until it received the HRR *)
8787+ let early_data_left = if List.mem `EarlyDataIndication ch.extensions then config.Config.zero_rtt else 0l in
8888+ let machina = Server13 AwaitClientHelloHRR13 in
8989+ Ok ({ state with early_data_left ; machina },
9090+ `Record (Packet.HANDSHAKE, hrr_raw) ::
9191+ (match ch.sessionid with
9292+ | None -> []
9393+ | Some _ -> [`Record change_cipher_spec]))
9494+ end
9595+ | Some group, Some cipher ->
9696+ Log.debug (fun m -> m "cipher %a" Ciphersuite.pp_ciphersuite cipher) ;
9797+ Log.debug (fun m -> m "group %a" pp_group group) ;
9898+9999+ if not (List.mem group groups) then
100100+ Error (`Fatal (`Handshake (`Message "keyshare group not in group list")))
101101+ else
102102+ (* we already checked above in keyshares that group is present there *)
103103+ let keyshare =
104104+ snd (List.find (fun (g, _) -> g = group) keyshares)
105105+ in
106106+ (* DHE - full handshake *)
107107+108108+ let* log =
109109+ if hrr then
110110+ let* c =
111111+ Option.to_result
112112+ ~none:(`Fatal (`Missing_extension "cookie"))
113113+ (Utils.map_find ~f:(function `Cookie c -> Some c | _ -> None) ch.extensions)
114114+ in
115115+ (* log is: 254 00 00 length c :: HRR *)
116116+ let hash_hdr = Writer.assemble_message_hash (String.length c) in
117117+ let hrr = { retry_version = `TLS_1_3 ; ciphersuite = cipher ; sessionid = ch.sessionid ; selected_group = group ; extensions = [ `Cookie c ]} in
118118+ let hs_buf = Writer.assemble_handshake (HelloRetryRequest hrr) in
119119+ Ok (String.concat "" [ hash_hdr ; c ; hs_buf ])
120120+ else
121121+ Ok ""
122122+ in
123123+124124+ let hostname = hostname ch in
125125+ let hlen =
126126+ let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 cipher)) in
127127+ H.digest_size
128128+ in
129129+130130+ let early_secret, epoch, exts, can_use_early_data =
131131+ let secret ?(psk = String.make hlen '\x00') () = Handshake_crypto13.(derive (empty cipher) psk) in
132132+ let no_resume = secret (), None, [], false in
133133+ match
134134+ config.Config.ticket_cache,
135135+ Utils.map_find ~f:(function `PreSharedKeys ids -> Some ids | _ -> None) ch.extensions,
136136+ Utils.map_find ~f:(function `PskKeyExchangeModes ms -> Some ms | _ -> None) ch.extensions
137137+ with
138138+ | None, _, _ | _, None, _ -> no_resume
139139+ | Some _, Some _, None -> no_resume (* should this lead to an error instead? *)
140140+ | Some cache, Some ids, Some ms ->
141141+ if not (List.mem Packet.PSK_KE_DHE ms) then
142142+ no_resume
143143+ else
144144+ let idx_ids = List.mapi (fun i id -> (i, id)) ids in
145145+ match
146146+ List.filter (fun (_, ((id, _), _)) ->
147147+ match cache.Config.lookup id with None -> false | Some _ -> true)
148148+ idx_ids
149149+ with
150150+ | [] ->
151151+ Log.info (fun m -> m "found no id in psk cache") ;
152152+ no_resume
153153+ | (idx, ((id, obf_age), binder))::_ ->
154154+ (* need to verify binder, do the obf_age computations + checking,
155155+ figure out whether the id is in our psk cache, and use the resumption secret as input
156156+ and Ok the idx *)
157157+ let psk, old_epoch =
158158+ match cache.Config.lookup id with
159159+ | None -> assert false (* see above *)
160160+ | Some x -> x
161161+ in
162162+ match Ciphersuite.(any_ciphersuite_to_ciphersuite13 (ciphersuite_to_any_ciphersuite old_epoch.ciphersuite)) with
163163+ | None -> no_resume
164164+ | Some c' ->
165165+ if c' = cipher &&
166166+ match hostname, old_epoch.own_name with
167167+ | None, None -> true
168168+ | Some x, Some y -> Domain_name.equal x y
169169+ | _ -> false
170170+ then
171171+ let now = cache.Config.timestamp () in
172172+ let server_delta_t = Ptime.diff now psk.issued_at in
173173+ let client_delta_t =
174174+ match Ptime.Span.of_float_s Int32.(to_float (sub obf_age psk.obfuscation) /. 1000.) with
175175+ | None ->
176176+ Logs.debug (fun m -> m "client_delta is not computable, using 0") ;
177177+ Ptime.Span.zero
178178+ | Some x -> x
179179+ in
180180+ (* ensure server&client_delta_t are not too far off! *)
181181+ match Ptime.Span.(to_int_s (abs (sub server_delta_t client_delta_t))) with
182182+ | None ->
183183+ Logs.debug (fun m -> m "s_c_delta computation lead nowhere") ;
184184+ no_resume
185185+ | Some s_c_delta ->
186186+ if s_c_delta > 10 then begin
187187+ Logs.debug (fun m -> m "delta between client and server is %d seconds, ignoring this ticket!" s_c_delta);
188188+ no_resume
189189+ end else
190190+ (* if ticket_creation ts + lifetime > now, continue *)
191191+ let until = match Ptime.add_span psk.issued_at (Ptime.Span.of_int_s (Int32.to_int cache.Config.lifetime)) with
192192+ | None -> Ptime.epoch
193193+ | Some ts -> ts
194194+ in
195195+ if Ptime.is_earlier now ~than:until then
196196+ let early_secret = secret ~psk:psk.secret () in
197197+ let binder_key = Handshake_crypto13.derive_secret early_secret "res binder" "" in
198198+ let binders_len = binders_len ids in
199199+ let ch_part = String.(sub raw 0 (length raw - binders_len)) in
200200+ let log = log ^ ch_part in
201201+ let binder' = Handshake_crypto13.finished early_secret.hash binder_key log in
202202+ if String.equal binder binder' then begin
203203+ (* from 4.1.2 - earlydata is not allowed after hrr *)
204204+ let zero = idx = 0 && not hrr && List.mem `EarlyDataIndication ch.extensions in
205205+ early_secret, Some old_epoch, [ `PreSharedKey idx ], zero
206206+ end else
207207+ no_resume
208208+ else
209209+ no_resume
210210+ else
211211+ no_resume
212212+ in
213213+214214+ let _, early_traffic_ctx = Handshake_crypto13.early_traffic early_secret raw in
215215+216216+ let secret, public = Handshake_crypto13.dh_gen_key group in
217217+ let* es = Handshake_crypto13.dh_shared secret keyshare in
218218+ let hs_secret = Handshake_crypto13.derive early_secret es in
219219+ Tracing.cs ~tag:"hs secret" hs_secret.secret ;
220220+221221+ let sh, session = base_server_hello ?epoch cipher (`KeyShare (group, public) :: exts) in
222222+ let sh_raw = Writer.assemble_handshake (ServerHello sh) in
223223+ Tracing.hs ~tag:"handshake-out" (ServerHello sh) ;
224224+225225+ let log = log ^ raw ^ sh_raw in
226226+ let server_hs_secret, server_ctx, client_hs_secret, client_ctx = hs_ctx hs_secret log in
227227+228228+ let* sigalgs =
229229+ Option.to_result
230230+ ~none:(`Fatal (`Missing_extension "signature algorithms"))
231231+ (Utils.map_find ~f:(function `SignatureAlgorithms sa -> Some sa | _ -> None) ch.extensions)
232232+ in
233233+ (* TODO respect certificate_signature_algs if present *)
234234+235235+ let f = supports_key_usage ~not_present:true `Digital_signature in
236236+ let* chain, priv =
237237+ let* r = agreed_cert ~f ~signature_algorithms:sigalgs config.Config.own_certificates hostname in
238238+ match r with
239239+ | c::cs, priv -> Ok (c::cs, priv)
240240+ | _ -> Error (`Fatal (`Handshake (`Message "couldn't find certificate chain")))
241241+ in
242242+ let* alpn_protocol = alpn_protocol config ch in
243243+ let session =
244244+ let common_session_data13 = { session.common_session_data13 with
245245+ own_name = hostname ; own_certificate = chain ;
246246+ own_private_key = Some priv ; alpn_protocol }
247247+ in
248248+ { session with common_session_data13 }
249249+ in
250250+251251+ let ee =
252252+ let hostname_ext = Option.fold ~none:[] ~some:(fun _ -> [`Hostname]) hostname
253253+ and alpn = Option.fold ~none:[] ~some:(fun proto -> [`ALPN proto]) alpn_protocol
254254+ and early_data = if can_use_early_data && config.Config.zero_rtt <> 0l then [ `EarlyDataIndication ] else []
255255+ in
256256+ EncryptedExtensions (hostname_ext @ alpn @ early_data)
257257+ in
258258+ (* TODO also max_fragment_length ; client_certificate_url ; trusted_ca_keys ; user_mapping ; client_authz ; server_authz ; cert_type ; use_srtp ; heartbeat ; alpn ; status_request_v2 ; signed_cert_timestamp ; client_cert_type ; server_cert_type *)
259259+ let ee_raw = Writer.assemble_handshake ee in
260260+ Tracing.hs ~tag:"handshake-out" ee ;
261261+ let log = log ^ ee_raw in
262262+263263+ let* c_out, log, session' =
264264+ if session.resumed then
265265+ Ok ([], log, session)
266266+ else
267267+ let out, log, session = match config.Config.authenticator with
268268+ | None -> [], log, session
269269+ | Some _ ->
270270+ let certreq =
271271+ let exts =
272272+ `SignatureAlgorithms config.Config.signature_algorithms ::
273273+ (match config.Config.acceptable_cas with
274274+ | [] -> []
275275+ | cas -> [ `CertificateAuthorities cas ])
276276+ in
277277+ CertificateRequest (Writer.assemble_certificate_request_1_3 exts)
278278+ in
279279+ Tracing.hs ~tag:"handshake-out" certreq ;
280280+ let raw_cert_req = Writer.assemble_handshake certreq in
281281+ let common_session_data13 = { session.common_session_data13 with client_auth = true } in
282282+ [raw_cert_req], log ^ raw_cert_req, { session with common_session_data13 }
283283+ in
284284+285285+ let certs = List.map X509.Certificate.encode_der chain in
286286+ let cert = Certificate (Writer.assemble_certificates_1_3 "" certs) in
287287+ let cert_raw = Writer.assemble_handshake cert in
288288+ Tracing.hs ~tag:"handshake-out" cert ;
289289+ let log = log ^ cert_raw in
290290+291291+ let tbs =
292292+ let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 cipher)) in
293293+ H.(to_raw_string (digest_string log))
294294+ in
295295+ let* signed =
296296+ signature `TLS_1_3
297297+ ~context_string:"TLS 1.3, server CertificateVerify"
298298+ tbs (Some sigalgs) config.Config.signature_algorithms priv
299299+ in
300300+ let cv = CertificateVerify signed in
301301+ let cv_raw = Writer.assemble_handshake cv in
302302+ Tracing.hs ~tag:"handshake-out" cv ;
303303+ let log = log ^ cv_raw in
304304+ Ok (out @ [cert_raw; cv_raw], log, session)
305305+ in
306306+307307+ let master_secret = Handshake_crypto13.derive hs_secret (String.make hlen '\x00') in
308308+ Tracing.cs ~tag:"master-secret" master_secret.secret ;
309309+310310+ let f_data = finished hs_secret.hash server_hs_secret log in
311311+ let fin = Finished f_data in
312312+ let fin_raw = Writer.assemble_handshake fin in
313313+314314+ Tracing.hs ~tag:"handshake-out" fin ;
315315+316316+ let log = log ^ fin_raw in
317317+ let server_app_secret, server_app_ctx, client_app_secret, client_app_ctx =
318318+ app_ctx master_secret log
319319+ in
320320+ let exporter_master_secret = Handshake_crypto13.exporter master_secret log in
321321+ let session' = { session' with server_app_secret ; client_app_secret ; exporter_master_secret } in
322322+323323+ let* () =
324324+ guard (String.length state.hs_fragment = 0)
325325+ (`Fatal (`Handshake `Fragments))
326326+ in
327327+328328+ (* send sessionticket early *)
329329+ (* TODO track the nonce across handshakes / newsessionticket messages (i.e. after post-handshake auth) - needs to be unique! *)
330330+ let st, st_raw =
331331+ match session.resumed, config.Config.ticket_cache with
332332+ | true, _ | _, None -> None, []
333333+ | false, Some cache ->
334334+ let age_add =
335335+ let cs = Mirage_crypto_rng.generate 4 in
336336+ String.get_int32_be cs 0
337337+ in
338338+ let psk_id = Mirage_crypto_rng.generate 32 in
339339+ let nonce = Mirage_crypto_rng.generate 4 in
340340+ let extensions = match config.Config.zero_rtt with
341341+ | 0l -> []
342342+ | x -> [ `EarlyDataIndication x ]
343343+ in
344344+ let st = { lifetime = cache.Config.lifetime ; age_add ; nonce ; ticket = psk_id ; extensions } in
345345+ Tracing.hs ~tag:"handshake-out" (SessionTicket st) ;
346346+ let st_raw = Writer.assemble_handshake (SessionTicket st) in
347347+ (Some st, [st_raw])
348348+ in
349349+350350+ let session =
351351+ let common_session_data13 = { session'.common_session_data13 with master_secret = master_secret.secret } in
352352+ { session' with common_session_data13 ; master_secret }
353353+ in
354354+ let st, session =
355355+ if can_use_early_data then
356356+ (AwaitEndOfEarlyData13 (client_hs_secret, client_ctx, client_app_ctx, st, log),
357357+ `TLS13 { session with state = `ZeroRTT } :: state.session)
358358+ else if session.common_session_data13.client_auth then
359359+ (AwaitClientCertificate13 (session, client_hs_secret, client_app_ctx, st, log),
360360+ state.session)
361361+ else
362362+ (AwaitClientFinished13 (client_hs_secret, client_app_ctx, st, log),
363363+ `TLS13 session :: state.session)
364364+ in
365365+ let early_data_left = if List.mem `EarlyDataIndication ch.extensions then config.Config.zero_rtt else 0l in
366366+ Ok ({ state with machina = Server13 st ; session ; early_data_left },
367367+ `Record (Packet.HANDSHAKE, sh_raw) ::
368368+ (match ch.sessionid with
369369+ | Some _ when not hrr -> [`Record change_cipher_spec]
370370+ | _ -> []) @
371371+ [ `Change_enc server_ctx ;
372372+ `Change_dec (if can_use_early_data then early_traffic_ctx else client_ctx) ;
373373+ `Record (Packet.HANDSHAKE, ee_raw) ] @
374374+ List.map (fun data -> `Record (Packet.HANDSHAKE, data)) c_out @
375375+ [ `Record (Packet.HANDSHAKE, fin_raw) ;
376376+ `Change_enc server_app_ctx ] @
377377+ List.map (fun data -> `Record (Packet.HANDSHAKE, data)) st_raw)
378378+379379+let answer_client_certificate state cert (sd : session_data13) client_fini dec_ctx st raw log =
380380+ let* c = map_reader_error (Reader.parse_certificates_1_3 cert) in
381381+ match c, state.config.Config.authenticator with
382382+ | (_, []), None -> Error (`Fatal (`Handshake (`Message "couldn't find authenticator")))
383383+ | (_ctx, []), Some auth ->
384384+ begin match auth ~host:None [] with
385385+ | Ok anchor ->
386386+ let trust_anchor = match anchor with
387387+ | None -> None
388388+ | Some (_chain, ta) -> Some ta
389389+ in
390390+ let common_session_data13 = { sd.common_session_data13 with trust_anchor } in
391391+ let sd = { sd with common_session_data13 } in
392392+ let st = AwaitClientFinished13 (client_fini, dec_ctx, st, log ^ raw) in
393393+ Ok ({ state with machina = Server13 st ; session = `TLS13 sd :: state.session }, [])
394394+ | Error e -> Error (`Error (`AuthenticationFailure e))
395395+ end
396396+ | (_ctx, cert_exts), auth ->
397397+ (* TODO what to do with ctx? send through authenticator? *)
398398+ (* TODO what to do with extensions? *)
399399+ let certs = List.map fst cert_exts in
400400+ let* peer_certificate, received_certificates, peer_certificate_chain, trust_anchor =
401401+ validate_chain auth certs state.config.Config.ip None
402402+ in
403403+ let sd' = let common_session_data13 = {
404404+ sd.common_session_data13 with
405405+ received_certificates ;
406406+ peer_certificate ;
407407+ peer_certificate_chain ;
408408+ trust_anchor
409409+ } in
410410+ { sd with common_session_data13 }
411411+ in
412412+ let st = AwaitClientCertificateVerify13 (sd', client_fini, dec_ctx, st, log ^ raw) in
413413+ Ok ({ state with machina = Server13 st }, [])
414414+415415+let answer_client_certificate_verify state cv (sd : session_data13) client_fini dec_ctx st raw log =
416416+ let tbs =
417417+ let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 sd.ciphersuite13)) in
418418+ H.(to_raw_string (digest_string log))
419419+ in
420420+ let* () =
421421+ verify_digitally_signed `TLS_1_3
422422+ ~context_string:"TLS 1.3, client CertificateVerify"
423423+ state.config.Config.signature_algorithms cv tbs
424424+ sd.common_session_data13.peer_certificate
425425+ in
426426+ let st = AwaitClientFinished13 (client_fini, dec_ctx, st, log ^ raw) in
427427+ Ok ({ state with machina = Server13 st ; session = `TLS13 sd :: state.session }, [])
428428+429429+let answer_client_finished state fin client_fini dec_ctx st raw log =
430430+ match state.session with
431431+ | `TLS13 session :: rest ->
432432+ let hash = Ciphersuite.hash13 session.ciphersuite13 in
433433+ let data = finished hash client_fini log in
434434+ let* () =
435435+ guard (String.equal data fin)
436436+ (`Fatal (`Handshake (`Message "couldn't verify finished")))
437437+ in
438438+ let* () =
439439+ guard (String.length state.hs_fragment = 0)
440440+ (`Fatal (`Handshake `Fragments))
441441+ in
442442+ let session' = match st, state.config.Config.ticket_cache with
443443+ | None, _ | _, None -> session
444444+ | Some st, Some cache ->
445445+ let resumption_secret = Handshake_crypto13.resumption session.master_secret (log ^ raw) in
446446+ let session = { session with resumption_secret } in
447447+ let secret = Handshake_crypto13.res_secret hash resumption_secret st.nonce in
448448+ let issued_at = cache.Config.timestamp () in
449449+ let psk = { identifier = st.ticket ; obfuscation = st.age_add ; secret ; lifetime = st.lifetime ; early_data = state.config.Config.zero_rtt ; issued_at } in
450450+ let epoch = epoch_of_session true None `TLS_1_3 (`TLS13 session) in
451451+ cache.Config.ticket_granted psk epoch ;
452452+ session
453453+ in
454454+ let state' = { state with machina = Server13 Established13 ; session = `TLS13 session' :: rest } in
455455+ Ok (state', [ `Change_dec dec_ctx ])
456456+ | _ -> Error (`Fatal (`Handshake (`Message "no session found in finished")))
457457+458458+let handle_end_of_early_data state cf hs_ctx cc st buf log =
459459+ let machina = AwaitClientFinished13 (cf, cc, st, log ^ buf) in
460460+ match state.session with
461461+ | `TLS13 s1 :: _ ->
462462+ let session = `TLS13 { s1 with state = `Established } :: state.session in
463463+ Ok ({ state with machina = Server13 machina ; session }, [ `Change_dec hs_ctx ])
464464+ | _ ->
465465+ Error (`Fatal (`Handshake (`Message "no session handling end of early data")))
466466+467467+let handle_key_update state req =
468468+ match state.session with
469469+ | `TLS13 session :: _ ->
470470+ let* () =
471471+ guard (String.length state.hs_fragment = 0)
472472+ (`Fatal (`Handshake `Fragments))
473473+ in
474474+ let client_app_secret, client_ctx =
475475+ app_secret_n_1 session.master_secret session.client_app_secret
476476+ in
477477+ let session' = { session with client_app_secret } in
478478+ let session', out = match req with
479479+ | Packet.UPDATE_NOT_REQUESTED -> session', []
480480+ | Packet.UPDATE_REQUESTED ->
481481+ let server_app_secret, server_ctx =
482482+ app_secret_n_1 session.master_secret session.server_app_secret
483483+ in
484484+ let ku = KeyUpdate Packet.UPDATE_NOT_REQUESTED in
485485+ Tracing.hs ~tag:"handshake-out" ku ;
486486+ let ku_raw = Writer.assemble_handshake ku in
487487+ { session' with server_app_secret },
488488+ [ `Record (Packet.HANDSHAKE, ku_raw); `Change_enc server_ctx ]
489489+ in
490490+ let session = `TLS13 session' :: state.session in
491491+ let state' = { state with machina = Server13 Established13 ; session } in
492492+ Ok (state', `Change_dec client_ctx :: out)
493493+ | _ -> Error (`Fatal (`Handshake (`Message "no session while handling key update")))
494494+495495+let handle_handshake cs hs buf =
496496+ let* handshake = map_reader_error (Reader.parse_handshake buf) in
497497+ Tracing.hs ~tag:"handshake-in" handshake;
498498+ match cs, handshake with
499499+ | AwaitClientHelloHRR13, ClientHello ch ->
500500+ answer_client_hello ~hrr:true hs ch buf
501501+ | AwaitClientCertificate13 (sd, cf, cc, st, log), Certificate cert ->
502502+ answer_client_certificate hs cert sd cf cc st buf log
503503+ | AwaitClientCertificateVerify13 (sd, cf, cc, st, log), CertificateVerify cv ->
504504+ answer_client_certificate_verify hs cv sd cf cc st buf log
505505+ | AwaitClientFinished13 (cf, cc, st, log), Finished x ->
506506+ answer_client_finished hs x cf cc st buf log
507507+ | AwaitEndOfEarlyData13 (cf, hs_c, cc, st, log), EndOfEarlyData ->
508508+ handle_end_of_early_data hs cf hs_c cc st buf log
509509+ | Established13, KeyUpdate req ->
510510+ handle_key_update hs req
511511+ | _, hs -> Error (`Fatal (`Unexpected (`Handshake hs)))
···11+open Packet
22+open Core
33+44+type error =
55+ | TrailingBytes of string
66+ | WrongLength of string
77+ | Unknown of string
88+99+exception Reader_error of error
1010+1111+let raise_unknown msg = raise (Reader_error (Unknown msg))
1212+and raise_wrong_length msg = raise (Reader_error (WrongLength msg))
1313+and raise_trailing_bytes msg = raise (Reader_error (TrailingBytes msg))
1414+1515+let shift str amount = String.sub str amount (String.length str - amount)
1616+1717+let catch f x =
1818+ try Ok (f x) with
1919+ | Reader_error TrailingBytes msg -> Error (`Decode ("trailing bytes: " ^ msg))
2020+ | Reader_error WrongLength msg -> Error (`Decode ("wrong length: " ^ msg))
2121+ | Reader_error Unknown msg -> Error (`Decode msg)
2222+ | Invalid_argument msg -> Error (`Decode msg)
2323+2424+let parse_version_int buf =
2525+ let major = String.get_uint8 buf 0 in
2626+ let minor = String.get_uint8 buf 1 in
2727+ (major, minor)
2828+2929+let parse_version_exn buf =
3030+ let version = parse_version_int buf in
3131+ match tls_version_of_pair version with
3232+ | Some x -> x
3333+ | None -> raise_unknown "version"
3434+3535+let parse_any_version_opt buf =
3636+ let version = parse_version_int buf in
3737+ tls_any_version_of_pair version, shift buf 2
3838+3939+let parse_any_version_exn buf =
4040+ match parse_any_version_opt buf with
4141+ | Some x, _ -> x
4242+ | None, _ -> raise_unknown "version"
4343+4444+let parse_version = catch parse_version_exn
4545+4646+let parse_any_version = catch parse_any_version_exn
4747+4848+let parse_record buf =
4949+ if String.length buf < 5 then
5050+ Ok (`Fragment buf)
5151+ else
5252+ let typ = String.get_uint8 buf 0
5353+ and version = parse_version_int (shift buf 1)
5454+ in
5555+ match String.get_uint16_be buf 3 with
5656+ | x when x > (1 lsl 14 + 2048) ->
5757+ (* 2 ^ 14 + 2048 for TLSCiphertext
5858+ 2 ^ 14 + 1024 for TLSCompressed
5959+ 2 ^ 14 for TLSPlaintext *)
6060+ Error (`Record_overflow x)
6161+ | x when 5 + x > String.length buf -> Ok (`Fragment buf)
6262+ | x ->
6363+ match
6464+ tls_any_version_of_pair version,
6565+ int_to_content_type typ
6666+ with
6767+ | None, _ -> Error (`Protocol_version (`Unknown_record version))
6868+ | _, None -> Error (`Unexpected (`Content_type typ))
6969+ | Some version, Some content_type ->
7070+ let payload, rest = split_str ~start:5 buf x in
7171+ Ok (`Record (({ content_type ; version }, payload), rest))
7272+7373+let validate_alert (lvl, typ) =
7474+ let open Packet in
7575+ match lvl, typ with
7676+ (* from RFC, find out which ones must be always FATAL
7777+ and report if this does not meet the expectations *)
7878+ | WARNING, (UNEXPECTED_MESSAGE | BAD_RECORD_MAC | RECORD_OVERFLOW
7979+ | HANDSHAKE_FAILURE | BAD_CERTIFICATE | DECODE_ERROR
8080+ | PROTOCOL_VERSION | INAPPROPRIATE_FALLBACK | MISSING_EXTENSION
8181+ | UNSUPPORTED_EXTENSION | UNRECOGNIZED_NAME |
8282+ NO_APPLICATION_PROTOCOL as x) ->
8383+ raise_unknown (alert_type_to_string x ^ " must always be fatal")
8484+8585+ (* those are always warnings *)
8686+ | FATAL, (USER_CANCELED | NO_RENEGOTIATION as x) ->
8787+ raise_unknown (alert_type_to_string x ^ " must always be a warning")
8888+8989+ | lvl, typ -> (lvl, typ)
9090+9191+let parse_alert = catch @@ fun buf ->
9292+ if String.length buf <> 2 then
9393+ raise_trailing_bytes "after alert"
9494+ else
9595+ let level = String.get_uint8 buf 0 in
9696+ let typ = String.get_uint8 buf 1 in
9797+ match int_to_alert_level level, int_to_alert_type typ with
9898+ | (Some lvl, msg) -> validate_alert (lvl, msg)
9999+ | _ -> raise_unknown @@ "alert level " ^ string_of_int level
100100+101101+let parse_change_cipher_spec buf =
102102+ match String.length buf, String.get_uint8 buf 0 with
103103+ | 1, 1 -> Ok ()
104104+ | _ -> Error (`Decode "bad change cipher spec message")
105105+106106+let rec parse_count_list parsef buf acc = function
107107+ | 0 -> (List.rev acc, buf)
108108+ | n ->
109109+ match parsef buf with
110110+ | Some elem, buf' -> parse_count_list parsef buf' (elem :: acc) (pred n)
111111+ | None , buf' -> parse_count_list parsef buf' acc (pred n)
112112+113113+let rec parse_list parsef buf acc =
114114+ match String.length buf with
115115+ | 0 -> List.rev acc
116116+ | _ ->
117117+ match parsef buf with
118118+ | Some elem, buf' -> parse_list parsef buf' (elem :: acc)
119119+ | None , buf' -> parse_list parsef buf' acc
120120+121121+let parse_compression_method buf =
122122+ let cm = String.get_uint8 buf 0 in
123123+ (int_to_compression_method cm, shift buf 1)
124124+125125+let parse_compression_methods buf =
126126+ let count = String.get_uint8 buf 0 in
127127+ parse_count_list parse_compression_method (shift buf 1) [] count
128128+129129+let parse_any_ciphersuite buf =
130130+ let typ = String.get_uint16_be buf 0 in
131131+ (int_to_any_ciphersuite typ, shift buf 2)
132132+133133+let parse_any_ciphersuites buf =
134134+ let count = String.get_uint16_be buf 0 in
135135+ if count mod 2 <> 0 then
136136+ raise_wrong_length "ciphersuite list"
137137+ else
138138+ parse_count_list parse_any_ciphersuite (shift buf 2) [] (count / 2)
139139+140140+let parse_ciphersuite buf =
141141+ match parse_any_ciphersuite buf with
142142+ | None , buf' -> (None, buf')
143143+ | Some cs, buf' -> match Ciphersuite.any_ciphersuite_to_ciphersuite cs with
144144+ | None -> (None, buf')
145145+ | Some cs' -> (Some cs', buf')
146146+147147+let parse_hostnames buf =
148148+ match String.length buf with
149149+ | 0 -> []
150150+ | n ->
151151+ let parsef buf =
152152+ let typ = String.get_uint8 buf 0 in
153153+ let entrylen = String.get_uint16_be buf 1 in
154154+ let rt = shift buf (3 + entrylen) in
155155+ match typ with
156156+ | 0 -> let hostname = String.sub buf 3 entrylen in
157157+ (Some hostname, rt)
158158+ | _ -> (None, rt)
159159+ in
160160+ let list_length = String.get_uint16_be buf 0 in
161161+ if list_length + 2 <> n then
162162+ raise_trailing_bytes "hostname"
163163+ else
164164+ parse_list parsef (String.sub buf 2 list_length) []
165165+166166+let parse_fragment_length buf =
167167+ if String.length buf <> 1 then
168168+ raise_trailing_bytes "fragment length"
169169+ else
170170+ int_to_max_fragment_length (String.get_uint8 buf 0)
171171+172172+let parse_supported_version buf =
173173+ parse_any_version_opt buf
174174+175175+let parse_supported_versions buf =
176176+ let len = String.get_uint8 buf 0 in
177177+ if len mod 2 <> 0 then
178178+ raise_wrong_length "supported versions"
179179+ else
180180+ parse_count_list parse_supported_version (shift buf 1) [] (len / 2)
181181+182182+let parse_named_group buf =
183183+ let typ = String.get_uint16_be buf 0 in
184184+ (int_to_named_group typ, shift buf 2)
185185+186186+let parse_group buf =
187187+ match parse_named_group buf with
188188+ | Some x, buf -> (named_group_to_group x, buf)
189189+ | None, buf -> (None, buf)
190190+191191+let parse_supported_groups buf =
192192+ let count = String.get_uint16_be buf 0 in
193193+ if count mod 2 <> 0 then
194194+ raise_wrong_length "elliptic curve list"
195195+ else
196196+ let cs, rt = parse_count_list parse_named_group (shift buf 2) [] (count / 2) in
197197+ if String.length rt <> 0 then
198198+ raise_trailing_bytes "elliptic curves"
199199+ else
200200+ cs
201201+202202+let parse_signature_algorithm buf =
203203+ match int_to_signature_alg (String.get_uint16_be buf 0) with
204204+ | Some sig_alg -> of_signature_alg sig_alg
205205+ | _ -> None
206206+207207+let parse_signature_algorithms buf =
208208+ let parsef buf = parse_signature_algorithm buf, shift buf 2 in
209209+ let count = String.get_uint16_be buf 0 in
210210+ if count mod 2 <> 0 then
211211+ raise_wrong_length "signature hash"
212212+ else
213213+ parse_count_list parsef (shift buf 2) [] (count / 2)
214214+215215+let parse_alpn_protocol raw =
216216+ let length = String.get_uint8 raw 0 in
217217+ let protocol = String.sub raw 1 length in
218218+ (Some protocol, shift raw (1 + length))
219219+220220+let parse_alpn_protocols buf =
221221+ let len = String.get_uint16_be buf 0 in
222222+ if String.length buf <> len + 2 then
223223+ raise_trailing_bytes "alpn"
224224+ else
225225+ parse_list parse_alpn_protocol (String.sub buf 2 len) []
226226+227227+let parse_ec_point_format buf =
228228+ (* this is deprecated, we only check that uncompressed (typ 0) is present *)
229229+ let data = String.get_uint8 buf 0 in
230230+ Some (data = 0), shift buf 1
231231+232232+let parse_ec_point_formats buf =
233233+ let count = String.get_uint8 buf 0 in
234234+ parse_count_list parse_ec_point_format (shift buf 1) [] count
235235+236236+let parse_extension buf = function
237237+ | MAX_FRAGMENT_LENGTH ->
238238+ (match parse_fragment_length buf with
239239+ | Some mfl -> `MaxFragmentLength mfl
240240+ | None -> raise_unknown "maximum fragment length")
241241+ | RENEGOTIATION_INFO ->
242242+ let len' = String.get_uint8 buf 0 in
243243+ if String.length buf <> len' + 1 then
244244+ raise_trailing_bytes "renegotiation"
245245+ else
246246+ `SecureRenegotiation (String.sub buf 1 len')
247247+ | EXTENDED_MASTER_SECRET ->
248248+ if String.length buf > 0 then
249249+ raise_trailing_bytes "extended master secret"
250250+ else
251251+ `ExtendedMasterSecret
252252+ | EC_POINT_FORMATS ->
253253+ let formats, rt = parse_ec_point_formats buf in
254254+ if String.length rt <> 0 then
255255+ raise_trailing_bytes "ec point formats"
256256+ else if List.mem true formats then
257257+ `ECPointFormats
258258+ else
259259+ raise_unknown "EC Point Formats without uncompressed"
260260+ | x -> `UnknownExtension (extension_type_to_int x, buf)
261261+262262+let parse_keyshare_entry buf =
263263+ let parse_share data =
264264+ let size = String.get_uint16_be data 0 in
265265+ split_str ~start:2 data size
266266+ in
267267+ let g, rest = parse_named_group buf in
268268+ let share, left = parse_share rest in
269269+ match g with
270270+ | None -> None, left
271271+ | Some g -> Some (g, share), left
272272+273273+let parse_id buf =
274274+ let id_len = String.get_uint16_be buf 0 in
275275+ if id_len = 0 then (* id must be non-empty! *)
276276+ raise_wrong_length "PSK id is empty"
277277+ else
278278+ let age = String.get_int32_be buf (id_len + 2) in
279279+ (Some (String.sub buf 2 id_len, age), shift buf (id_len + 6))
280280+281281+let parse_binder buf =
282282+ let l = String.get_uint8 buf 0 in
283283+ Some (String.sub buf 1 l), shift buf (l + 1)
284284+285285+let parse_client_presharedkeys buf =
286286+ let id_len = String.get_uint16_be buf 0 in
287287+ let identities = parse_list parse_id (String.sub buf 2 id_len) [] in
288288+ let binders_len = String.get_uint16_be buf (id_len + 2) in
289289+ let binders = parse_list parse_binder (String.sub buf (4 + id_len) binders_len) [] in
290290+ let id_binder = List.combine identities binders in
291291+ if String.length buf <> 4 + binders_len + id_len then
292292+ raise_trailing_bytes "psk"
293293+ else
294294+ id_binder
295295+296296+let parse_cookie buf =
297297+ let len = String.get_uint16_be buf 0 in
298298+ (String.sub buf 2 len, shift buf (2 + len))
299299+300300+let parse_psk_key_exchange_mode buf =
301301+ let data = String.get_uint8 buf 0 in
302302+ (int_to_psk_key_exchange_mode data, shift buf 1)
303303+304304+let parse_psk_key_exchange_modes buf =
305305+ let count = String.get_uint8 buf 0 in
306306+ parse_count_list parse_psk_key_exchange_mode (shift buf 1) [] count
307307+308308+let parse_ext raw =
309309+ let etype = String.get_uint16_be raw 0
310310+ and length = String.get_uint16_be raw 2
311311+ in
312312+ (etype, length, String.sub raw 4 length)
313313+314314+let parse_client_extension raw =
315315+ let etype, len, buf = parse_ext raw in
316316+ let data =
317317+ match int_to_extension_type etype with
318318+ | Some SERVER_NAME ->
319319+ (match parse_hostnames buf with
320320+ | [name] ->
321321+ (match Domain_name.of_string name with
322322+ | Error (`Msg err) ->
323323+ raise_unknown ("unable to canonicalize " ^ name ^ "into a domain name: " ^ err)
324324+ | Ok domain_name ->
325325+ (match Domain_name.host domain_name with
326326+ | Error (`Msg err) ->
327327+ raise_unknown ("unable to build a hostname from " ^ name ^ ": " ^ err)
328328+ | Ok hostname -> `Hostname hostname))
329329+ | _ -> raise_unknown "bad server name indication (multiple names)")
330330+ | Some SUPPORTED_GROUPS ->
331331+ let gs = parse_supported_groups buf in
332332+ `SupportedGroups gs
333333+ | Some PADDING ->
334334+ let rec check = function
335335+ | 0 -> `Padding len
336336+ | n -> let idx = pred n in
337337+ if String.get_uint8 buf idx <> 0 then
338338+ raise_unknown "bad padding in padding extension"
339339+ else
340340+ check idx
341341+ in
342342+ check len
343343+ | Some SIGNATURE_ALGORITHMS ->
344344+ let algos, rt = parse_signature_algorithms buf in
345345+ if String.length rt <> 0 then
346346+ raise_trailing_bytes "signature algorithms"
347347+ else
348348+ `SignatureAlgorithms algos
349349+ | Some APPLICATION_LAYER_PROTOCOL_NEGOTIATION ->
350350+ let protocols = parse_alpn_protocols buf in
351351+ `ALPN protocols
352352+ | Some KEY_SHARE ->
353353+ let ll = String.get_uint16_be buf 0 in
354354+ if ll + 2 <> String.length buf then
355355+ raise_unknown "bad key share extension"
356356+ else
357357+ let shares = parse_list parse_keyshare_entry (String.sub buf 2 ll) [] in
358358+ `KeyShare shares
359359+ | Some PRE_SHARED_KEY ->
360360+ let ids = parse_client_presharedkeys buf in
361361+ `PreSharedKeys ids
362362+ | Some EARLY_DATA ->
363363+ if String.length buf <> 0 then
364364+ raise_trailing_bytes "early data"
365365+ else
366366+ `EarlyDataIndication
367367+ | Some SUPPORTED_VERSIONS ->
368368+ let versions, rt = parse_supported_versions buf in
369369+ if String.length rt <> 0 then
370370+ raise_trailing_bytes "supported versions"
371371+ else
372372+ `SupportedVersions versions
373373+ | Some POST_HANDSHAKE_AUTH ->
374374+ if String.length buf = 0 then
375375+ `PostHandshakeAuthentication
376376+ else
377377+ raise_unknown "non-empty post handshake authentication"
378378+ | Some COOKIE ->
379379+ let c, rt = parse_cookie buf in
380380+ if String.length rt <> 0 then
381381+ raise_trailing_bytes "cookie"
382382+ else
383383+ `Cookie c
384384+ | Some PSK_KEY_EXCHANGE_MODES ->
385385+ let modes, rt = parse_psk_key_exchange_modes buf in
386386+ if String.length rt <> 0 then
387387+ raise_trailing_bytes "psk key exchange modes"
388388+ else
389389+ `PskKeyExchangeModes modes
390390+ | Some x -> parse_extension buf x
391391+ | None -> `UnknownExtension (etype, buf)
392392+ in
393393+ (Some data, shift raw (4 + len))
394394+395395+let parse_server_extension raw =
396396+ let etype, len, buf = parse_ext raw in
397397+ let data =
398398+ match int_to_extension_type etype with
399399+ | Some SERVER_NAME ->
400400+ (match parse_hostnames buf with
401401+ | [] -> `Hostname
402402+ | _ -> raise_unknown "bad server name indication (multiple names)")
403403+ | Some KEY_SHARE ->
404404+ (match parse_keyshare_entry buf with
405405+ | _, xs when String.length xs <> 0 -> raise_trailing_bytes "server keyshare"
406406+ | None, _ -> raise_unknown "keyshare entry"
407407+ | Some (g, ks), _ ->
408408+ match named_group_to_group g with
409409+ | Some g -> `KeyShare (g, ks)
410410+ | None -> raise_unknown "keyshare entry")
411411+ | Some PRE_SHARED_KEY ->
412412+ if String.length buf <> 2 then
413413+ raise_trailing_bytes "server pre_shared_key"
414414+ else
415415+ `PreSharedKey (String.get_uint16_be buf 0)
416416+ | Some SUPPORTED_GROUPS | Some SIGNATURE_ALGORITHMS | Some PADDING ->
417417+ raise_unknown "invalid extension in server hello!"
418418+ | Some APPLICATION_LAYER_PROTOCOL_NEGOTIATION ->
419419+ (match parse_alpn_protocols buf with
420420+ | [protocol] -> `ALPN protocol
421421+ | _ -> raise_unknown "bad ALPN (none or multiple names)")
422422+ | Some SUPPORTED_VERSIONS ->
423423+ let version = parse_version_exn buf in
424424+ `SelectedVersion version
425425+ | Some x -> parse_extension buf x
426426+ | None -> `UnknownExtension (etype, buf)
427427+ in
428428+ (Some data, shift raw (4 + len))
429429+430430+let parse_encrypted_extension raw =
431431+ let etype, len, buf = parse_ext raw in
432432+ let data =
433433+ match int_to_extension_type etype with
434434+ | Some SERVER_NAME ->
435435+ (match parse_hostnames buf with
436436+ | [] -> `Hostname
437437+ | _ -> raise_unknown "bad server name indication (multiple names)")
438438+ | Some SUPPORTED_GROUPS ->
439439+ let gs = parse_supported_groups buf in
440440+ let supported = List.filter_map named_group_to_group gs in
441441+ `SupportedGroups supported
442442+ | Some APPLICATION_LAYER_PROTOCOL_NEGOTIATION ->
443443+ (match parse_alpn_protocols buf with
444444+ | [protocol] -> `ALPN protocol
445445+ | _ -> raise_unknown "bad ALPN (none or multiple names)")
446446+ | Some EARLY_DATA ->
447447+ if String.length buf <> 0 then
448448+ raise_trailing_bytes "server early_data"
449449+ else
450450+ `EarlyDataIndication
451451+ | Some x -> raise_unknown ("bad encrypted extension " ^ (extension_type_to_string x)) (* TODO maybe unknown instead? *)
452452+ | None -> `UnknownExtension (etype, buf)
453453+ in
454454+ (Some data, shift raw (4 + len))
455455+456456+let parse_retry_extension raw =
457457+ let etype, len, buf = parse_ext raw in
458458+ let data =
459459+ match int_to_extension_type etype with
460460+ | Some KEY_SHARE ->
461461+ begin
462462+ let group, rt = parse_group buf in
463463+ if String.length rt <> 0 then
464464+ raise_trailing_bytes "key share"
465465+ else
466466+ match group with
467467+ | None -> raise_unknown "unknown group in key share"
468468+ | Some g -> `SelectedGroup g
469469+ end
470470+ | Some SUPPORTED_VERSIONS ->
471471+ let version = parse_version_exn buf in
472472+ `SelectedVersion version
473473+ | Some COOKIE ->
474474+ let c, rt = parse_cookie buf in
475475+ if String.length rt <> 0 then
476476+ raise_trailing_bytes "cookie"
477477+ else
478478+ `Cookie c
479479+ | _ -> `UnknownExtension (etype, buf)
480480+ in
481481+ (Some data, shift raw (4 + len))
482482+483483+let parse_extensions parse_ext buf =
484484+ let len = String.get_uint16_be buf 0 in
485485+ if String.length buf <> len + 2 then
486486+ raise_trailing_bytes "extensions"
487487+ else
488488+ parse_list parse_ext (String.sub buf 2 len) []
489489+490490+let parse_client_hello buf =
491491+ let client_version = parse_any_version_exn buf in
492492+ let client_random = String.sub buf 2 32 in
493493+ let slen = String.get_uint8 buf 34 in
494494+ let sessionid = if slen = 0 then None else Some (String.sub buf 35 slen) in
495495+ let ciphersuites, rt = parse_any_ciphersuites (shift buf (35 + slen)) in
496496+ let _, rt' = parse_compression_methods rt in
497497+ let extensions =
498498+ if String.length rt' = 0 then [] else parse_extensions parse_client_extension rt'
499499+ in
500500+ (* TLS 1.3 mandates PreSharedKeys to be the last extension *)
501501+ (if List.exists (function `PreSharedKeys _ -> true | _ -> false) extensions then
502502+ match List.rev extensions with
503503+ | `PreSharedKeys _::_ -> ()
504504+ | _ -> raise_unknown "Pre-shared key extension exists, but is not the last");
505505+ ClientHello { client_version ; client_random ; sessionid ; ciphersuites ; extensions }
506506+507507+let parse_server_hello buf =
508508+ let server_version = parse_version_exn buf in
509509+ let server_random = String.sub buf 2 32 in
510510+ let slen = String.get_uint8 buf 34 in
511511+ let sessionid = if slen = 0 then None else Some (String.sub buf 35 slen) in
512512+ let ciphersuite, rt = match parse_ciphersuite (shift buf (35 + slen)) with
513513+ | Some x, buf' -> (x, buf')
514514+ | None , _ -> raise_unknown "ciphersuite"
515515+ in
516516+ let rt' = match parse_compression_method rt with
517517+ | Some NULL, buf' -> buf'
518518+ | None , _ -> raise_unknown "compression method"
519519+ in
520520+ (* depending on the content of the server_random we have to diverge in behaviour *)
521521+ if String.equal server_random helloretryrequest then begin
522522+ (* hello retry request, TODO: verify compression=empty *)
523523+ match Ciphersuite.ciphersuite_to_ciphersuite13 ciphersuite with
524524+ | None -> raise_unknown "unsupported ciphersuite in hello retry request"
525525+ | Some ciphersuite ->
526526+ let extensions =
527527+ if String.length rt' = 0 then [] else parse_extensions parse_retry_extension rt'
528528+ in
529529+ let retry_version =
530530+ match Utils.map_find ~f:(function `SelectedVersion v -> Some v | _ -> None) extensions with
531531+ | None -> server_version
532532+ | Some v -> v
533533+ in
534534+ let selected_group =
535535+ match Utils.map_find ~f:(function `SelectedGroup g -> Some g | _ -> None) extensions with
536536+ | None -> raise_unknown "unknown selected group"
537537+ | Some g -> g
538538+ in
539539+ HelloRetryRequest { retry_version ; sessionid ; ciphersuite ; selected_group ; extensions }
540540+ end else begin
541541+ let extensions =
542542+ if String.length rt' = 0 then [] else parse_extensions parse_server_extension rt'
543543+ in
544544+ let server_version =
545545+ match Utils.map_find ~f:(function `SelectedVersion v -> Some v | _ -> None) extensions with
546546+ | None -> server_version
547547+ | Some v -> v
548548+ in
549549+ ServerHello { server_version ; server_random ; sessionid ; ciphersuite ; extensions }
550550+ end
551551+552552+let parse_certificates_exn buf =
553553+ let parsef buf =
554554+ let len = get_uint24_len ~off:0 buf in
555555+ (Some (String.sub buf 3 len), shift buf (len + 3))
556556+ in
557557+ let len = get_uint24_len ~off:0 buf in
558558+ if String.length buf <> len + 3 then
559559+ raise_trailing_bytes "certificates"
560560+ else
561561+ parse_list parsef (String.sub buf 3 len) []
562562+563563+let parse_certificates = catch @@ parse_certificates_exn
564564+565565+(* TODO finish implementation of certificate extensions *)
566566+let parse_certificate_ext _ = None, ""
567567+568568+let parse_certificate_ext_1_3_exn buf =
569569+ let certlen = get_uint24_len ~off:0 buf in
570570+ let cert, extbuf, rest =
571571+ let cert, rt = split_str ~start:3 buf certlen in
572572+ let ext_len = String.get_uint16_be rt 0 in
573573+ let extbuf, rt = split_str ~start:2 rt ext_len in
574574+ cert, extbuf, rt
575575+ in
576576+ let exts = parse_list parse_certificate_ext extbuf [] in
577577+ (Some (cert, exts), rest)
578578+579579+let parse_certificate_ext_list_1_3_exn buf =
580580+ let len = get_uint24_len ~off:0 buf in
581581+ if String.length buf <> len + 3 then
582582+ raise_trailing_bytes "certificates"
583583+ else
584584+ parse_list parse_certificate_ext_1_3_exn (shift buf 3) []
585585+586586+let parse_certificates_1_3_exn buf =
587587+ let clen = String.get_uint8 buf 0 in
588588+ let context, rt = split_str ~start:1 buf clen in
589589+ let certs = parse_certificate_ext_list_1_3_exn rt in
590590+ (context, certs)
591591+592592+let parse_certificates_1_3 = catch @@ parse_certificates_1_3_exn
593593+594594+let parse_certificate_types buf =
595595+ let parsef buf =
596596+ let byte = String.get_uint8 buf 0 in
597597+ (int_to_client_certificate_type byte, shift buf 1)
598598+ in
599599+ let count = String.get_uint8 buf 0 in
600600+ parse_count_list parsef (shift buf 1) [] count
601601+602602+let parse_cas buf =
603603+ let parsef buf =
604604+ let length = String.get_uint16_be buf 0 in
605605+ let name = String.sub buf 2 length in
606606+ (Some name, shift buf (2 + length))
607607+ in
608608+ let calength = String.get_uint16_be buf 0 in
609609+ let cas, rt = split_str ~start:2 buf calength in
610610+ (parse_list parsef cas [], rt)
611611+612612+let parse_certificate_request_exn buf =
613613+ let certificate_types, buf' = parse_certificate_types buf in
614614+ let certificate_authorities, buf' = parse_cas buf' in
615615+ if String.length buf' <> 0 then
616616+ raise_trailing_bytes "certificate request"
617617+ else
618618+ (certificate_types, certificate_authorities)
619619+620620+let parse_certificate_request =
621621+ catch parse_certificate_request_exn
622622+623623+let parse_certificate_request_1_2_exn buf =
624624+ let certificate_types, buf' = parse_certificate_types buf in
625625+ let sigs, buf' = parse_signature_algorithms buf' in
626626+ let cas, buf' = parse_cas buf' in
627627+ if String.length buf' <> 0 then
628628+ raise_trailing_bytes "certificate request"
629629+ else
630630+ (certificate_types, sigs, cas)
631631+632632+let parse_certificate_request_1_2 =
633633+ catch parse_certificate_request_1_2_exn
634634+635635+let parse_certificate_request_extension raw =
636636+ let etype, len, buf = parse_ext raw in
637637+ let data = match int_to_extension_type etype with
638638+ | Some SIGNATURE_ALGORITHMS ->
639639+ let algos, rt = parse_signature_algorithms buf in
640640+ if String.length rt <> 0 then
641641+ raise_trailing_bytes "signature algorithms"
642642+ else
643643+ `SignatureAlgorithms algos
644644+ | Some CERTIFICATE_AUTHORITIES ->
645645+ let cas, rt = parse_cas buf in
646646+ if String.length rt <> 0 then
647647+ raise_trailing_bytes "certificate authorities"
648648+ else
649649+ let cas = List.fold_left (fun cas buf ->
650650+ match X509.Distinguished_name.decode_der buf with
651651+ | Ok ca -> ca :: cas
652652+ | Error _ -> cas)
653653+ [] cas
654654+ in
655655+ `CertificateAuthorities (List.rev cas)
656656+ | _ -> `UnknownExtension (etype, buf)
657657+ in
658658+ (Some data, shift raw (4 + len))
659659+660660+let parse_certificate_request_1_3_exn buf =
661661+ let contextlen = String.get_uint8 buf 0 in
662662+ let context, rt =
663663+ if contextlen = 0 then
664664+ None, shift buf 1
665665+ else
666666+ let ctx, rest = split_str ~start:1 buf contextlen in
667667+ Some ctx, rest
668668+ in
669669+ let exts = parse_extensions parse_certificate_request_extension rt in
670670+ (context, exts)
671671+672672+let parse_certificate_request_1_3 =
673673+ catch parse_certificate_request_1_3_exn
674674+675675+let parse_dh_parameters = catch @@ fun raw ->
676676+ let plength = String.get_uint16_be raw 0 in
677677+ let dh_p = String.sub raw 2 plength in
678678+ let buf = shift raw (2 + plength) in
679679+ let glength = String.get_uint16_be buf 0 in
680680+ let dh_g = String.sub buf 2 glength in
681681+ let buf = shift buf (2 + glength) in
682682+ let yslength = String.get_uint16_be buf 0 in
683683+ let dh_Ys = String.sub buf 2 yslength in
684684+ let buf = shift buf (2 + yslength) in
685685+ let rawparams = String.sub raw 0 (plength + glength + yslength + 6) in
686686+ ({ dh_p ; dh_g ; dh_Ys }, rawparams, buf)
687687+688688+let parse_ec_parameters = catch @@ fun raw ->
689689+ if String.get_uint8 raw 0 <> ec_curve_type_to_int NAMED_CURVE then
690690+ raise_unknown "EC curve type"
691691+ else
692692+ match int_to_named_group (String.get_uint16_be raw 1) with
693693+ | Some g ->
694694+ begin match named_group_to_group g with
695695+ | Some ((`X25519 | `P256 | `P384 | `P521) as g) ->
696696+ let data_len = String.get_uint8 raw 3 in
697697+ let d, rest = split_str ~start:4 raw data_len in
698698+ g, d, String.sub raw 0 (data_len + 4), rest
699699+ | _ -> raise_unknown "EC group"
700700+ end
701701+ | None -> raise_unknown "EC named group"
702702+703703+let parse_digitally_signed_exn buf =
704704+ let siglen = String.get_uint16_be buf 0 in
705705+ if String.length buf <> siglen + 2 then
706706+ raise_trailing_bytes "digitally signed"
707707+ else
708708+ String.sub buf 2 siglen
709709+710710+let parse_digitally_signed =
711711+ catch parse_digitally_signed_exn
712712+713713+let parse_digitally_signed_1_2 = catch @@ fun buf ->
714714+ match parse_signature_algorithm buf with
715715+ | Some sig_alg ->
716716+ let signature = parse_digitally_signed_exn (shift buf 2) in
717717+ (sig_alg, signature)
718718+ | None -> raise_unknown "hash or signature algorithm"
719719+720720+let parse_session_ticket_extension raw =
721721+ let etype, len, buf = parse_ext raw in
722722+ let data = match int_to_extension_type etype with
723723+ | Some EARLY_DATA ->
724724+ if String.length buf <> 4 then
725725+ raise_unknown "bad early_data extension in session ticket"
726726+ else
727727+ let size = String.get_int32_be buf 0 in
728728+ `EarlyDataIndication size
729729+ | _ -> `UnknownExtension (etype, buf)
730730+ in
731731+ (Some data, shift raw (4 + len))
732732+733733+let parse_session_ticket buf =
734734+ let lifetime = String.get_int32_be buf 0
735735+ and age_add = String.get_int32_be buf 4
736736+ and nonce_len = String.get_uint8 buf 8
737737+ in
738738+ let nonce = String.sub buf 9 nonce_len in
739739+ let ticket_len = String.get_uint16_be buf (9 + nonce_len) in
740740+ let ticket, exts_buf = split_str ~start:(11 + nonce_len) buf ticket_len in
741741+ let extensions = parse_extensions parse_session_ticket_extension exts_buf in
742742+ { lifetime ; age_add ; nonce ; ticket ; extensions }
743743+744744+let parse_client_dh_key_exchange_exn buf =
745745+ let len = String.get_uint16_be buf 0 in
746746+ if String.length buf <> len + 2 then
747747+ raise_trailing_bytes "client key exchange"
748748+ else
749749+ String.sub buf 2 len
750750+751751+let parse_client_dh_key_exchange = catch parse_client_dh_key_exchange_exn
752752+753753+let parse_client_ec_key_exchange_exn buf =
754754+ let len = String.get_uint8 buf 0 in
755755+ if String.length buf <> len + 1 then
756756+ raise_trailing_bytes "client key exchange"
757757+ else
758758+ String.sub buf 1 len
759759+760760+let parse_client_ec_key_exchange = catch parse_client_ec_key_exchange_exn
761761+762762+let parse_keyupdate buf =
763763+ if String.length buf <> 1 then
764764+ raise_trailing_bytes "key update"
765765+ else
766766+ match int_to_key_update_request_type (String.get_uint8 buf 0) with
767767+ | Some y -> y
768768+ | None -> raise_unknown "key update content"
769769+770770+let parse_handshake_frame buf =
771771+ if String.length buf < 4 then
772772+ (None, buf)
773773+ else
774774+ let l = get_uint24_len ~off:1 buf in
775775+ let hslen = l + 4 in
776776+ if String.length buf >= hslen then
777777+ let hs, rest = split_str buf hslen in
778778+ (Some hs, rest)
779779+ else
780780+ (None, buf)
781781+782782+let parse_handshake = catch @@ fun buf ->
783783+ let typ = String.get_uint8 buf 0 in
784784+ let handshake_type = int_to_handshake_type typ in
785785+ let len = get_uint24_len ~off:1 buf in
786786+ if String.length buf <> len + 4 then
787787+ raise_trailing_bytes "handshake"
788788+ else
789789+ let payload = String.sub buf 4 len in
790790+ match handshake_type with
791791+ | Some HELLO_REQUEST ->
792792+ if String.length payload = 0 then HelloRequest else raise_trailing_bytes "hello request"
793793+ | Some CLIENT_HELLO -> parse_client_hello payload
794794+ | Some SERVER_HELLO -> parse_server_hello payload
795795+ | Some CERTIFICATE -> Certificate payload
796796+ | Some CERTIFICATE_VERIFY -> CertificateVerify payload
797797+ | Some SERVER_KEY_EXCHANGE -> ServerKeyExchange payload
798798+ | Some SERVER_HELLO_DONE ->
799799+ if String.length payload = 0 then ServerHelloDone else raise_trailing_bytes "server hello done"
800800+ | Some CERTIFICATE_REQUEST -> CertificateRequest payload
801801+ | Some CLIENT_KEY_EXCHANGE -> ClientKeyExchange payload
802802+ | Some FINISHED -> Finished payload
803803+ | Some ENCRYPTED_EXTENSIONS ->
804804+ let ee = parse_extensions parse_encrypted_extension payload in
805805+ EncryptedExtensions ee
806806+ | Some KEY_UPDATE ->
807807+ let ku = parse_keyupdate payload in
808808+ KeyUpdate ku
809809+ | Some SESSION_TICKET ->
810810+ let ticket = parse_session_ticket payload in
811811+ SessionTicket ticket
812812+ | Some END_OF_EARLY_DATA ->
813813+ EndOfEarlyData
814814+ | Some _
815815+ | None -> raise_unknown @@ "handshake type" ^ string_of_int typ
+31
vendor/opam/tls/lib/reader.mli
···11+22+val parse_version : string -> (Core.tls_version, [> `Decode of string ]) result
33+val parse_any_version : string -> (Core.tls_any_version, [> `Decode of string ]) result
44+val parse_record : string ->
55+ ([ `Record of (Core.tls_hdr * string) * string
66+ | `Fragment of string
77+ ], [> `Unexpected of [> `Content_type of int ]
88+ | `Protocol_version of [> `Unknown_record of int * int ]
99+ | `Record_overflow of int ]) result
1010+1111+val parse_handshake_frame : string -> (string option * string)
1212+val parse_handshake : string -> (Core.tls_handshake, [> `Decode of string ]) result
1313+1414+val parse_alert : string -> (Core.tls_alert, [> `Decode of string ]) result
1515+1616+val parse_change_cipher_spec : string -> (unit, [> `Decode of string ]) result
1717+1818+val parse_certificate_request : string -> (Packet.client_certificate_type list * string list, [> `Decode of string ]) result
1919+val parse_certificate_request_1_2 : string -> (Packet.client_certificate_type list * Core.signature_algorithm list * string list, [> `Decode of string ]) result
2020+val parse_certificate_request_1_3 : string -> (string option * Core.certificate_request_extension list, [> `Decode of string ]) result
2121+2222+val parse_certificates : string -> (string list, [> `Decode of string ]) result
2323+val parse_certificates_1_3 : string -> (string * (string * 'a list) list, [> `Decode of string ]) result
2424+2525+val parse_client_dh_key_exchange : string -> (string, [> `Decode of string ]) result
2626+val parse_client_ec_key_exchange : string -> (string, [> `Decode of string ]) result
2727+2828+val parse_dh_parameters : string -> (Core.dh_parameters * string * string, [> `Decode of string ]) result
2929+val parse_ec_parameters : string -> ([ `X25519 | `P256 | `P384 | `P521 ] * string * string * string, [> `Decode of string ]) result
3030+val parse_digitally_signed : string -> (string, [> `Decode of string ]) result
3131+val parse_digitally_signed_1_2 : string -> (Core.signature_algorithm * string, [> `Decode of string ]) result
+352
vendor/opam/tls/lib/state.ml
···11+(* Defines all high-level datatypes for the TLS library. It is opaque to clients
22+ of this library, and only used from within the library. *)
33+44+open Core
55+open Mirage_crypto
66+77+type hmac_key = string
88+99+(* initialisation vector style, depending on TLS version *)
1010+type iv_mode =
1111+ | Iv of string (* traditional CBC (reusing last cipherblock) *)
1212+ | Random_iv (* TLS 1.1 and higher explicit IV (we use random) *)
1313+1414+type 'k cbc_cipher = (module Block.CBC with type key = 'k)
1515+type 'k cbc_state = {
1616+ cipher : 'k cbc_cipher ;
1717+ cipher_secret : 'k ;
1818+ iv_mode : iv_mode ;
1919+ hmac : Digestif.hash' ;
2020+ hmac_secret : hmac_key
2121+}
2222+2323+type nonce = string
2424+2525+type 'k aead_cipher = (module AEAD with type key = 'k)
2626+type 'k aead_state = {
2727+ cipher : 'k aead_cipher ;
2828+ cipher_secret : 'k ;
2929+ nonce : nonce ;
3030+ explicit_nonce : bool ; (* RFC 7905: no explicit nonce, instead TLS 1.3 construction is adapted *)
3131+3232+}
3333+3434+(* state of a symmetric cipher *)
3535+type cipher_st =
3636+ | CBC : 'k cbc_state -> cipher_st
3737+ | AEAD : 'k aead_state -> cipher_st
3838+3939+(* context of a TLS connection (both in and out has each one of these) *)
4040+type crypto_context = {
4141+ sequence : int64 ; (* sequence number *)
4242+ cipher_st : cipher_st ; (* cipher state *)
4343+}
4444+(* the raw handshake log we need to carry around *)
4545+type hs_log = string list
4646+4747+type dh_secret = [
4848+ | `Finite_field of Mirage_crypto_pk.Dh.secret
4949+ | `P256 of Mirage_crypto_ec.P256.Dh.secret
5050+ | `P384 of Mirage_crypto_ec.P384.Dh.secret
5151+ | `P521 of Mirage_crypto_ec.P521.Dh.secret
5252+ | `X25519 of Mirage_crypto_ec.X25519.secret
5353+]
5454+5555+(* a collection of client and server verify bytes for renegotiation *)
5656+type reneg_params = string * string
5757+5858+type common_session_data = {
5959+ server_random : string ; (* 32 bytes random from the server hello *)
6060+ client_random : string ; (* 32 bytes random from the client hello *)
6161+ peer_certificate_chain : X509.Certificate.t list ;
6262+ peer_certificate : X509.Certificate.t option ;
6363+ trust_anchor : X509.Certificate.t option ;
6464+ received_certificates : X509.Certificate.t list ;
6565+ own_certificate : X509.Certificate.t list ;
6666+ own_private_key : X509.Private_key.t option ;
6767+ own_name : [`host] Domain_name.t option ;
6868+ client_auth : bool ;
6969+ master_secret : master_secret ;
7070+ alpn_protocol : string option ; (* selected alpn protocol after handshake *)
7171+}
7272+7373+type session_data = {
7474+ common_session_data : common_session_data ;
7575+ client_version : tls_any_version ; (* version in client hello (needed in RSA client key exchange) *)
7676+ ciphersuite : Ciphersuite.ciphersuite ;
7777+ group : group option ;
7878+ renegotiation : reneg_params ; (* renegotiation data *)
7979+ session_id : string ;
8080+ extended_ms : bool ;
8181+ tls_unique : string ;
8282+}
8383+8484+(* state machine of the server *)
8585+type server_handshake_state =
8686+ | AwaitClientHello (* initial state *)
8787+ | AwaitClientHelloRenegotiate
8888+ | AwaitClientCertificate_RSA of session_data * hs_log
8989+ | AwaitClientCertificate_DHE of session_data * dh_secret * hs_log
9090+ | AwaitClientKeyExchange_RSA of session_data * hs_log (* server hello done is sent, and RSA key exchange used, waiting for a client key exchange message *)
9191+ | 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 *)
9292+ | AwaitClientCertificateVerify of session_data * crypto_context * crypto_context * hs_log
9393+ | AwaitClientChangeCipherSpec of session_data * crypto_context * crypto_context * hs_log (* client key exchange received, next should be change cipher spec *)
9494+ | AwaitClientChangeCipherSpecResume of session_data * crypto_context * string * hs_log (* resumption: next should be change cipher spec *)
9595+ | AwaitClientFinished of session_data * hs_log (* change cipher spec received, next should be the finished including a hmac over all handshake packets *)
9696+ | AwaitClientFinishedResume of session_data * string * hs_log (* change cipher spec received, next should be the finished including a hmac over all handshake packets *)
9797+ | Established (* handshake successfully completed *)
9898+9999+(* state machine of the client *)
100100+type client_handshake_state =
101101+ | ClientInitial (* initial state *)
102102+ | AwaitServerHello of client_hello * (group * dh_secret) list * hs_log (* client hello is sent, handshake_params are half-filled *)
103103+ | AwaitServerHelloRenegotiate of session_data * client_hello * hs_log (* client hello is sent, handshake_params are half-filled *)
104104+ | AwaitCertificate_RSA of session_data * hs_log (* certificate expected with RSA key exchange *)
105105+ | AwaitCertificate_DHE of session_data * hs_log (* certificate expected with DHE key exchange *)
106106+ | AwaitServerKeyExchange_DHE of session_data * hs_log (* server key exchange expected with DHE *)
107107+ | AwaitCertificateRequestOrServerHelloDone of session_data * string * string * hs_log (* server hello done expected, client key exchange and premastersecret are ready *)
108108+ | AwaitServerHelloDone of session_data * signature_algorithm list option * string * string * hs_log (* server hello done expected, client key exchange and premastersecret are ready *)
109109+ | AwaitServerChangeCipherSpec of session_data * crypto_context * string * hs_log (* change cipher spec expected *)
110110+ | AwaitServerChangeCipherSpecResume of session_data * crypto_context * crypto_context * hs_log (* change cipher spec expected *)
111111+ | AwaitServerFinished of session_data * string * hs_log (* finished expected with a hmac over all handshake packets *)
112112+ | AwaitServerFinishedResume of session_data * hs_log (* finished expected with a hmac over all handshake packets *)
113113+ | Established (* handshake successfully completed *)
114114+115115+type kdf = {
116116+ secret : string ;
117117+ cipher : Ciphersuite.ciphersuite13 ;
118118+ hash : Digestif.hash' ;
119119+}
120120+121121+(* TODO needs log of CH..CF for post-handshake auth *)
122122+(* TODO drop master_secret!? *)
123123+type session_data13 = {
124124+ common_session_data13 : common_session_data ;
125125+ ciphersuite13 : Ciphersuite.ciphersuite13 ;
126126+ master_secret : kdf ;
127127+ exporter_master_secret : string ;
128128+ resumption_secret : string ;
129129+ state : epoch_state ;
130130+ resumed : bool ;
131131+ client_app_secret : string ;
132132+ server_app_secret : string ;
133133+}
134134+135135+type client13_handshake_state =
136136+ | AwaitServerHello13 of client_hello * (group * dh_secret) list * string (* this is for CH1 ~> HRR ~> CH2 <~ WAIT SH *)
137137+ | AwaitServerEncryptedExtensions13 of session_data13 * string * string * string
138138+ | AwaitServerCertificateRequestOrCertificate13 of session_data13 * string * string * string
139139+ | AwaitServerCertificate13 of session_data13 * string * string * signature_algorithm list option * string
140140+ | AwaitServerCertificateVerify13 of session_data13 * string * string * signature_algorithm list option * string
141141+ | AwaitServerFinished13 of session_data13 * string * string * signature_algorithm list option * string
142142+ | Established13
143143+144144+type server13_handshake_state =
145145+ | AwaitClientHelloHRR13 (* if we sent out HRR (also to-be-used for tls13-only) *)
146146+ | AwaitClientCertificate13 of session_data13 * string * crypto_context * session_ticket option * string
147147+ | AwaitClientCertificateVerify13 of session_data13 * string * crypto_context * session_ticket option * string
148148+ | AwaitClientFinished13 of string * crypto_context * session_ticket option * string
149149+ | AwaitEndOfEarlyData13 of string * crypto_context * crypto_context * session_ticket option * string
150150+ | Established13
151151+152152+type handshake_machina_state =
153153+ | Client of client_handshake_state
154154+ | Server of server_handshake_state
155155+ | Client13 of client13_handshake_state
156156+ | Server13 of server13_handshake_state
157157+158158+(* state during a handshake, used in the handlers *)
159159+type handshake_state = {
160160+ session : [ `TLS of session_data | `TLS13 of session_data13 ] list ;
161161+ protocol_version : tls_version ;
162162+ early_data_left : int32 ;
163163+ machina : handshake_machina_state ; (* state machine state *)
164164+ config : Config.config ; (* given config *)
165165+ hs_fragment : string ; (* handshake messages can be fragmented, leftover from before *)
166166+}
167167+168168+(* connection state: initially None, after handshake a crypto context *)
169169+type crypto_state = crypto_context option
170170+171171+(* record consisting of a content type and a byte vector *)
172172+type record = Packet.content_type * string
173173+174174+(* response returned by a handler *)
175175+type rec_resp = [
176176+ | `Change_enc of crypto_context (* either instruction to change the encryptor to the given one *)
177177+ | `Change_dec of crypto_context (* either change the decryptor to the given one *)
178178+ | `Record of record (* or a record which should be sent out *)
179179+]
180180+181181+(* return type of handshake handlers *)
182182+type handshake_return = handshake_state * rec_resp list
183183+184184+(* Top level state, encapsulating the entire session. *)
185185+type state = {
186186+ handshake : handshake_state ; (* the current handshake state *)
187187+ decryptor : crypto_state ; (* the current decryption state *)
188188+ encryptor : crypto_state ; (* the current encryption state *)
189189+ fragment : string ; (* the leftover fragment from TCP fragmentation *)
190190+ read_closed : bool ;
191191+ write_closed : bool ;
192192+}
193193+194194+type error = [
195195+ | `AuthenticationFailure of X509.Validation.validation_error
196196+ | `NoConfiguredCiphersuite of Ciphersuite.ciphersuite list
197197+ | `NoConfiguredVersions of tls_version list
198198+ | `NoConfiguredSignatureAlgorithm of signature_algorithm list
199199+ | `NoMatchingCertificateFound of string
200200+ | `CouldntSelectCertificate
201201+]
202202+203203+let pp_error ppf = function
204204+ | `AuthenticationFailure v ->
205205+ Fmt.pf ppf "authentication failure: %a" X509.Validation.pp_validation_error v
206206+ | `NoConfiguredCiphersuite cs ->
207207+ Fmt.pf ppf "no configured ciphersuite: %a"
208208+ Fmt.(list ~sep:(any ", ") Ciphersuite.pp_ciphersuite) cs
209209+ | `NoConfiguredVersions vs ->
210210+ Fmt.pf ppf "no configured version: %a"
211211+ Fmt.(list ~sep:(any ", ") pp_tls_version) vs
212212+ | `NoConfiguredSignatureAlgorithm sas ->
213213+ Fmt.pf ppf "no configure signature algorithm: %a"
214214+ Fmt.(list ~sep:(any ", ") pp_signature_algorithm) sas
215215+ | `NoMatchingCertificateFound host ->
216216+ Fmt.pf ppf "no matching certificate found for %s" host
217217+ | `CouldntSelectCertificate -> Fmt.string ppf "couldn't select certificate"
218218+219219+type fatal = [
220220+ | `Protocol_version of [
221221+ | `None_supported of tls_any_version list
222222+ | `Unknown_record of int * int
223223+ | `Bad_record of tls_any_version
224224+ ]
225225+ | `Unexpected of [
226226+ | `Content_type of int
227227+ | `Message of string
228228+ | `Handshake of tls_handshake
229229+ ]
230230+ | `Decode of string
231231+ | `Handshake of [
232232+ | `Message of string
233233+ | `Fragments
234234+ | `BadDH of string
235235+ | `BadECDH of Mirage_crypto_ec.error
236236+ ]
237237+ | `Bad_certificate of string
238238+ | `Missing_extension of string
239239+ | `Bad_mac
240240+ | `Record_overflow of int
241241+ | `Unsupported_extension
242242+ | `Inappropriate_fallback
243243+ | `No_application_protocol
244244+]
245245+246246+let pp_protocol_version ppf = function
247247+ | `None_supported vs ->
248248+ Fmt.pf ppf "none supported, client provided %a"
249249+ Fmt.(list ~sep:(any ", ") pp_tls_any_version) vs
250250+ | `Unknown_record (maj, min) ->
251251+ Fmt.pf ppf "unknown record version %u.%u" maj min
252252+ | `Bad_record v ->
253253+ Fmt.pf ppf "bad record version %a" pp_tls_any_version v
254254+255255+let pp_unexpected ppf = function
256256+ | `Content_type c -> Fmt.pf ppf "content type %u" c
257257+ | `Message msg -> Fmt.string ppf msg
258258+ | `Handshake hs -> Fmt.pf ppf "handshake %a" pp_handshake hs
259259+260260+let pp_handshake_error ppf = function
261261+ | `Message msg -> Fmt.string ppf msg
262262+ | `Fragments -> Fmt.string ppf "fragments are not empty"
263263+ | `BadDH msg -> Fmt.pf ppf "bad DH %s" msg
264264+ | `BadECDH e -> Fmt.pf ppf "bad ECDH %a" Mirage_crypto_ec.pp_error e
265265+266266+let pp_fatal ppf = function
267267+ | `Protocol_version e -> Fmt.pf ppf "version error: %a" pp_protocol_version e
268268+ | `Unexpected p -> Fmt.pf ppf "unexpected: %a" pp_unexpected p
269269+ | `Decode msg -> Fmt.pf ppf "decode error: %s" msg
270270+ | `Handshake h -> Fmt.pf ppf "handshake error: %a" pp_handshake_error h
271271+ | `Bad_certificate msg -> Fmt.pf ppf "bad certificate: %s" msg
272272+ | `Missing_extension msg -> Fmt.pf ppf "missing extension: %s" msg
273273+ | `Bad_mac -> Fmt.string ppf "MAC mismatch"
274274+ | `Record_overflow n -> Fmt.pf ppf "record overflow %u" n
275275+ | `Unsupported_extension -> Fmt.string ppf "unsupported extension"
276276+ | `Inappropriate_fallback -> Fmt.string ppf "inappropriate fallback"
277277+ | `No_application_protocol -> Fmt.string ppf "no application protocol"
278278+279279+type failure = [
280280+ | `Error of error
281281+ | `Fatal of fatal
282282+ | `Alert of Packet.alert_type
283283+]
284284+285285+let pp_failure ppf = function
286286+ | `Error e -> pp_error ppf e
287287+ | `Fatal f -> pp_fatal ppf f
288288+ | `Alert a -> Fmt.pf ppf "alert %s" (Packet.alert_type_to_string a)
289289+290290+let common_data_to_epoch common is_server peer_name =
291291+ let own_random, peer_random =
292292+ if is_server then
293293+ common.server_random, common.client_random
294294+ else
295295+ common.client_random, common.server_random
296296+ in
297297+ let epoch : epoch_data =
298298+ { side = if is_server then `Server else `Client ;
299299+ state = `Established ;
300300+ protocol_version = `TLS_1_0 ;
301301+ ciphersuite = `DHE_RSA_WITH_AES_256_CBC_SHA ;
302302+ peer_random ;
303303+ peer_certificate = common.peer_certificate ;
304304+ peer_certificate_chain = common.peer_certificate_chain ;
305305+ peer_name ;
306306+ trust_anchor = common.trust_anchor ;
307307+ own_random ;
308308+ own_certificate = common.own_certificate ;
309309+ own_private_key = common.own_private_key ;
310310+ own_name = common.own_name ;
311311+ received_certificates = common.received_certificates ;
312312+ master_secret = common.master_secret ;
313313+ exporter_master_secret = "" ;
314314+ alpn_protocol = common.alpn_protocol ;
315315+ session_id = "" ;
316316+ extended_ms = false ;
317317+ tls_unique = None ;
318318+ } in
319319+ epoch
320320+321321+let epoch_of_session server peer_name protocol_version = function
322322+ | `TLS (session : session_data) ->
323323+ let epoch = common_data_to_epoch session.common_session_data server peer_name in
324324+ {
325325+ epoch with
326326+ protocol_version = protocol_version ;
327327+ ciphersuite = session.ciphersuite ;
328328+ session_id = session.session_id ;
329329+ extended_ms = session.extended_ms ;
330330+ tls_unique = Some session.tls_unique ;
331331+ }
332332+ | `TLS13 (session : session_data13) ->
333333+ let epoch : epoch_data = common_data_to_epoch session.common_session_data13 server peer_name in
334334+ {
335335+ epoch with
336336+ protocol_version = protocol_version ;
337337+ ciphersuite = (session.ciphersuite13 :> Ciphersuite.ciphersuite) ;
338338+ extended_ms = true ; (* RFC 8446, Appendix D, last paragraph *)
339339+ state = session.state ;
340340+ exporter_master_secret = session.exporter_master_secret ;
341341+ }
342342+343343+let epoch_of_hs hs =
344344+ let server =
345345+ match hs.machina with
346346+ | Client _ | Client13 _ -> false
347347+ | Server _ | Server13 _ -> true
348348+ and peer_name = Config.(hs.config.peer_name)
349349+ in
350350+ match hs.session with
351351+ | [] -> None
352352+ | session :: _ -> Some (epoch_of_session server peer_name hs.protocol_version session)
+40
vendor/opam/tls/lib/utils.ml
···11+module List_set = struct
22+ let subset ?(compare = compare) l1 l2 =
33+ let rec loop xs ys =
44+ match (xs, ys) with
55+ | ([], _) -> true
66+ | (_, []) -> false
77+ | (x::xss, y::yss) ->
88+ match compare x y with
99+ | -1 -> false
1010+ | 1 -> loop xs yss
1111+ | _ -> loop xss yss in
1212+ loop (List.sort compare l1) (List.sort compare l2)
1313+1414+ let is_proper_set l =
1515+ let rec repeats = function
1616+ | x::(y::_ as xs) -> x = y || repeats xs
1717+ | _ -> false in
1818+ not @@ repeats (List.sort compare l)
1919+end
2020+2121+let rec map_find ~f = function
2222+ | [] -> None
2323+ | x::xs ->
2424+ match f x with
2525+ | None -> map_find ~f xs
2626+ | Some _ as x' -> x'
2727+2828+let init_and_last list =
2929+ List.fold_right (fun x -> function
3030+ | None -> Some ([], x)
3131+ | Some (xs, y) -> Some (x::xs, y))
3232+ list None
3333+3434+let rec first_match l1 = function
3535+ | [] -> None
3636+ | x::_ when List.mem x l1 -> Some x
3737+ | _::xs -> first_match l1 xs
3838+3939+let sub_equal ~off ~len v x =
4040+ v = String.sub x off len
+491
vendor/opam/tls/lib/writer.ml
···11+open Packet
22+open Core
33+44+let assemble_protocol_version_int buf off version =
55+ let major, minor = pair_of_tls_version version in
66+ Bytes.set_uint8 buf off major;
77+ Bytes.set_uint8 buf (off + 1) minor
88+99+let assemble_protocol_version ?(buf= Bytes.create 2) version =
1010+ assemble_protocol_version_int buf 0 version;
1111+ Bytes.unsafe_to_string buf
1212+1313+let assemble_any_protocol_version_into buf off version =
1414+ let major, minor = pair_of_tls_any_version version in
1515+ Bytes.set_uint8 buf off major;
1616+ Bytes.set_uint8 buf (off + 1) minor
1717+1818+let assemble_any_protocol_version version =
1919+ let buf = Bytes.create 2 in
2020+ assemble_any_protocol_version_into buf 0 version;
2121+ Bytes.unsafe_to_string buf
2222+2323+let assemble_hdr version (content_type, payload) =
2424+ let buf = Bytes.create 5 in
2525+ Bytes.set_uint8 buf 0 (content_type_to_int content_type);
2626+ assemble_protocol_version_int buf 1 version;
2727+ Bytes.set_uint16_be buf 3 (String.length payload);
2828+ Bytes.unsafe_to_string buf ^ payload
2929+3030+type len = One | Two | Three
3131+3232+let assemble_list ?none_if_empty lenb f elements =
3333+ let length body =
3434+ match lenb with
3535+ | One ->
3636+ let l = Bytes.create 1 in
3737+ Bytes.set_uint8 l 0 (String.length body) ;
3838+ Bytes.unsafe_to_string l
3939+ | Two ->
4040+ let l = Bytes.create 2 in
4141+ Bytes.set_uint16_be l 0 (String.length body) ;
4242+ Bytes.unsafe_to_string l
4343+ | Three ->
4444+ let l = Bytes.create 3 in
4545+ set_uint24_len ~off:0 l (String.length body) ;
4646+ Bytes.unsafe_to_string l
4747+ in
4848+ let b es = String.concat "" (List.map f es) in
4949+ let full es =
5050+ let body = b es in
5151+ length body ^ body
5252+ in
5353+ match none_if_empty with
5454+ | Some _ -> (match elements with
5555+ | [] -> ""
5656+ | eles -> full eles)
5757+ | None -> full elements
5858+5959+let assemble_certificate c =
6060+ let length = String.length c in
6161+ let buf = Bytes.create 3 in
6262+ set_uint24_len ~off:0 buf length;
6363+ Bytes.unsafe_to_string buf ^ c
6464+6565+let assemble_certificates cs =
6666+ assemble_list Three assemble_certificate cs
6767+6868+let assemble_compression_method m =
6969+ String.make 1 (Char.unsafe_chr (compression_method_to_int m))
7070+7171+let assemble_compression_methods ms =
7272+ assemble_list One assemble_compression_method ms
7373+7474+let assemble_any_ciphersuite c =
7575+ let buf = Bytes.create 2 in
7676+ Bytes.set_uint16_be buf 0 (any_ciphersuite_to_int c);
7777+ Bytes.unsafe_to_string buf
7878+7979+let assemble_any_ciphersuites cs =
8080+ assemble_list Two assemble_any_ciphersuite cs
8181+8282+let assemble_ciphersuite c =
8383+ let acs = Ciphersuite.ciphersuite_to_any_ciphersuite c in
8484+ assemble_any_ciphersuite acs
8585+8686+let assemble_hostname host =
8787+ let host = Domain_name.to_string host in
8888+ (* 8 bit hostname type; 16 bit length; value *)
8989+ let vallength = String.length host in
9090+ let buf = Bytes.create 3 in
9191+ Bytes.set_uint8 buf 0 0; (* type, only 0 registered *)
9292+ Bytes.set_uint16_be buf 1 vallength;
9393+ Bytes.unsafe_to_string buf ^ host
9494+9595+let assemble_hostnames hosts =
9696+ assemble_list Two assemble_hostname hosts
9797+9898+let assemble_hash_signature sigalg =
9999+ let buf = Bytes.create 2 in
100100+ Bytes.set_uint16_be buf 0 (signature_alg_to_int (to_signature_alg sigalg)) ;
101101+ Bytes.unsafe_to_string buf
102102+103103+let assemble_signature_algorithms s =
104104+ assemble_list Two assemble_hash_signature s
105105+106106+let assemble_certificate_types ts =
107107+ let ass x =
108108+ String.make 1 (Char.unsafe_chr (client_certificate_type_to_int x))
109109+ in
110110+ assemble_list One ass ts
111111+112112+let assemble_cas cas =
113113+ let ass x =
114114+ let buf = Bytes.create 2 in
115115+ Bytes.set_uint16_be buf 0 (String.length x) ;
116116+ Bytes.unsafe_to_string buf ^ x
117117+ in
118118+ assemble_list Two ass cas
119119+120120+let assemble_certificate_request ts cas =
121121+ assemble_certificate_types ts ^ assemble_cas cas
122122+123123+let assemble_certificate_request_1_2 ts sigalgs cas =
124124+ String.concat "" [
125125+ assemble_certificate_types ts;
126126+ assemble_signature_algorithms sigalgs;
127127+ assemble_cas cas
128128+ ]
129129+130130+let assemble_named_group g =
131131+ let buf = Bytes.create 2 in
132132+ Bytes.set_uint16_be buf 0 (named_group_to_int g);
133133+ Bytes.unsafe_to_string buf
134134+135135+let assemble_group g =
136136+ assemble_named_group (group_to_named_group g)
137137+138138+let assemble_supported_groups groups =
139139+ assemble_list Two assemble_named_group groups
140140+141141+let assemble_keyshare_entry (ng, ks) =
142142+ let g = assemble_named_group ng in
143143+ let l = Bytes.create 2 in
144144+ Bytes.set_uint16_be l 0 (String.length ks) ;
145145+ String.concat "" [ g ; Bytes.unsafe_to_string l ; ks ]
146146+147147+let assemble_psk_id (id, age) =
148148+ let id_len = Bytes.create 2 in
149149+ Bytes.set_uint16_be id_len 0 (String.length id) ;
150150+ let age_buf = Bytes.create 4 in
151151+ Bytes.set_int32_be age_buf 0 age ;
152152+ String.concat "" [ Bytes.unsafe_to_string id_len ; id ; Bytes.unsafe_to_string age_buf ]
153153+154154+let assemble_binder b =
155155+ let b_len = String.make 1 (Char.unsafe_chr (String.length b)) in
156156+ b_len ^ b
157157+158158+let assemble_client_psks psks =
159159+ let ids, binders = List.split psks in
160160+ let ids_buf = assemble_list Two assemble_psk_id ids in
161161+ let binders_buf = assemble_list Two assemble_binder binders in
162162+ ids_buf ^ binders_buf
163163+164164+let assemble_alpn_protocol p =
165165+ let buf = String.make 1 (Char.unsafe_chr (String.length p)) in
166166+ buf ^ p
167167+168168+let assemble_alpn_protocols protocols =
169169+ assemble_list Two assemble_alpn_protocol protocols
170170+171171+let assemble_supported_versions vs =
172172+ assemble_list One assemble_any_protocol_version vs
173173+174174+let assemble_extension = function
175175+ | `SecureRenegotiation x ->
176176+ let buf = String.make 1 (Char.unsafe_chr (String.length x)) in
177177+ (buf ^ x, RENEGOTIATION_INFO)
178178+ | `ExtendedMasterSecret -> ("", EXTENDED_MASTER_SECRET)
179179+ | `ECPointFormats ->
180180+ (* a list of point formats, we support type 0 = uncompressed unconditionally *)
181181+ let data = Bytes.make 2 '\x00' in
182182+ Bytes.set_uint8 data 0 1;
183183+ (Bytes.unsafe_to_string data, EC_POINT_FORMATS)
184184+ | _ -> invalid_arg "unknown extension"
185185+186186+let assemble_cookie c =
187187+ let l = Bytes.create 2 in
188188+ Bytes.set_uint16_be l 0 (String.length c) ;
189189+ Bytes.unsafe_to_string l ^ c
190190+191191+let assemble_psk_key_exchange_mode mode =
192192+ String.make 1 (Char.unsafe_chr (psk_key_exchange_mode_to_int mode))
193193+194194+let assemble_psk_key_exchange_modes modes =
195195+ assemble_list One assemble_psk_key_exchange_mode modes
196196+197197+let assemble_ext (pay, typ) =
198198+ let buf = Bytes.create 4 in
199199+ Bytes.set_uint16_be buf 0 (extension_type_to_int typ);
200200+ Bytes.set_uint16_be buf 2 (String.length pay);
201201+ Bytes.unsafe_to_string buf ^ pay
202202+203203+let assemble_extensions ?none_if_empty assemble_e es =
204204+ assemble_list ?none_if_empty Two assemble_e es
205205+206206+let assemble_ca ca =
207207+ let lenbuf = Bytes.create 2 in
208208+ let data = X509.Distinguished_name.encode_der ca in
209209+ Bytes.set_uint16_be lenbuf 0 (String.length data) ;
210210+ Bytes.unsafe_to_string lenbuf ^ data
211211+212212+let assemble_certificate_authorities cas =
213213+ assemble_list Two assemble_ca cas
214214+215215+let assemble_certificate_request_extension e =
216216+ assemble_ext @@ match e with
217217+ | `SignatureAlgorithms s ->
218218+ (assemble_signature_algorithms s, SIGNATURE_ALGORITHMS)
219219+ | `CertificateAuthorities cas ->
220220+ (assemble_certificate_authorities cas, CERTIFICATE_AUTHORITIES)
221221+ | _ -> invalid_arg "unknown extension"
222222+223223+let assemble_certificate_request_1_3 ?(context = "") exts =
224224+ let clen = String.make 1 (Char.unsafe_chr (String.length context)) in
225225+ let exts = assemble_extensions assemble_certificate_request_extension exts in
226226+ String.concat "" [ clen ; context ; exts ]
227227+228228+let assemble_client_extension e =
229229+ assemble_ext @@ match e with
230230+ | `SupportedGroups groups ->
231231+ (assemble_supported_groups groups, SUPPORTED_GROUPS)
232232+ | `Hostname name -> (assemble_hostnames [name], SERVER_NAME)
233233+ | `Padding x -> (String.make x '\x00', PADDING)
234234+ | `SignatureAlgorithms s ->
235235+ (assemble_signature_algorithms s, SIGNATURE_ALGORITHMS)
236236+ | `ALPN protocols ->
237237+ (assemble_alpn_protocols protocols, APPLICATION_LAYER_PROTOCOL_NEGOTIATION)
238238+ | `KeyShare ks ->
239239+ (assemble_list Two assemble_keyshare_entry ks, KEY_SHARE)
240240+ | `PreSharedKeys ids ->
241241+ (assemble_client_psks ids, PRE_SHARED_KEY)
242242+ | `EarlyDataIndication ->
243243+ ("", EARLY_DATA)
244244+ | `SupportedVersions vs ->
245245+ (assemble_supported_versions vs, SUPPORTED_VERSIONS)
246246+ | `PostHandshakeAuthentication ->
247247+ ("", POST_HANDSHAKE_AUTH)
248248+ | `Cookie c ->
249249+ (assemble_cookie c, COOKIE)
250250+ | `PskKeyExchangeModes modes ->
251251+ (assemble_psk_key_exchange_modes modes, PSK_KEY_EXCHANGE_MODES)
252252+ | x -> assemble_extension x
253253+254254+let assemble_server_extension e =
255255+ assemble_ext @@ match e with
256256+ | `Hostname -> ("", SERVER_NAME)
257257+ | `ALPN protocol ->
258258+ (assemble_alpn_protocols [protocol], APPLICATION_LAYER_PROTOCOL_NEGOTIATION)
259259+ | `KeyShare (g, ks) ->
260260+ let ng = group_to_named_group g in
261261+ (assemble_keyshare_entry (ng, ks), KEY_SHARE)
262262+ | `PreSharedKey id ->
263263+ let data = Bytes.create 2 in
264264+ Bytes.set_uint16_be data 0 id ;
265265+ (Bytes.unsafe_to_string data, PRE_SHARED_KEY)
266266+ | `SelectedVersion v -> (assemble_protocol_version v, SUPPORTED_VERSIONS)
267267+ | x -> assemble_extension x
268268+269269+let assemble_encrypted_extension e =
270270+ assemble_ext @@ match e with
271271+ | `Hostname -> ("", SERVER_NAME)
272272+ | `ALPN protocol ->
273273+ (assemble_alpn_protocols [protocol], APPLICATION_LAYER_PROTOCOL_NEGOTIATION)
274274+ | `SupportedGroups groups ->
275275+ (assemble_supported_groups (List.map group_to_named_group groups), SUPPORTED_GROUPS)
276276+ | `EarlyDataIndication -> ("", EARLY_DATA)
277277+ | _ -> invalid_arg "unknown extension"
278278+279279+let assemble_retry_extension e =
280280+ assemble_ext @@ match e with
281281+ | `SelectedGroup g -> (assemble_group g, KEY_SHARE)
282282+ | `Cookie c -> (assemble_cookie c, COOKIE)
283283+ | `SelectedVersion v -> (assemble_protocol_version v, SUPPORTED_VERSIONS)
284284+ | `UnknownExtension _ -> invalid_arg "unknown retry extension"
285285+286286+let assemble_cert_ext (certificate, extensions) =
287287+ let cert = assemble_certificate certificate
288288+ and exts = assemble_list Two assemble_server_extension extensions
289289+ in
290290+ cert ^ exts
291291+292292+let assemble_certs_exts cs =
293293+ assemble_list Three assemble_cert_ext cs
294294+295295+let assemble_certificates_1_3 context certs =
296296+ let l = String.make 1 (Char.unsafe_chr (String.length context)) in
297297+ String.concat "" [ l ; context ; assemble_certs_exts (List.map (fun c -> c, []) certs) ]
298298+299299+let assemble_sid sid =
300300+ match sid with
301301+ | None -> String.make 1 '\x00'
302302+ | Some s -> String.make 1 (Char.unsafe_chr (String.length s)) ^ s
303303+304304+let assemble_client_hello (cl : client_hello) : string =
305305+ let version = match cl.client_version with
306306+ | `TLS_1_3 -> `TLS_1_2 (* keep 0x03 0x03 on wire *)
307307+ | x -> x
308308+ in
309309+ let v = assemble_any_protocol_version version in
310310+ let sid = assemble_sid cl.sessionid in
311311+ let css = assemble_any_ciphersuites cl.ciphersuites in
312312+ (* compression methods, completely useless *)
313313+ let cms = assemble_compression_methods [NULL] in
314314+ let bbuf = String.concat "" [ v ; cl.client_random ; sid ; css ; cms ] in
315315+ let extensions = assemble_extensions ~none_if_empty:true assemble_client_extension cl.extensions in
316316+ (* some widely deployed firewalls drop ClientHello messages which are
317317+ > 256 and < 511 byte, insert PADDING extension for these *)
318318+ (* from draft-ietf-tls-padding-00:
319319+ As an example, consider a client that wishes to avoid sending a
320320+ ClientHello with a record size between 256 and 511 bytes (inclusive).
321321+ This case is considered because at least one TLS implementation is
322322+ known to hang the connection when such a ClientHello record is
323323+ received.
324324+325325+ After building a ClientHello as normal, the client can add four to
326326+ the length (to account for the "msg_type" and "length" fields of the
327327+ handshake protocol) and test whether the resulting length falls into
328328+ that range. If it does, a padding extension can be added in order to
329329+ push the length to (at least) 512 bytes. *)
330330+ let extrapadding =
331331+ (* since PreSharedKeys _must_ be the last extension, don't bother padding
332332+ when it is present. rationale from ietf-tls WG
333333+ "Padding extension and 0-RTT" thread (2016-10-30) *)
334334+ if List.exists (function `PreSharedKeys _ -> true | _ -> false) cl.extensions then
335335+ ""
336336+ else
337337+ let buflen = String.length bbuf + String.length extensions + 4 (* see above, header *) in
338338+ if buflen >= 256 && buflen <= 511 then
339339+ match String.length extensions with
340340+ | 0 -> (* need to construct a 2 byte extension length as well *)
341341+ let l = 512 (* desired length *) - 2 (* extension length *) - 4 (* padding extension header *) - buflen in
342342+ let l = max l 0 in (* negative size is not good *)
343343+ let padding = assemble_client_extension (`Padding l) in
344344+ let extension_length = Bytes.create 2 in
345345+ Bytes.set_uint16_be extension_length 0 (String.length padding);
346346+ Bytes.unsafe_to_string extension_length ^ padding
347347+ | _ ->
348348+ let l = 512 - 4 (* padding extension header *) - buflen in
349349+ let l = max l 0 in
350350+ let padding = assemble_client_extension (`Padding l) in
351351+ (* extensions include the 16 bit extension length field *)
352352+ let elen = String.length extensions + String.length padding - 2 (* the 16 bit length field *) in
353353+ Bytes.set_uint16_be (Bytes.unsafe_of_string extensions) 0 elen;
354354+ padding
355355+ else
356356+ ""
357357+ in
358358+ String.concat "" [ bbuf ; extensions ; extrapadding ]
359359+360360+let assemble_server_hello (sh : server_hello) : string =
361361+ let version, exts = match sh.server_version with
362362+ | `TLS_1_3 -> `TLS_1_2, `SelectedVersion `TLS_1_3 :: sh.extensions
363363+ | x -> x, sh.extensions
364364+ in
365365+ let v = assemble_protocol_version version in
366366+ let sid = assemble_sid sh.sessionid in
367367+ let cs = assemble_ciphersuite sh.ciphersuite in
368368+ (* useless compression method *)
369369+ let cm = assemble_compression_method NULL in
370370+ let extensions = assemble_extensions ~none_if_empty:true assemble_server_extension exts in
371371+ String.concat "" [ v ; sh.server_random ; sid ; cs ; cm ; extensions ]
372372+373373+let assemble_dh_parameters p =
374374+ let plen, glen, yslen = (String.length p.dh_p, String.length p.dh_g, String.length p.dh_Ys) in
375375+ let buf = Bytes.create (2 + 2 + 2 + plen + glen + yslen) in
376376+ Bytes.set_uint16_be buf 0 plen;
377377+ Bytes.blit_string p.dh_p 0 buf 2 plen;
378378+ Bytes.set_uint16_be buf (2 + plen) glen;
379379+ Bytes.blit_string p.dh_g 0 buf (4 + plen) glen;
380380+ Bytes.set_uint16_be buf (4 + plen + glen) yslen;
381381+ Bytes.blit_string p.dh_Ys 0 buf (6 + plen + glen) yslen;
382382+ Bytes.unsafe_to_string buf
383383+384384+let assemble_ec_parameters named_curve point =
385385+ let hdr = Bytes.create 4 in
386386+ Bytes.set_uint8 hdr 0 (ec_curve_type_to_int NAMED_CURVE);
387387+ Bytes.set_uint16_be hdr 1 (named_group_to_int (group_to_named_group named_curve));
388388+ Bytes.set_uint8 hdr 3 (String.length point);
389389+ Bytes.unsafe_to_string hdr ^ point
390390+391391+let assemble_digitally_signed signature =
392392+ let lenbuf = Bytes.create 2 in
393393+ Bytes.set_uint16_be lenbuf 0 (String.length signature);
394394+ Bytes.unsafe_to_string lenbuf ^ signature
395395+396396+let assemble_digitally_signed_1_2 sigalg signature =
397397+ (assemble_hash_signature sigalg) ^ (assemble_digitally_signed signature)
398398+399399+let assemble_session_ticket_extension e =
400400+ assemble_ext @@ match e with
401401+ | `EarlyDataIndication max ->
402402+ let buf = Bytes.create 4 in
403403+ Bytes.set_int32_be buf 0 max ;
404404+ (Bytes.unsafe_to_string buf, EARLY_DATA)
405405+ | _ -> invalid_arg "unknown extension"
406406+407407+let assemble_session_ticket (se : session_ticket) =
408408+ let buf = Bytes.create 9 in
409409+ Bytes.set_int32_be buf 0 se.lifetime ;
410410+ Bytes.set_int32_be buf 4 se.age_add ;
411411+ Bytes.set_uint8 buf 8 (String.length se.nonce) ;
412412+ let ticketlen = Bytes.create 2 in
413413+ Bytes.set_uint16_be ticketlen 0 (String.length se.ticket) ;
414414+ let exts = assemble_extensions assemble_session_ticket_extension se.extensions in
415415+ String.concat "" [ Bytes.unsafe_to_string buf ; se.nonce ; Bytes.unsafe_to_string ticketlen ; se.ticket ; exts ]
416416+417417+let assemble_client_dh_key_exchange kex =
418418+ let len = String.length kex in
419419+ let buf = Bytes.create (len + 2) in
420420+ Bytes.set_uint16_be buf 0 len;
421421+ Bytes.blit_string kex 0 buf 2 len;
422422+ Bytes.unsafe_to_string buf
423423+424424+let assemble_client_ec_key_exchange kex =
425425+ let len = String.length kex in
426426+ let buf = Bytes.create (len + 1) in
427427+ Bytes.set_uint8 buf 0 len;
428428+ Bytes.blit_string kex 0 buf 1 len;
429429+ Bytes.unsafe_to_string buf
430430+431431+let assemble_hello_retry_request hrr =
432432+ let exts = `SelectedGroup hrr.selected_group :: hrr.extensions in
433433+ let version, exts = match hrr.retry_version with
434434+ | `TLS_1_3 -> `TLS_1_2, `SelectedVersion `TLS_1_3 :: exts
435435+ | x -> x, exts
436436+ in
437437+ let v = assemble_protocol_version version in
438438+ let sid = assemble_sid hrr.sessionid in
439439+ let cs = assemble_ciphersuite (hrr.ciphersuite :> Ciphersuite.ciphersuite) in
440440+ (* useless compression method *)
441441+ let cm = String.make 1 '\x00' in
442442+ let extensions = assemble_extensions ~none_if_empty:true assemble_retry_extension exts in
443443+ String.concat "" [ v ; helloretryrequest ; sid ; cs ; cm ; extensions ]
444444+445445+let assemble_hs typ len =
446446+ let buf = Bytes.create 4 in
447447+ Bytes.set_uint8 buf 0 (handshake_type_to_int typ);
448448+ set_uint24_len ~off:1 buf len;
449449+ Bytes.unsafe_to_string buf
450450+451451+let assemble_message_hash len =
452452+ assemble_hs MESSAGE_HASH len
453453+454454+let assemble_key_update req =
455455+ String.make 1 (Char.unsafe_chr (key_update_request_type_to_int req))
456456+457457+let assemble_handshake hs =
458458+ let (payload, payload_type) =
459459+ match hs with
460460+ | ClientHello ch -> (assemble_client_hello ch, CLIENT_HELLO)
461461+ | ServerHello sh -> (assemble_server_hello sh, SERVER_HELLO)
462462+ | HelloRetryRequest hr -> (assemble_hello_retry_request hr, SERVER_HELLO)
463463+ | Certificate cs -> (cs, CERTIFICATE)
464464+ | CertificateRequest cr -> (cr, CERTIFICATE_REQUEST)
465465+ | CertificateVerify c -> (c, CERTIFICATE_VERIFY)
466466+ | ServerKeyExchange kex -> (kex, SERVER_KEY_EXCHANGE)
467467+ | ClientKeyExchange kex -> (kex, CLIENT_KEY_EXCHANGE)
468468+ | ServerHelloDone -> ("", SERVER_HELLO_DONE)
469469+ | HelloRequest -> ("", HELLO_REQUEST)
470470+ | Finished fs -> (fs, FINISHED)
471471+ | SessionTicket st -> (assemble_session_ticket st, SESSION_TICKET)
472472+ | EncryptedExtensions ee ->
473473+ let cs = assemble_extensions assemble_encrypted_extension ee in
474474+ (cs, ENCRYPTED_EXTENSIONS)
475475+ | KeyUpdate req ->
476476+ let cs = assemble_key_update req in
477477+ (cs, KEY_UPDATE)
478478+ | EndOfEarlyData -> ("", END_OF_EARLY_DATA)
479479+ in
480480+ let pay_len = String.length payload in
481481+ let buf = assemble_hs payload_type pay_len in
482482+ buf ^ payload
483483+484484+let assemble_alert ?(level = Packet.FATAL) typ =
485485+ let buf = Bytes.create 2 in
486486+ Bytes.set_uint8 buf 1 (alert_type_to_int typ);
487487+ Bytes.set_uint8 buf 0 (alert_level_to_int level) ;
488488+ Bytes.unsafe_to_string buf
489489+490490+let assemble_change_cipher_spec =
491491+ String.make 1 '\x01'
···11+22+open Ex_common
33+open Lwt
44+55+let cached_session : Tls.Core.epoch_data =
66+ let hex = Ohex.decode in
77+ {
88+ Tls.Core.side = `Client ;
99+ protocol_version = `TLS_1_3 ;
1010+ ciphersuite = `DHE_RSA_WITH_AES_128_GCM_SHA256 ;
1111+ peer_random = hex "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ;
1212+ peer_certificate = None ;
1313+ peer_certificate_chain = [] ;
1414+ peer_name = None ;
1515+ trust_anchor = None ;
1616+ received_certificates = [] ;
1717+ own_random = hex "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ;
1818+ own_certificate = [] ;
1919+ own_private_key = None ;
2020+ own_name = None ;
2121+ master_secret = hex "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ;
2222+ exporter_master_secret = "" ;
2323+ session_id = "" ;
2424+ extended_ms = true ;
2525+ alpn_protocol = None ;
2626+ state = `Established ;
2727+ tls_unique = None ;
2828+ }
2929+3030+let echo_client ?ca hostname port =
3131+ let open Lwt_io in
3232+ auth ?ca () >>= fun authenticator ->
3333+ X509_lwt.private_of_pems
3434+ ~cert:server_cert
3535+ ~priv_key:server_key >>= fun certificate ->
3636+ Tls_lwt.connect_ext
3737+ (get_ok Tls.Config.(client ~authenticator ~cached_session ~certificates:(`Single certificate) ~ciphers:Ciphers.supported ()))
3838+ (hostname, port) >>= fun (ic, oc) ->
3939+ Lwt.join [
4040+ lines ic |> Lwt_stream.iter_s (printf "+ %s\n%!") ;
4141+ lines stdin |> Lwt_stream.iter_s (write_line oc)
4242+ ]
4343+4444+let jump _ port host ca =
4545+ try
4646+ Lwt_main.run (echo_client ?ca host port)
4747+ with
4848+ | Tls_lwt.Tls_alert alert as exn ->
4949+ print_alert "remote end" alert ; raise exn
5050+ | Tls_lwt.Tls_failure alert as exn ->
5151+ print_fail "our end" alert ; raise exn
5252+5353+open Cmdliner
5454+5555+let port =
5656+ let doc = "Port to connect to" in
5757+ Arg.(value & opt int 443 & info [ "port" ] ~doc)
5858+5959+let host =
6060+ let doc = "Host to connect to" in
6161+ Arg.(value & opt string "" & info [ "host" ] ~doc)
6262+6363+let trust =
6464+ let doc = "Trust anchor" in
6565+ Arg.(value & opt (some string) None & info [ "trust" ] ~doc)
6666+6767+let cmd =
6868+ let term = Term.(const jump $ setup_log $ port $ host $ trust)
6969+ and info = Cmd.info "echo_client" ~version:"%%VERSION_NUM%%"
7070+ in
7171+ Cmd.v info term
7272+7373+let () = exit (Cmd.eval cmd)
+22
vendor/opam/tls/lwt/examples/echo_client_alpn.ml
···11+22+open Ex_common
33+open Lwt
44+55+let echo_client host port =
66+ let open Lwt_io in
77+ let port = int_of_string port in
88+ let authenticator = null_auth in
99+ Tls_lwt.Unix.connect
1010+ (get_ok Tls.Config.(client ~authenticator ~alpn_protocols:["http/1.1"; "h2"] ()))
1111+ (host, port) >>= fun t ->
1212+ match Tls_lwt.Unix.epoch t with
1313+ | Error () -> printl "Error"
1414+ | Ok epoch -> (
1515+ match epoch.Tls.Core.alpn_protocol with
1616+ | None -> printl "No protocol selected"
1717+ | Some protocol -> printl ("Selected protocol: " ^ protocol)
1818+ )
1919+ >>= fun () -> Tls_lwt.Unix.close t
2020+2121+let () =
2222+ Lwt_main.run (echo_client "127.0.0.1" "4433")
+75
vendor/opam/tls/lwt/examples/echo_server.ml
···11+open Lwt
22+open Ex_common
33+44+let string_of_unix_err err f p =
55+ Printf.sprintf "Unix_error (%s, %s, %s)"
66+ (Unix.error_message err) f p
77+88+let serve_ssl port callback =
99+1010+ let tag = "server" in
1111+1212+ X509_lwt.private_of_pems
1313+ ~cert:server_cert
1414+ ~priv_key:server_key >>= fun cert ->
1515+1616+ let server_s () =
1717+ let open Lwt_unix in
1818+ let s = socket PF_INET SOCK_STREAM 0 in
1919+ setsockopt s SO_REUSEADDR true ;
2020+ bind s (ADDR_INET (Unix.inet_addr_any, port)) >|= fun () ->
2121+ listen s 10 ;
2222+ s in
2323+2424+ let handle channels addr =
2525+ async @@ fun () ->
2626+ Lwt.catch (fun () -> callback channels addr >>= fun () -> yap ~tag "<- handler done")
2727+ (function
2828+ | Tls_lwt.Tls_alert a ->
2929+ yap ~tag @@ "handler: " ^ Tls.Packet.alert_type_to_string a
3030+ | Tls_lwt.Tls_failure a ->
3131+ yap ~tag @@ "handler: " ^ Tls.Engine.string_of_failure a
3232+ | Unix.Unix_error (e, f, p) ->
3333+ yap ~tag @@ "handler: " ^ (string_of_unix_err e f p)
3434+ | _exn -> yap ~tag "handler: exception")
3535+ in
3636+3737+ yap ~tag ("-> start @ " ^ string_of_int port) >>= fun () ->
3838+ let rec loop s =
3939+ let authenticator = null_auth in
4040+ let config = get_ok (Tls.Config.server ~version:(`TLS_1_0, `TLS_1_3) ~ciphers:Tls.Config.Ciphers.supported ~reneg:true ~certificates:(`Single cert) ~authenticator ()) in
4141+ (Lwt.catch
4242+ (fun () -> Tls_lwt.accept_ext config s >|= fun r -> `R r)
4343+ (function
4444+ | Unix.Unix_error (e, f, p) -> return (`L (string_of_unix_err e f p))
4545+ | Tls_lwt.Tls_alert a -> return (`L (Tls.Packet.alert_type_to_string a))
4646+ | Tls_lwt.Tls_failure f -> return (`L (Tls.Engine.string_of_failure f))
4747+ | exn -> return (`L ("loop: exception: " ^ Printexc.to_string exn)))) >>= function
4848+ | `R (channels, addr) ->
4949+ yap ~tag "-> connect" >>= fun () -> ( handle channels addr ; loop s )
5050+ | `L (msg) ->
5151+ yap ~tag ("server socket: " ^ msg) >>= fun () -> loop s
5252+ in
5353+ server_s () >>= fun s ->
5454+ loop s
5555+5656+let echo_server _ port =
5757+ Lwt_main.run (
5858+ serve_ssl port @@ fun (ic, oc) _addr ->
5959+ lines ic |> Lwt_stream.iter_s (fun line ->
6060+ yap ~tag:"handler" ("+ " ^ line) >>= fun () ->
6161+ Lwt_io.write_line oc line))
6262+6363+open Cmdliner
6464+6565+let port =
6666+ let doc = "Port to connect to" in
6767+ Arg.(value & opt int 4433 & info [ "port" ] ~doc)
6868+6969+let cmd =
7070+ let term = Term.(ret (const echo_server $ setup_log $ port))
7171+ and info = Cmd.info "echo_server" ~version:"%%VERSION_NUM%%"
7272+ in
7373+ Cmd.v info term
7474+7575+let () = exit (Cmd.eval cmd)
+68
vendor/opam/tls/lwt/examples/echo_server_alpn.ml
···11+22+open Lwt
33+open Ex_common
44+55+let split_on_char sep s =
66+ let r = ref [] in
77+ let j = ref (String.length s) in
88+ for i = String.length s - 1 downto 0 do
99+ if s.[i] = sep then begin
1010+ r := String.sub s (i + 1) (!j - i - 1) :: !r;
1111+ j := i
1212+ end
1313+ done;
1414+ String.sub s 0 !j :: !r
1515+1616+let serve_ssl alpn_protocols port callback =
1717+1818+ let tag = "server" in
1919+2020+ X509_lwt.private_of_pems
2121+ ~cert:server_cert
2222+ ~priv_key:server_key >>= fun certificate ->
2323+2424+ let server_s =
2525+ let open Lwt_unix in
2626+ let s = socket PF_INET SOCK_STREAM 0 in
2727+ bind s (ADDR_INET (Unix.inet_addr_any, port)) >|= fun () ->
2828+ listen s 10 ;
2929+ s in
3030+3131+ let handle ep channels addr =
3232+ let alpn = match ep with
3333+ | Ok data -> (match data.Tls.Core.alpn_protocol with
3434+ | Some a -> a
3535+ | None -> "no alpn")
3636+ | Error () -> "no session"
3737+ in
3838+ async @@ fun () ->
3939+ Lwt.catch (fun () -> callback alpn channels addr >>= fun () -> yap ~tag "<- handler done")
4040+ (function
4141+ | Tls_lwt.Tls_alert a ->
4242+ yap ~tag @@ "handler: " ^ Tls.Packet.alert_type_to_string a
4343+ | exn -> yap ~tag "handler: exception" >>= fun () -> fail exn)
4444+ in
4545+4646+ let ps = string_of_int port in
4747+ yap ~tag ("-> start @ " ^ ps ^ " (use `openssl s_client -connect host:" ^ ps ^ " -alpn <proto>`), available protocols: " ^ String.concat "," alpn_protocols) >>= fun () ->
4848+ let rec loop () =
4949+ let config = get_ok (Tls.Config.server ~certificates:(`Single certificate) ~alpn_protocols ()) in
5050+ server_s >>= fun s ->
5151+ Tls_lwt.Unix.accept config s >>= fun (t, addr) ->
5252+ yap ~tag "-> connect" >>= fun () ->
5353+ ( handle (Tls_lwt.Unix.epoch t) (Tls_lwt.of_t t) addr ; loop () )
5454+ in
5555+ loop ()
5656+5757+5858+let echo_server protocols port =
5959+ serve_ssl protocols port @@ fun alpn (ic, oc) _addr ->
6060+ lines ic |> Lwt_stream.iter_s (fun line ->
6161+ yap ~tag:("handler alpn: " ^ alpn) ("+ " ^ line) >>= fun () ->
6262+ Lwt_io.write_line oc line)
6363+6464+let () =
6565+ let protocols =
6666+ try split_on_char ',' Sys.argv.(1) with _ -> [ "h2" ; "http/1.1" ]
6767+ in
6868+ Lwt_main.run (echo_server protocols 4433)
+61
vendor/opam/tls/lwt/examples/echo_server_sni.ml
···11+22+open Lwt
33+open Ex_common
44+55+let serve_ssl port callback =
66+77+ let tag = "server" in
88+99+ X509_lwt.private_of_pems
1010+ ~cert:(ca_cert_dir ^ "/bar.pem")
1111+ ~priv_key:server_key >>= fun barcert ->
1212+1313+ X509_lwt.private_of_pems
1414+ ~cert:(ca_cert_dir ^ "/foo.pem")
1515+ ~priv_key:server_key >>= fun foocert ->
1616+1717+ let server_s =
1818+ let open Lwt_unix in
1919+ let s = socket PF_INET SOCK_STREAM 0 in
2020+ bind s (ADDR_INET (Unix.inet_addr_any, port)) >|= fun () ->
2121+ listen s 10 ;
2222+ s in
2323+2424+ let handle ep channels addr =
2525+ let host = match ep with
2626+ | Ok data -> ( match data.Tls.Core.own_name with
2727+ | Some n -> Domain_name.to_string n
2828+ | None -> "no name" )
2929+ | Error () -> "no session"
3030+ in
3131+ async @@ fun () ->
3232+ Lwt.catch (fun () -> callback host channels addr >>= fun () -> yap ~tag "<- handler done")
3333+ (function
3434+ | Tls_lwt.Tls_alert a ->
3535+ yap ~tag @@ "handler: " ^ Tls.Packet.alert_type_to_string a
3636+ | exn -> yap ~tag "handler: exception" >>= fun () -> fail exn)
3737+ in
3838+3939+ let ps = string_of_int port in
4040+ yap ~tag ("-> start @ " ^ ps ^ " (use `openssl s_client -connect host:" ^ ps ^ " -servername foo` (or -servername bar))") >>= fun () ->
4141+ let rec loop () =
4242+ let config = get_ok (Tls.Config.server ~certificates:(`Multiple [barcert ; foocert]) ()) in
4343+ server_s >>= fun s ->
4444+ Tls_lwt.Unix.accept config s >>= fun (t, addr) ->
4545+ yap ~tag "-> connect" >>= fun () ->
4646+ ( handle (Tls_lwt.Unix.epoch t) (Tls_lwt.of_t t) addr ; loop () )
4747+ in
4848+ loop ()
4949+5050+5151+let echo_server port =
5252+ serve_ssl port @@ fun host (ic, oc) _addr ->
5353+ lines ic |> Lwt_stream.iter_s (fun line ->
5454+ yap ~tag:("handler " ^ host) ("+ " ^ line) >>= fun () ->
5555+ Lwt_io.write_line oc line)
5656+5757+let () =
5858+ let port =
5959+ try int_of_string Sys.argv.(1) with _ -> 4433
6060+ in
6161+ Lwt_main.run (echo_server port)
+55
vendor/opam/tls/lwt/examples/ex_common.ml
···11+22+open Lwt
33+44+let o f g x = f (g x)
55+66+let ca_cert_dir = "./certificates"
77+let server_cert = "./certificates/server.pem"
88+let server_key = "./certificates/server.key"
99+let server_ec_cert = "./certificates/server-ec.pem"
1010+let server_ec_key = "./certificates/server-ec.key"
1111+1212+let yap ~tag msg = Lwt_io.printf "(%s %s)\n%!" tag msg
1313+1414+let lines ic =
1515+ Lwt_stream.from @@ fun () ->
1616+ Lwt_io.read_line_opt ic >>= function
1717+ | None -> Lwt_io.close ic >>= fun () -> return_none
1818+ | line -> return line
1919+2020+let print_alert where alert =
2121+ Printf.eprintf "(TLS ALERT (%s): %s)\n%!"
2222+ where (Tls.Packet.alert_type_to_string alert)
2323+2424+let print_fail where fail =
2525+ Printf.eprintf "(TLS FAIL (%s): %s)\n%!"
2626+ where (Tls.Engine.string_of_failure fail)
2727+2828+let null_auth ?ip:_ ~host:_ _ = Ok None
2929+3030+let auth ?ca ?fp () =
3131+ match ca with
3232+ | Some "NONE" when fp = None -> Lwt.return null_auth
3333+ | _ ->
3434+ let a = match ca, fp with
3535+ | None, Some fp -> `Hex_key_fingerprint (`SHA256, fp)
3636+ | None, _ -> `Ca_dir ca_cert_dir
3737+ | Some f, _ -> `Ca_file f
3838+ in
3939+ X509_lwt.authenticator a
4040+4141+let setup_log style_renderer level =
4242+ Fmt_tty.setup_std_outputs ?style_renderer ();
4343+ Logs.set_level level;
4444+ Logs.set_reporter (Logs_fmt.reporter ~dst:Format.std_formatter ())
4545+4646+open Cmdliner
4747+4848+let setup_log =
4949+ Term.(const setup_log
5050+ $ Fmt_cli.style_renderer ()
5151+ $ Logs_cli.level ())
5252+5353+let get_ok = function
5454+ | Ok cfg -> cfg
5555+ | Error `Msg msg -> invalid_arg msg
+101
vendor/opam/tls/lwt/examples/fuzz_server.ml
···11+open Lwt
22+open Ex_common
33+44+let string_of_unix_err err f p =
55+ Printf.sprintf "Unix_error (%s, %s, %s)"
66+ (Unix.error_message err) f p
77+88+let add_to_cache, find_in_cache =
99+ let c = ref [] in
1010+ (fun ticket session ->
1111+ let id = ticket.Tls.Core.identifier in
1212+ Logs.info (fun m -> m "adding id %a to cache" Ohex.pp id) ;
1313+ c := (id, (ticket, session)) :: !c),
1414+ (fun id -> match List.find_opt (fun (id', _) -> String.compare id id' = 0) !c with
1515+ | None -> None
1616+ | Some (_, ep) -> Some ep)
1717+1818+let ticket_cache = {
1919+ Tls.Config.lookup = find_in_cache ;
2020+ ticket_granted = add_to_cache ;
2121+ lifetime = 300l ;
2222+ timestamp = Ptime_clock.now
2323+}
2424+2525+let serve_ssl port callback =
2626+2727+ let tag = "server" in
2828+2929+ X509_lwt.private_of_pems
3030+ ~cert:server_cert
3131+ ~priv_key:server_key >>= fun cert ->
3232+3333+ let server_s () =
3434+ let open Lwt_unix in
3535+ let s = socket PF_INET SOCK_STREAM 0 in
3636+ setsockopt s SO_REUSEADDR true ;
3737+ bind s (ADDR_INET (Unix.inet_addr_any, port)) >|= fun () ->
3838+ listen s 10 ;
3939+ s in
4040+4141+ let handle channels addr =
4242+ async @@ fun () ->
4343+ Lwt.catch (fun () -> callback channels addr >>= fun () -> yap ~tag "<- handler done")
4444+ (function
4545+ | Tls_lwt.Tls_alert a ->
4646+ yap ~tag @@ "handler: " ^ Tls.Packet.alert_type_to_string a
4747+ | Tls_lwt.Tls_failure a ->
4848+ yap ~tag @@ "handler: " ^ Tls.Engine.string_of_failure a
4949+ | Unix.Unix_error (e, f, p) ->
5050+ yap ~tag @@ "handler: " ^ (string_of_unix_err e f p)
5151+ | _exn -> yap ~tag "handler: exception")
5252+ in
5353+5454+ yap ~tag ("-> start @ " ^ string_of_int port) >>= fun () ->
5555+ let rec loop s =
5656+ let config = get_ok (Tls.Config.server ~ticket_cache ~reneg:true ~certificates:(`Single cert) ~version:(`TLS_1_2, `TLS_1_3) ~zero_rtt:32768l ()) in
5757+ (Lwt.catch
5858+ (fun () -> Tls_lwt.Unix.accept config s >|= fun r -> `R r)
5959+ (function
6060+ | Unix.Unix_error (e, f, p) -> return (`L (string_of_unix_err e f p))
6161+ | Tls_lwt.Tls_alert a -> return (`L (Tls.Packet.alert_type_to_string a))
6262+ | Tls_lwt.Tls_failure f -> return (`L (Tls.Engine.string_of_failure f))
6363+ | exn -> let str = Printexc.to_string exn in return (`L ("loop: exception " ^ str)))) >>= function
6464+ | `R (t, addr) ->
6565+ let channels = Tls_lwt.of_t t in
6666+ yap ~tag "-> connect" >>= fun () -> ( handle channels addr ; loop s )
6767+ | `L (msg) ->
6868+ yap ~tag ("server socket: " ^ msg) >>= fun () -> loop s
6969+ in
7070+ server_s () >>= fun s ->
7171+ loop s
7272+7373+let echo_server port =
7474+ serve_ssl port @@ fun (ic, oc) _addr ->
7575+ yap ~tag:"handler" "accepted" >>= fun () ->
7676+ let out = "HTTP/1.1 404 Not Found\r\n\r\n" in
7777+ Lwt_io.write_from_string_exactly oc out 0 (String.length out) >>= fun () ->
7878+ (* Lwt_io.close oc *)
7979+ let rec loop () =
8080+ Lwt_io.read_line ic >>= fun line ->
8181+ yap ~tag:"handler" ("+ " ^ line) >>= fun () ->
8282+ loop ()
8383+ in
8484+ loop ()
8585+8686+let jump _ port =
8787+ Lwt_main.run (echo_server port)
8888+8989+open Cmdliner
9090+9191+let port =
9292+ let doc = "Port to connect to" in
9393+ Arg.(value & opt int 4433 & info [ "port" ] ~doc)
9494+9595+let cmd =
9696+ let term = Term.(ret (const jump $ setup_log $ port))
9797+ and info = Cmd.info "fuzz_server" ~version:"%%VERSION_NUM%%"
9898+ in
9999+ Cmd.v info term
100100+101101+let () = exit (Cmd.eval cmd)
+29
vendor/opam/tls/lwt/examples/http_client.ml
···11+22+open Lwt
33+open Ex_common
44+55+let http_client ?ca ?fp hostname port =
66+ let port = int_of_string port in
77+ auth ?ca ?fp () >>= fun authenticator ->
88+ Tls_lwt.connect_ext
99+ (get_ok (Tls.Config.client ~authenticator ()))
1010+ (hostname, port) >>= fun (ic, oc) ->
1111+ let req = String.concat "\r\n" [
1212+ "GET / HTTP/1.1" ; "Host: " ^ hostname ; "Connection: close" ; "" ; ""
1313+ ] in
1414+ Lwt_io.(write oc req >>= fun () -> read ic >>= print >>= fun () -> printf "++ done.\n%!")
1515+1616+let () =
1717+ try
1818+ match Sys.argv with
1919+ | [| _ ; host ; port ; "FP" ; fp |] -> Lwt_main.run (http_client host port ~fp)
2020+ | [| _ ; host ; port ; trust |] -> Lwt_main.run (http_client host port ~ca:trust)
2121+ | [| _ ; host ; port |] -> Lwt_main.run (http_client host port)
2222+ | [| _ ; host |] -> Lwt_main.run (http_client host "443")
2323+ | args -> Printf.eprintf "%s <host> <port>\n%!" args.(0)
2424+ with
2525+ | Tls_lwt.Tls_alert alert as exn ->
2626+ print_alert "remote end" alert ; raise exn
2727+ | Tls_lwt.Tls_failure fail as exn ->
2828+ print_fail "our end" fail ; raise exn
2929+
+39
vendor/opam/tls/lwt/examples/resume_client.ml
···11+22+open Lwt
33+open Ex_common
44+55+let http_client ?ca ?fp hostname port =
66+ let port = int_of_string port in
77+ auth ?ca ?fp () >>= fun authenticator ->
88+ let config = get_ok (Tls.Config.client ~authenticator ()) in
99+ Tls_lwt.Unix.connect config (hostname, port) >>= fun t ->
1010+ Tls_lwt.Unix.write t "foo\n" >>= fun () ->
1111+ let cs = Bytes.create 4 in
1212+ Tls_lwt.Unix.read t cs >>= fun _len ->
1313+ let cached_session = match Tls_lwt.Unix.epoch t with
1414+ | Ok e -> e
1515+ | Error () -> invalid_arg "error retrieving epoch"
1616+ in
1717+ Tls_lwt.Unix.close t >>= fun () ->
1818+ Printf.printf "closed session\n" ;
1919+ let config = get_ok (Tls.Config.client ~authenticator ~cached_session ()) in
2020+ Tls_lwt.connect_ext config (hostname, port) >>= fun (ic, oc) ->
2121+ let req = String.concat "\r\n" [
2222+ "GET / HTTP/1.1" ; "Host: " ^ hostname ; "Connection: close" ; "" ; ""
2323+ ] in
2424+ Lwt_io.(write oc req >>= fun () -> read ic >>= print >>= fun () -> printf "++ done.\n%!")
2525+2626+let () =
2727+ try
2828+ match Sys.argv with
2929+ | [| _ ; host ; port ; "FP" ; fp |] -> Lwt_main.run (http_client host port ~fp)
3030+ | [| _ ; host ; port ; trust |] -> Lwt_main.run (http_client host port ~ca:trust)
3131+ | [| _ ; host ; port |] -> Lwt_main.run (http_client host port)
3232+ | [| _ ; host |] -> Lwt_main.run (http_client host "443")
3333+ | args -> Printf.eprintf "%s <host> <port>\n%!" args.(0)
3434+ with
3535+ | Tls_lwt.Tls_alert alert as exn ->
3636+ print_alert "remote end" alert ; raise exn
3737+ | Tls_lwt.Tls_failure fail as exn ->
3838+ print_fail "our end" fail ; raise exn
3939+
···11+open Lwt
22+open Ex_common
33+44+let string_of_unix_err err f p =
55+ Printf.sprintf "Unix_error (%s, %s, %s)"
66+ (Unix.error_message err) f p
77+88+99+module HT = Hashtbl.Make (Tls.Core.PreSharedKeyID)
1010+let cache_psk, psk_cache =
1111+ let cache = HT.create 7 in
1212+ ((fun psk ed -> HT.add cache psk.Tls.Core.identifier (psk, ed)),
1313+ HT.find_opt cache)
1414+1515+let ticket_cache = {
1616+ Tls.Config.lookup = psk_cache ;
1717+ ticket_granted = cache_psk ;
1818+ lifetime = 300l ;
1919+ timestamp = Ptime_clock.now
2020+}
2121+2222+let serve_ssl port callback =
2323+2424+ let tag = "server" in
2525+2626+ X509_lwt.private_of_pems
2727+ ~cert:server_cert
2828+ ~priv_key:server_key >>= fun cert ->
2929+3030+ let hex = Ohex.decode in
3131+ let epoch =
3232+ {
3333+ Tls.Core.side = `Client ;
3434+ state = `Established ;
3535+ protocol_version = `TLS_1_3 ;
3636+ ciphersuite = `DHE_RSA_WITH_AES_128_GCM_SHA256 ;
3737+ peer_random = hex "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ;
3838+ peer_certificate_chain = [] ;
3939+ peer_certificate = None ;
4040+ peer_name = None ;
4141+ trust_anchor = None ;
4242+ received_certificates = [] ;
4343+ own_random = hex "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ;
4444+ own_certificate = fst cert ;
4545+ own_private_key = Some (snd cert) ;
4646+ own_name = Some Domain_name.(host_exn (of_string_exn "tls13test.nqsb.io")) ;
4747+ master_secret = hex "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ;
4848+ exporter_master_secret = "" ;
4949+ session_id = "" ;
5050+ extended_ms = true ;
5151+ alpn_protocol = None ;
5252+ tls_unique = None ;
5353+ }
5454+ and psk = {
5555+ Tls.Core.identifier = hex "0000" ;
5656+ obfuscation = Randomconv.int32 Mirage_crypto_rng.generate ;
5757+ secret = hex "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ;
5858+ lifetime = 300l ;
5959+ early_data = 0l ;
6060+ issued_at = Ptime_clock.now ();
6161+ }
6262+ in
6363+ cache_psk psk epoch ;
6464+6565+ let server_s () =
6666+ let open Lwt_unix in
6767+ let s = socket PF_INET SOCK_STREAM 0 in
6868+ setsockopt s SO_REUSEADDR true ;
6969+ bind s (ADDR_INET (Unix.inet_addr_any, port)) >|= fun () ->
7070+ listen s 10 ;
7171+ s in
7272+7373+ let handle channels =
7474+ async @@ fun () ->
7575+ Lwt.catch (fun () -> callback channels >>= fun () -> yap ~tag "<- handler done")
7676+ (function
7777+ | Tls_lwt.Tls_alert a ->
7878+ yap ~tag @@ "handler: " ^ Tls.Packet.alert_type_to_string a
7979+ | Tls_lwt.Tls_failure a ->
8080+ yap ~tag @@ "handler: " ^ Tls.Engine.string_of_failure a
8181+ | Unix.Unix_error (e, f, p) ->
8282+ yap ~tag @@ "handler: " ^ (string_of_unix_err e f p)
8383+ | _exn -> yap ~tag "handler: exception")
8484+ in
8585+8686+ yap ~tag ("-> start @ " ^ string_of_int port) >>= fun () ->
8787+ let rec loop s =
8888+ let authenticator ?ip:_ ~host:_ _ = Ok None in
8989+ let config = get_ok (Tls.Config.server ~certificates:(`Single cert) ~ticket_cache ~authenticator ()) in
9090+ (Lwt.catch
9191+ (fun () ->
9292+ Lwt_unix.accept s >>= fun (s, addr) ->
9393+ let txt = Unix.(match addr with
9494+ | ADDR_UNIX x -> "unix-" ^ x
9595+ | ADDR_INET (ip, p) -> string_of_inet_addr ip ^ ":" ^ string_of_int p)
9696+ in
9797+ yap ~tag:"client-connect" txt >>= fun () ->
9898+ Tls_lwt.Unix.server_of_fd config s >|= fun t -> `R t)
9999+ (function
100100+ | Unix.Unix_error (e, f, p) -> return (`L (string_of_unix_err e f p))
101101+ | Tls_lwt.Tls_alert a -> return (`L (Tls.Packet.alert_type_to_string a))
102102+ | Tls_lwt.Tls_failure f -> return (`L (Tls.Engine.string_of_failure f))
103103+ | exn -> let str = Printexc.to_string exn in return (`L ("loop: exception " ^ str)))) >>= function
104104+ | `R t ->
105105+ yap ~tag "-> connect" >>= fun () ->
106106+ handle (Tls_lwt.of_t t); loop s
107107+ | `L msg ->
108108+ yap ~tag ("server socket: " ^ msg) >>= fun () -> loop s
109109+ in
110110+ server_s () >>= fun s ->
111111+ loop s
112112+113113+let echo_server port =
114114+ serve_ssl port @@ fun (ic, oc) ->
115115+ lines ic |> Lwt_stream.iter_s (fun line ->
116116+ yap ~tag:"handler" ("+ " ^ string_of_int (String.length line)) >>= fun () ->
117117+ Lwt_io.write_line oc line)
118118+119119+let () =
120120+ let port =
121121+ try int_of_string Sys.argv.(1) with _ -> 4433
122122+ in
123123+ Lwt_main.run (echo_server port)
+74
vendor/opam/tls/lwt/examples/starttls_server.ml
···11+open Lwt.Infix
22+open Ex_common
33+44+let capability = "[CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE STARTTLS AUTH=PLAIN] server ready.\r\n"
55+66+let ok_starttls = "OK STARTTLS\r\n"
77+88+let cert () =
99+ X509_lwt.private_of_pems
1010+ ~cert:"./certificates/server.pem"
1111+ ~priv_key:"./certificates/server.key"
1212+1313+let init_socket addr port =
1414+ let sockaddr = Unix.ADDR_INET (Unix.inet_addr_of_string addr, port) in
1515+ let socket = Lwt_unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
1616+ Lwt_unix.setsockopt socket Unix.SO_REUSEADDR true;
1717+ Lwt_unix.bind socket sockaddr >|= fun () ->
1818+ socket
1919+2020+let create_srv_socket addr port =
2121+ init_socket addr port >|= fun socket ->
2222+ Lwt_unix.listen socket 10;
2323+ socket
2424+2525+let accept sock =
2626+ Lwt_unix.accept sock >>= fun (sock_cl, addr) ->
2727+ let ic = Lwt_io.of_fd ~close:(fun () -> Lwt.return_unit) ~mode:Lwt_io.input sock_cl in
2828+ let oc = Lwt_io.of_fd ~close:(fun () -> Lwt.return_unit) ~mode:Lwt_io.output sock_cl in
2929+ Lwt.return ((ic,oc), addr, sock_cl)
3030+3131+let start_server () =
3232+ let write oc buff =
3333+ Lwt_io.write oc buff >>= fun () -> Lwt_io.flush oc
3434+ in
3535+ let read ic =
3636+ Lwt_io.read ic ~count:2048 >>= fun buff ->
3737+ Printf.printf "%s%!" buff;
3838+ Lwt.return buff
3939+ in
4040+ let parse buff =
4141+ match String.index buff ' ' with
4242+ | exception Not_found -> "", ""
4343+ | idx ->
4444+ let l = String.length buff in
4545+ String.sub buff 0 idx, String.sub buff (succ idx) (l - succ idx)
4646+ in
4747+ let rec wait_cmd sock_cl ic oc =
4848+ read ic >>= fun buff ->
4949+ let tag,cmd = parse buff in
5050+ match cmd with
5151+ | "CAPABILITY" ->
5252+ write oc ("* " ^ capability ^ tag ^ " OK CAPABILITY\r\n") >>= fun () ->
5353+ wait_cmd sock_cl ic oc
5454+ | "STARTTLS" ->
5555+ write oc (tag ^ ok_starttls) >>= fun () ->
5656+ Lwt_io.close ic >>= fun () ->
5757+ Lwt_io.close oc >>= fun () ->
5858+ cert () >>= fun cert ->
5959+ Tls_lwt.Unix.server_of_fd
6060+ (get_ok (Tls.Config.server ~certificates:(`Single cert) ())) sock_cl >>= fun s ->
6161+ let ic,oc = Tls_lwt.of_t s in
6262+ write oc ("* OK " ^ capability) >>= fun () ->
6363+ wait_cmd sock_cl ic oc
6464+ | _ ->
6565+ write oc ("BAD\r\n") >>= fun () ->
6666+ wait_cmd sock_cl ic oc
6767+ in
6868+ create_srv_socket "127.0.0.1" 143 >>= fun sock ->
6969+ accept sock >>= fun ((ic,oc), _addr, sock_cl) ->
7070+ write oc ("* OK " ^ capability) >>= fun () ->
7171+ wait_cmd sock_cl ic oc
7272+7373+let () =
7474+ Lwt_main.run (start_server ())
+49
vendor/opam/tls/lwt/examples/test_client.ml
···11+open Lwt
22+open Ex_common
33+44+let mypsk = ref None
55+66+let ticket_cache = {
77+ Tls.Config.lookup = (fun _ -> None) ;
88+ ticket_granted = (fun psk epoch -> mypsk := Some (psk, epoch)) ;
99+ lifetime = 0l ;
1010+ timestamp = Ptime_clock.now
1111+}
1212+1313+let test_client _ =
1414+(* X509_lwt.private_of_pems
1515+ ~cert:server_cert
1616+ ~priv_key:server_key >>= fun cert -> *)
1717+ let port = 4433 in
1818+ let host = "127.0.0.1" in
1919+ let authenticator = null_auth in
2020+ Tls_lwt.Unix.connect
2121+ (get_ok Tls.Config.(client ~version:(`TLS_1_0, `TLS_1_3) (* ~certificates:(`Single cert) *) ?cached_ticket:!mypsk ~ticket_cache ~authenticator ~ciphers:Ciphers.supported ()))
2222+ (host, port) >>= fun t ->
2323+ let (ic, oc) = Tls_lwt.of_t t in
2424+ let req = String.concat "\r\n" [
2525+ "GET / HTTP/1.1" ; "Host: " ^ host ; "Connection: close" ; "" ; ""
2626+ ] in
2727+ Lwt_io.(write oc req >>= fun () ->
2828+ read ~count:3 ic >>= print >>= fun () ->
2929+ close oc >>= fun () ->
3030+ printf "++ done.\n%!")
3131+3232+let jump _ =
3333+ try
3434+ Lwt_main.run (test_client ()) ; `Ok ()
3535+ with
3636+ | Tls_lwt.Tls_alert alert as exn ->
3737+ print_alert "remote end" alert ; raise exn
3838+ | Tls_lwt.Tls_failure alert as exn ->
3939+ print_fail "our end" alert ; raise exn
4040+4141+open Cmdliner
4242+4343+let cmd =
4444+ let term = Term.(ret (const jump $ setup_log))
4545+ and info = Cmd.info "test_client" ~version:"%%VERSION_NUM%%"
4646+ in
4747+ Cmd.v info term
4848+4949+let () = exit (Cmd.eval cmd)
+47
vendor/opam/tls/lwt/examples/test_server.ml
···11+22+open Lwt
33+open Ex_common
44+55+let serve_ssl port callback =
66+77+ let tag = "server" in
88+99+ X509_lwt.private_of_pems
1010+ ~cert:server_cert
1111+ ~priv_key:server_key >>= fun certificate ->
1212+ X509_lwt.private_of_pems
1313+ ~cert:server_ec_cert
1414+ ~priv_key:server_ec_key >>= fun ec_certificate ->
1515+ let certificates = `Multiple [ certificate ; ec_certificate ] in
1616+ let config =
1717+ get_ok (Tls.Config.(server ~version:(`TLS_1_0, `TLS_1_3) ~certificates ~ciphers:Ciphers.supported ()))
1818+ in
1919+2020+ let server_s =
2121+ let open Lwt_unix in
2222+ let s = socket PF_INET SOCK_STREAM 0 in
2323+ setsockopt s Unix.SO_REUSEADDR true ;
2424+ bind s (ADDR_INET (Unix.inet_addr_any, port)) >|= fun () ->
2525+ listen s 10 ;
2626+ s in
2727+2828+ yap ~tag ("-> start @ " ^ string_of_int port) >>= fun () ->
2929+ server_s >>= fun s ->
3030+ Tls_lwt.Unix.accept config s >>= fun (t, addr) ->
3131+ let channels = Tls_lwt.of_t t in
3232+ yap ~tag "-> connect" >>= fun () ->
3333+ callback channels addr >>= fun () ->
3434+ yap ~tag "<- handler done"
3535+3636+let test_server port =
3737+ serve_ssl port @@ fun (ic, oc) _addr ->
3838+ yap ~tag:"handler" "accepted" >>= fun () ->
3939+ Lwt_io.read_line ic >>= fun line ->
4040+ yap ~tag:"handler" ("+ " ^ line) >>= fun () ->
4141+ Lwt_io.write_line oc line
4242+4343+let () =
4444+ let port =
4545+ try int_of_string Sys.argv.(1) with _ -> 4433
4646+ in
4747+ Lwt_main.run (test_server port)
+70
vendor/opam/tls/lwt/examples/tls_over_tls.ml
···11+open Lwt
22+open Ex_common
33+44+let hostname = "mirage.io"
55+66+let proxy = "127.0.0.1", 3129
77+88+(* To test TLS-over-TLS, the `squid` proxy can be installed locally and configured to support HTTPS:
99+1010+- Generate a certificate for localhost: https://gist.github.com/cecilemuller/9492b848eb8fe46d462abeb26656c4f8
1111+1212+$ openssl req -x509 -nodes -new -sha256 -days 1024 -newkey rsa:2048 -keyout RootCA.key -out RootCA.pem -subj "/C=US/CN=Example-Root-CA"
1313+$ openssl x509 -outform pem -in RootCA.pem -out RootCA.crt
1414+$ cat <<EOF > domains.ext
1515+authorityKeyIdentifier=keyid,issuer
1616+basicConstraints=CA:FALSE
1717+keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
1818+subjectAltName = @alt_names
1919+[alt_names]
2020+DNS.1 = localhost
2121+EOF
2222+$ openssl req -new -nodes -newkey rsa:2048 -keyout localhost.key -out localhost.csr -subj "/C=US/ST=YourState/L=YourCity/O=Example-Certificates/CN=localhost.local"
2323+$ openssl x509 -req -sha256 -days 1024 -in localhost.csr -CA RootCA.pem -CAkey RootCA.key -CAcreateserial -extfile domains.ext -out localhost.crt
2424+2525+- Configure squid by adding HTTPS support on port 3129 in /etc/squid/squid.conf :
2626+2727+https_port 3129 tls-cert=/path/to/localhost.crt tls-key=/path/to/localhost.key
2828+2929+*)
3030+3131+let client = get_ok (Tls.Config.client ~authenticator:null_auth ())
3232+3333+let string_prefix ~prefix msg =
3434+ let len = String.length prefix in
3535+ String.length msg >= len && String.sub msg 0 len = prefix
3636+3737+let host = Result.get_ok (Domain_name.of_string hostname)
3838+let host = Result.get_ok (Domain_name.host host)
3939+4040+let test_client _ =
4141+ (* Connect to proxy *)
4242+ Tls_lwt.Unix.connect client proxy >>= fun t ->
4343+ let (ic, oc) = Tls_lwt.of_t t in
4444+4545+ (* Request proxy to connect to hostname *)
4646+ let req =
4747+ Printf.sprintf "CONNECT %s:443 HTTP/1.1\r\nHost: %s\r\n\r\n"
4848+ hostname hostname
4949+ in
5050+ Lwt_io.write oc req >>= fun () ->
5151+ Lwt_io.read ic ~count:1024 >>= fun msg ->
5252+ assert (string_prefix ~prefix:"HTTP/1.1 200 " msg) ;
5353+5454+ (* TLS with hostname, over the TLS connection with the proxy *)
5555+ Tls_lwt.Unix.client_of_channels client ~host (ic, oc) >>= fun t ->
5656+ let (ic, oc) = Tls_lwt.of_t t in
5757+5858+ (* Request homepage from host *)
5959+ let req =
6060+ Printf.sprintf "GET / HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n"
6161+ hostname
6262+ in
6363+6464+ Lwt_io.(write oc req >>= fun () ->
6565+ read ~count:1024 ic >>= print >>= fun () ->
6666+ read ~count:1024 ic >>= print >>= fun () ->
6767+ close oc >>= fun () ->
6868+ printf "++ done.\n%!")
6969+7070+let () = Lwt_main.run (test_client ())
+364
vendor/opam/tls/lwt/tls_lwt.ml
···11+open Lwt.Infix
22+33+exception Tls_alert of Tls.Packet.alert_type
44+exception Tls_failure of Tls.Engine.failure
55+66+(* This really belongs just about anywhere else: generic unix name resolution. *)
77+let resolve host service =
88+ let open Lwt_unix in
99+ getprotobyname "tcp" >>= fun tcp ->
1010+ getaddrinfo host service [AI_PROTOCOL tcp.p_proto] >>= function
1111+ | [] ->
1212+ let msg = Printf.sprintf "no address for %s:%s" host service in
1313+ Lwt.reraise (Invalid_argument msg)
1414+ | ai::_ -> Lwt.return ai.ai_addr
1515+1616+module Lwt_cs = struct
1717+1818+ let naked ~name f fd cs off len =
1919+ f fd cs off len >>= fun res ->
2020+ match Lwt_unix.getsockopt_error fd with
2121+ | None -> Lwt.return res
2222+ | Some err -> Lwt.reraise @@ Unix.Unix_error (err, name, "")
2323+2424+ let write = naked ~name:"Tls_lwt.write" Lwt_unix.write
2525+ and read = naked ~name:"Tls_lwt.read" Lwt_unix.read
2626+2727+ let rec write_full ?(off = 0) ?len fd buf =
2828+ let len = Option.value ~default:(String.length buf - off) len in
2929+ if len = 0 then
3030+ Lwt.return_unit
3131+ else
3232+ write fd (Bytes.unsafe_of_string buf) off len >>= fun written ->
3333+ write_full ~off:(off + written) ~len:(len - written) fd buf
3434+3535+ let read fd buf = read fd buf 0 (Bytes.length buf)
3636+end
3737+3838+module Lwt_fd = struct
3939+ type t = {
4040+ read : bytes -> int Lwt.t ;
4141+ write : string -> unit Lwt.t ;
4242+ close : unit -> unit Lwt.t ;
4343+ }
4444+4545+ let read t cs = t.read cs
4646+ let write t cs = t.write cs
4747+ let close t = t.close ()
4848+4949+ let of_fd fd =
5050+ let close () =
5151+ (* (partially) avoid double-closes by checking if the fd has already been closed *)
5252+ match Lwt_unix.state fd with
5353+ | Lwt_unix.Closed -> Lwt.return_unit
5454+ | Lwt_unix.Opened | Lwt_unix.Aborted _ -> Lwt_unix.close fd
5555+ in
5656+ {
5757+ read = Lwt_cs.read fd ;
5858+ write = Lwt_cs.write_full fd ;
5959+ close = close ;
6060+ }
6161+6262+ let of_channels ic oc =
6363+ {
6464+ read = (fun bs -> Lwt_io.read_into ic bs 0 (Bytes.length bs)) ;
6565+ write = (Lwt_io.write oc) ;
6666+ close = (fun () -> Lwt_io.close oc <&> Lwt_io.close ic) ;
6767+ }
6868+end
6969+7070+module Unix = struct
7171+7272+ type t = {
7373+ fd : Lwt_fd.t ;
7474+ mutable state : [ `Active of Tls.Engine.state
7575+ | `Read_closed of Tls.Engine.state
7676+ | `Write_closed of Tls.Engine.state
7777+ | `Closed
7878+ | `Error of exn ] ;
7979+ mutable linger : string option ;
8080+ recv_buf : bytes ;
8181+ }
8282+8383+ let half_close state mode =
8484+ match state, mode with
8585+ | `Active tls, `read -> `Read_closed tls
8686+ | `Active tls, `write -> `Write_closed tls
8787+ | `Active _, `read_write -> `Closed
8888+ | `Read_closed tls, `read -> `Read_closed tls
8989+ | `Read_closed _, (`write | `read_write) -> `Closed
9090+ | `Write_closed tls, `write -> `Write_closed tls
9191+ | `Write_closed _, (`read | `read_write) -> `Closed
9292+ | (`Closed | `Error _) as e, (`read | `write | `read_write) -> e
9393+9494+ let inject_state tls = function
9595+ | `Active _ -> `Active tls
9696+ | `Read_closed _ -> `Read_closed tls
9797+ | `Write_closed _ -> `Write_closed tls
9898+ | (`Closed | `Error _) as e -> e
9999+100100+ let safely th =
101101+ Lwt.catch
102102+ (fun () -> th >>= fun _ -> Lwt.return_unit)
103103+ (function
104104+ | Out_of_memory -> raise Out_of_memory
105105+ | _ -> Lwt.return_unit)
106106+107107+ let (read_t, write_t) =
108108+ let recording_errors op t cs =
109109+ Lwt.catch
110110+ (fun () -> op t.fd cs)
111111+ (function
112112+ | Out_of_memory -> raise Out_of_memory
113113+ | exn -> (match t.state with
114114+ | `Error _ -> ()
115115+ | _ -> t.state <- `Error exn) ;
116116+ Lwt.reraise exn)
117117+ in
118118+ (recording_errors Lwt_fd.read, recording_errors Lwt_fd.write)
119119+120120+ let when_some f = function None -> Lwt.return_unit | Some x -> f x
121121+122122+ let rec read_react t =
123123+124124+ let handle tls buf =
125125+ match Tls.Engine.handle_tls tls buf with
126126+ | Ok (state', eof, `Response resp, `Data data) ->
127127+ let state' = inject_state state' t.state in
128128+ let state' = Option.(value ~default:state' (map (fun `Eof -> half_close state' `read) eof)) in
129129+ t.state <- state' ;
130130+ safely (resp |> when_some (write_t t)) >|= fun () ->
131131+ `Ok data
132132+133133+ | Error (fail, `Response resp) ->
134134+ t.state <- `Error (match fail with
135135+ | `Alert a -> Tls_alert a
136136+ | f -> Tls_failure f);
137137+ write_t t resp >>= fun () -> read_react t
138138+ in
139139+140140+ match t.state with
141141+ | `Error e -> Lwt.reraise e
142142+ | `Closed
143143+ | `Read_closed _ -> Lwt.return `Eof
144144+ | _ ->
145145+ read_t t t.recv_buf >>= function
146146+ | 0 ->
147147+ t.state <- half_close t.state `read;
148148+ Lwt.return `Eof
149149+ | n ->
150150+ match t.state with
151151+ | `Error e -> Lwt.reraise e
152152+ | `Active tls | `Read_closed tls | `Write_closed tls ->
153153+ handle tls (String.sub (Bytes.unsafe_to_string t.recv_buf) 0 n)
154154+ | `Closed -> Lwt.return `Eof
155155+156156+ let rec read t ?(off = 0) buf =
157157+ if off < 0 || off >= Bytes.length buf then
158158+ invalid_arg "offset must be >= 0 and < Bytes.length buf";
159159+160160+ let writeout res =
161161+ let rlen = String.length res in
162162+ let n = min (Bytes.length buf - off) rlen in
163163+ Bytes.blit_string res 0 buf off n ;
164164+ t.linger <-
165165+ (if n < rlen then Some (String.sub res n (rlen - n)) else None) ;
166166+ Lwt.return n in
167167+168168+ match t.linger with
169169+ | Some res -> writeout res
170170+ | None ->
171171+ read_react t >>= function
172172+ | `Eof -> Lwt.return 0
173173+ | `Ok None -> read t ~off buf
174174+ | `Ok (Some res) -> writeout res
175175+176176+ let writev t css =
177177+ match t.state with
178178+ | `Error err -> Lwt.reraise err
179179+ | `Write_closed _ | `Closed -> Lwt.reraise @@ Invalid_argument "tls: closed socket"
180180+ | `Active tls | `Read_closed tls ->
181181+ match Tls.Engine.send_application_data tls css with
182182+ | Some (tls, tlsdata) ->
183183+ ( t.state <- inject_state tls t.state ; write_t t tlsdata )
184184+ | None -> Lwt.reraise @@ Invalid_argument "tls: write: socket not ready"
185185+186186+ let write t cs = writev t [cs]
187187+188188+ (*
189189+ * XXX bad XXX
190190+ * This is a point that should particularly be protected from concurrent r/w.
191191+ * Doing this before a `t` is returned is safe; redoing it during rekeying is
192192+ * not, as the API client already sees the `t` and can mistakenly interleave
193193+ * writes while this is in progress.
194194+ * *)
195195+ let rec drain_handshake t =
196196+ let push_linger t mcs =
197197+ match (mcs, t.linger) with
198198+ | (None, _) -> ()
199199+ | (scs, None) -> t.linger <- scs
200200+ | (Some cs, Some l) -> t.linger <- Some (l ^ cs)
201201+ in
202202+ match t.state with
203203+ | `Active tls when not (Tls.Engine.handshake_in_progress tls) ->
204204+ Lwt.return t
205205+ | _ ->
206206+ read_react t >>= function
207207+ | `Eof -> Lwt.reraise End_of_file
208208+ | `Ok cs -> push_linger t cs ; drain_handshake t
209209+210210+ let reneg ?authenticator ?acceptable_cas ?cert ?(drop = true) t =
211211+ match t.state with
212212+ | `Error err -> Lwt.reraise err
213213+ | `Closed | `Read_closed _ | `Write_closed _ ->
214214+ Lwt.reraise @@ Invalid_argument "tls: closed socket"
215215+ | `Active tls ->
216216+ match Tls.Engine.reneg ?authenticator ?acceptable_cas ?cert tls with
217217+ | None -> Lwt.reraise @@ Invalid_argument "tls: can't renegotiate"
218218+ | Some (tls', buf) ->
219219+ if drop then t.linger <- None ;
220220+ t.state <- inject_state tls' t.state ;
221221+ write_t t buf >>= fun () ->
222222+ drain_handshake t >>= fun _ ->
223223+ Lwt.return_unit
224224+225225+ let key_update ?request t =
226226+ match t.state with
227227+ | `Error err -> Lwt.reraise err
228228+ | `Write_closed _ | `Closed -> Lwt.reraise @@ Invalid_argument "tls: closed socket"
229229+ | `Active tls | `Read_closed tls ->
230230+ match Tls.Engine.key_update ?request tls with
231231+ | Error f -> Lwt.reraise @@ Invalid_argument (Format.asprintf "tls: can't update key: %a" Tls.Engine.pp_failure f)
232232+ | Ok (tls', buf) ->
233233+ t.state <- inject_state tls' t.state ;
234234+ write_t t buf
235235+236236+ let shutdown t mode =
237237+ (match mode with
238238+ | `read -> Lwt.return_unit
239239+ | `write | `read_write ->
240240+ match t.state with
241241+ | `Active tls | `Read_closed tls ->
242242+ let tls', buf = Tls.Engine.send_close_notify tls in
243243+ t.state <- inject_state tls' (half_close t.state `write) ;
244244+ write_t t buf
245245+ | _ -> Lwt.return_unit) >>= fun () ->
246246+ t.state <- half_close t.state mode;
247247+ match t.state with
248248+ | `Closed | `Error _ -> safely (Lwt_fd.close t.fd)
249249+ | _ -> Lwt.return_unit
250250+251251+ let close t = shutdown t `read_write
252252+253253+ let server_of_fd config fd =
254254+ drain_handshake {
255255+ state = `Active (Tls.Engine.server config) ;
256256+ fd = fd ;
257257+ linger = None ;
258258+ recv_buf = Bytes.create 4096
259259+ }
260260+261261+ let server_of_channels config (ic, oc) =
262262+ server_of_fd config (Lwt_fd.of_channels ic oc)
263263+264264+ let server_of_fd config fd =
265265+ server_of_fd config (Lwt_fd.of_fd fd)
266266+267267+ let client_of_fd config ?host fd =
268268+ let config' = match host with
269269+ | None -> config
270270+ | Some host -> Tls.Config.peer config host
271271+ in
272272+ let (tls, init) = Tls.Engine.client config' in
273273+ let t = {
274274+ state = `Active tls ;
275275+ fd = fd ;
276276+ linger = None ;
277277+ recv_buf = Bytes.create 4096
278278+ }
279279+ in
280280+ write_t t init >>= fun () ->
281281+ drain_handshake t
282282+283283+ let client_of_channels config ?host (ic, oc) =
284284+ client_of_fd config ?host (Lwt_fd.of_channels ic oc)
285285+286286+ let client_of_fd config ?host fd =
287287+ client_of_fd config ?host (Lwt_fd.of_fd fd)
288288+289289+ let accept conf fd =
290290+ Lwt_unix.accept fd >>= fun (fd', addr) ->
291291+ Lwt.catch (fun () -> server_of_fd conf fd' >|= fun t -> (t, addr))
292292+ (function
293293+ | Out_of_memory -> raise Out_of_memory
294294+ | exn -> safely (Lwt_unix.close fd') >>= fun () -> Lwt.reraise exn)
295295+296296+ let connect conf (host, port) =
297297+ resolve host (string_of_int port) >>= fun addr ->
298298+ let fd = Lwt_unix.(socket (Unix.domain_of_sockaddr addr) SOCK_STREAM 0) in
299299+ Lwt.catch (fun () ->
300300+ let host =
301301+ Result.to_option
302302+ (Result.bind (Domain_name.of_string host) Domain_name.host)
303303+ in
304304+ Lwt_unix.connect fd addr >>= fun () -> client_of_fd conf ?host fd)
305305+ (function
306306+ | Out_of_memory -> raise Out_of_memory
307307+ | exn -> safely (Lwt_unix.close fd) >>= fun () -> Lwt.reraise exn)
308308+309309+ let read_bytes t bs off len =
310310+ let buf = Bytes.create len in
311311+ read t buf >|= fun n ->
312312+ let to_copy = min n len in
313313+ Lwt_bytes.blit_from_bytes buf 0 bs off to_copy;
314314+ to_copy
315315+316316+ let write_bytes t bs off len =
317317+ let buf = Bytes.create len in
318318+ Lwt_bytes.blit_to_bytes bs off buf 0 len;
319319+ write t (Bytes.unsafe_to_string buf)
320320+321321+ let epoch t =
322322+ match t.state with
323323+ | `Active tls | `Read_closed tls | `Write_closed tls -> Tls.Engine.epoch tls
324324+ | `Closed | `Error _ -> Error ()
325325+end
326326+327327+type ic = Lwt_io.input_channel
328328+type oc = Lwt_io.output_channel
329329+330330+let of_t ?close t =
331331+ let close = match close with
332332+ | Some f -> (fun () -> Unix.safely (f ()))
333333+ | None -> (fun () -> Unix.(safely (close t)))
334334+ in
335335+ (Lwt_io.make ~close ~mode:Lwt_io.Input (Unix.read_bytes t)),
336336+ (Lwt_io.make ~close ~mode:Lwt_io.Output @@
337337+ fun a b c -> Unix.write_bytes t a b c >>= fun () -> Lwt.return c)
338338+339339+let accept_ext conf fd =
340340+ Unix.accept conf fd >|= fun (t, peer) -> (of_t t, peer)
341341+342342+and connect_ext conf addr =
343343+ Unix.connect conf addr >|= of_t
344344+345345+let accept certificate fd =
346346+ match Tls.Config.server ~certificates:certificate () with
347347+ | Ok config -> accept_ext config fd >|= fun w -> Ok w
348348+ | Error _ as e -> Lwt.return e
349349+350350+and connect authenticator addr =
351351+ match Tls.Config.client ~authenticator () with
352352+ | Ok config -> connect_ext config addr >|= fun w -> Ok w
353353+ | Error _ as e -> Lwt.return e
354354+355355+(* Boot the entropy loop at module init time. *)
356356+let () = Mirage_crypto_rng_unix.use_default ()
357357+358358+let () =
359359+ Printexc.register_printer (function
360360+ | Tls_alert typ ->
361361+ Some ("TLS alert from peer: " ^ Tls.Packet.alert_type_to_string typ)
362362+ | Tls_failure f ->
363363+ Some ("TLS failure: " ^ Tls.Engine.string_of_failure f)
364364+ | _ -> None)
+129
vendor/opam/tls/lwt/tls_lwt.mli
···11+(** Effectful operations using Lwt for pure TLS.
22+33+ The pure TLS is state and buffer in, state and buffer out. This
44+ module uses Lwt for communication over the network.
55+66+ This module implements a high-level API and a low-level API (in
77+ {!Unix}). Most applications should use the high-level API described below. *)
88+99+(** [Tls_alert] exception received from the other endpoint *)
1010+exception Tls_alert of Tls.Packet.alert_type
1111+1212+(** [Tls_failure] exception while processing incoming data *)
1313+exception Tls_failure of Tls.Engine.failure
1414+1515+(** Low-level API *)
1616+module Unix : sig
1717+1818+ (** {1 Unix API} *)
1919+2020+ (** It is the responsibility of the client to handle error
2121+ conditions. The underlying file descriptors are not closed. *)
2222+2323+ (** Abstract type of a session *)
2424+ type t
2525+2626+ (** {2 Constructors} *)
2727+2828+ (** [server_of_fd server fd] is [t], after server-side TLS
2929+ handshake of [fd] using [server] configuration. *)
3030+ val server_of_fd : Tls.Config.server -> Lwt_unix.file_descr -> t Lwt.t
3131+3232+ (** [server_of_channels server (ic, oc)] is [t], after server-side TLS
3333+ handshake on the input/output channels [ic, oc] using [server] configuration. *)
3434+ val server_of_channels : Tls.Config.server -> Lwt_io.input_channel * Lwt_io.output_channel -> t Lwt.t
3535+3636+ (** [client_of_fd client ~host fd] is [t], after client-side
3737+ TLS handshake of [fd] using [client] configuration and [host]. *)
3838+ val client_of_fd : Tls.Config.client -> ?host:[ `host ] Domain_name.t -> Lwt_unix.file_descr -> t Lwt.t
3939+4040+ (** [client_of_channels client ~host (ic, oc)] is [t], after client-side
4141+ TLS handshake over the input/output channels [ic, oc] using [client] configuration and [host]. *)
4242+ val client_of_channels : Tls.Config.client -> ?host:[ `host ] Domain_name.t -> Lwt_io.input_channel * Lwt_io.output_channel -> t Lwt.t
4343+4444+ (** [accept server fd] is [t, sockaddr], after accepting a
4545+ client on [fd] and upgrading to a TLS connection. *)
4646+ val accept : Tls.Config.server -> Lwt_unix.file_descr -> (t * Lwt_unix.sockaddr) Lwt.t
4747+4848+ (** [connect client (host, port)] is [t], after successful
4949+ connection to [host] on [port] and TLS upgrade. *)
5050+ val connect : Tls.Config.client -> string * int -> t Lwt.t
5151+5252+ (** {2 Common stream operations} *)
5353+5454+ (** [read t ~off buffer] is [length], the number of bytes read into
5555+ [buffer]. It fills [buffer] starting at [off] (default is 0). *)
5656+ val read : t -> ?off:int -> bytes -> int Lwt.t
5757+5858+ (** [write t buffer] writes the [buffer] to the session. *)
5959+ val write : t -> string -> unit Lwt.t
6060+6161+ (** [writev t buffers] writes the [buffers] to the session. *)
6262+ val writev : t -> string list -> unit Lwt.t
6363+6464+ (** [read_bytes t bytes offset len] is [read_bytes], the amount of
6565+ bytes read. *)
6666+ val read_bytes : t -> Lwt_bytes.t -> int -> int -> int Lwt.t
6767+6868+ (** [write_bytes t bytes offset length] writes [length] bytes of
6969+ [bytes] starting at [offset] to the session. *)
7070+ val write_bytes : t -> Lwt_bytes.t -> int -> int -> unit Lwt.t
7171+7272+ (** [shutdown t direction] closes the [direction] of the TLS session [t].
7373+ If [`read_write] or [`write] is closed, a TLS close_notify is sent to the
7474+ other endpoint. If this results in a fully closed session (or an
7575+ errorneous session), the underlying file descriptor is closed. *)
7676+ val shutdown : t -> [ `read | `write | `read_write ] -> unit Lwt.t
7777+7878+ (** [close t] closes the TLS session and the underlying file descriptor. *)
7979+ val close : t -> unit Lwt.t
8080+8181+ (** [reneg ~authenticator ~acceptable_cas ~cert ~drop t] renegotiates the
8282+ session, and blocks until the renegotiation finished. Optionally, a new
8383+ [authenticator] and [acceptable_cas] can be used. The own certificate can
8484+ be adjusted by [cert]. If [drop] is [true] (the default),
8585+ application data received before the renegotiation finished is dropped. *)
8686+ val reneg : ?authenticator:X509.Authenticator.t ->
8787+ ?acceptable_cas:X509.Distinguished_name.t list -> ?cert:Tls.Config.own_cert ->
8888+ ?drop:bool -> t -> unit Lwt.t
8989+9090+ (** [key_update ~request t] updates the traffic key and requests a traffic key
9191+ update from the peer if [request] is provided and [true] (the default).
9292+ This is only supported in TLS 1.3. *)
9393+ val key_update : ?request:bool -> t -> unit Lwt.t
9494+9595+ (** [epoch t] returns [epoch], which contains information of the
9696+ active session. *)
9797+ val epoch : t -> (Tls.Core.epoch_data, unit) result
9898+end
9999+100100+(** {1 High-level API} *)
101101+102102+type ic = Lwt_io.input_channel
103103+type oc = Lwt_io.output_channel
104104+105105+(** [accept_ext server fd] is [(ic, oc), sockaddr], the input
106106+ and output channel from an accepted connection on the given [fd],
107107+ after upgrading to TLS using the [server] configuration. *)
108108+val accept_ext : Tls.Config.server -> Lwt_unix.file_descr ->
109109+ ((ic * oc) * Lwt_unix.sockaddr) Lwt.t
110110+111111+(** [accept own_cert fd] is [(ic, oc), sockaddr], the input and
112112+ output channel from the accepted connection on [fd], using the
113113+ default configuration with the given [own_cert]. *)
114114+val accept : Tls.Config.own_cert -> Lwt_unix.file_descr ->
115115+ ((ic * oc) * Lwt_unix.sockaddr, [> `Msg of string]) result Lwt.t
116116+117117+(** [connect_ext client (host, port)] is [ic, oc], the input
118118+ and output channel of a TLS connection to [host] on [port] using
119119+ the [client] configuration. *)
120120+val connect_ext : Tls.Config.client -> string * int -> (ic * oc) Lwt.t
121121+122122+(** [connect authenticator (host, port)] is [ic, oc], the input
123123+ and output channel of a TLS connection to [host] on [port] using the
124124+ default configuration and the [authenticator]. *)
125125+val connect : X509.Authenticator.t -> string * int -> (ic * oc, [> `Msg of string ]) result Lwt.t
126126+127127+(** [of_t t] is [ic, oc], the input and output channel. [close]
128128+ defaults to [!Unix.close]. *)
129129+val of_t : ?close:(unit -> unit Lwt.t) -> Unix.t -> ic * oc
+109
vendor/opam/tls/lwt/x509_lwt.ml
···11+open Lwt
22+33+let failure msg = fail @@ Failure msg
44+55+let catch_invalid_arg th h =
66+ Lwt.catch (fun () -> th)
77+ (function
88+ | Invalid_argument msg -> h msg
99+ | exn -> fail exn)
1010+1111+let (</>) a b = a ^ "/" ^ b
1212+1313+let o f g x = f (g x)
1414+1515+let read_file path =
1616+ let open Lwt_io in
1717+ open_file ~mode:Input path >>= fun file ->
1818+ read file >>= fun cs ->
1919+ close file >|= fun () ->
2020+ cs
2121+2222+let read_dir path =
2323+ let open Lwt_unix in
2424+ let rec collect acc d =
2525+ readdir_n d 10 >>= function
2626+ | [||] -> return acc
2727+ | xs -> collect (Array.to_list xs @ acc) d in
2828+ opendir path >>= fun dir ->
2929+ collect [] dir >>= fun entries ->
3030+ closedir dir >|= fun () ->
3131+ entries
3232+3333+let extension str =
3434+ let n = String.length str in
3535+ let rec scan = function
3636+ | i when i = 0 -> None
3737+ | i when str.[i - 1] = '.' ->
3838+ Some (String.sub str i (n - i))
3939+ | i -> scan (pred i) in
4040+ scan n
4141+4242+4343+let private_of_pems ~cert ~priv_key =
4444+ catch_invalid_arg
4545+ (read_file cert >|= fun pem ->
4646+ match X509.Certificate.decode_pem_multiple pem with
4747+ | Ok cs -> cs
4848+ | Error (`Msg m) -> invalid_arg ("failed to parse certificates " ^ m))
4949+ (o failure @@ Printf.sprintf "Private certificates (%s): %s" cert) >>= fun certs ->
5050+ catch_invalid_arg
5151+ (read_file priv_key >|= fun pem ->
5252+ match X509.Private_key.decode_pem pem with
5353+ | Ok key -> key
5454+ | Error (`Msg m) -> invalid_arg ("failed to parse private key " ^ m))
5555+ (o failure @@ Printf.sprintf "Private key (%s): %s" priv_key) >>= fun pk ->
5656+ return (certs, pk)
5757+5858+let certs_of_pem path =
5959+ catch_invalid_arg
6060+ (read_file path >|= fun pem ->
6161+ match X509.Certificate.decode_pem_multiple pem with
6262+ | Ok cs -> cs
6363+ | Error (`Msg m) -> invalid_arg ("failed to parse certificates " ^ m))
6464+ (o failure @@ Printf.sprintf "Certificates in %s: %s" path)
6565+6666+let certs_of_pem_dir path =
6767+ read_dir path
6868+ >|= List.filter (fun file -> extension file = Some "crt")
6969+ >>= Lwt_list.map_p (fun file -> certs_of_pem (path </> file))
7070+ >|= List.concat
7171+7272+let crl_of_pem path =
7373+ catch_invalid_arg
7474+ (read_file path >|= fun data ->
7575+ match X509.CRL.decode_der data with
7676+ | Ok cs -> cs
7777+ | Error (`Msg m) -> invalid_arg ("failed to parse CRL " ^ m))
7878+ (o failure @@ Printf.sprintf "CRL in %s: %s" path)
7979+8080+let crls_of_pem_dir = function
8181+ | None -> Lwt.return None
8282+ | Some path ->
8383+ read_dir path >>= fun files ->
8484+ Lwt_list.map_p (fun file -> crl_of_pem (path </> file)) files >|= fun crls ->
8585+ Some crls
8686+8787+let authenticator ?allowed_hashes ?crls param =
8888+ let time () = Some (Ptime_clock.now ()) in
8989+ let of_cas cas =
9090+ crls_of_pem_dir crls >|= fun crls ->
9191+ X509.Authenticator.chain_of_trust ?allowed_hashes ?crls ~time cas
9292+ and dotted_hex_to_cs hex =
9393+ Ohex.decode (String.map (function ':' -> ' ' | x -> x) hex)
9494+ and fingerp hash fingerprint =
9595+ X509.Authenticator.key_fingerprint ~time ~hash ~fingerprint
9696+ and cert_fingerp hash fingerprint =
9797+ X509.Authenticator.cert_fingerprint ~time ~hash ~fingerprint
9898+ in
9999+ match param with
100100+ | `Ca_file path -> certs_of_pem path >>= of_cas
101101+ | `Ca_dir path -> certs_of_pem_dir path >>= of_cas
102102+ | `Key_fingerprint (hash, fp) -> return (fingerp hash fp)
103103+ | `Hex_key_fingerprint (hash, fp) ->
104104+ let fp = dotted_hex_to_cs fp in
105105+ return (fingerp hash fp)
106106+ | `Cert_fingerprint (hash, fp) -> return (cert_fingerp hash fp)
107107+ | `Hex_cert_fingerprint (hash, fp) ->
108108+ let fp = dotted_hex_to_cs fp in
109109+ return (cert_fingerp hash fp)
+26
vendor/opam/tls/lwt/x509_lwt.mli
···11+(** X.509 certificate handling using Lwt. *)
22+33+(** [private_of_pems ~cert ~priv_key] is [priv], after reading the
44+ private key and certificate chain from the given PEM-encoded
55+ files. *)
66+val private_of_pems : cert:Lwt_io.file_name -> priv_key:Lwt_io.file_name -> Tls.Config.certchain Lwt.t
77+88+(** [certs_of_pem file] is [certificates], which are read from the
99+ PEM-encoded [file]. *)
1010+val certs_of_pem : Lwt_io.file_name -> X509.Certificate.t list Lwt.t
1111+1212+(** [certs_of_pem_dir dir] is [certificates], which are read from all
1313+ PEM-encoded files in [dir]. *)
1414+val certs_of_pem_dir : Lwt_io.file_name -> X509.Certificate.t list Lwt.t
1515+1616+(** [authenticator methods] constructs an [authenticator] using the
1717+ specified method and data. *)
1818+val authenticator : ?allowed_hashes:Digestif.hash' list -> ?crls:Lwt_io.file_name ->
1919+ [ `Ca_file of Lwt_io.file_name
2020+ | `Ca_dir of Lwt_io.file_name
2121+ | `Key_fingerprint of Digestif.hash' * string
2222+ | `Hex_key_fingerprint of Digestif.hash' * string
2323+ | `Cert_fingerprint of Digestif.hash' * string
2424+ | `Hex_cert_fingerprint of Digestif.hash' * string
2525+ ]
2626+ -> X509.Authenticator.t Lwt.t
···11+let rec random_path ?(tries = 10) fmt =
22+ if tries <= 0 then failwith "Impossible to generate an available random path";
33+ let res = Bytes.create 6 in
44+ for i = 0 to Bytes.length res - 1 do
55+ let chr =
66+ match Random.int (10 + 26 + 26) with
77+ | n when n < 10 -> Char.chr (Char.code '0' + n)
88+ | n when n < 10 + 26 -> Char.chr (Char.code 'a' + n - 10)
99+ | n -> Char.chr (Char.code 'A' + n - 10 - 26)
1010+ in
1111+ Bytes.set res i chr
1212+ done;
1313+ let path = Fmt.str fmt (Bytes.unsafe_to_string res) in
1414+ if Sys.file_exists path then random_path ~tries:(pred tries) fmt else path
1515+1616+let unlink_if_exists path =
1717+ try Unix.unlink path with Unix.Unix_error (Unix.ENOENT, _, _) -> ()
1818+1919+let bind_and_listen ?(backlog = 16) () =
2020+ let tmp = random_path "socket-%s.socket" in
2121+ unlink_if_exists tmp;
2222+ let socket = Unix.socket ~cloexec:true Unix.PF_UNIX Unix.SOCK_STREAM 0 in
2323+ let addr = Unix.ADDR_UNIX tmp in
2424+ Unix.bind socket addr;
2525+ Unix.listen socket backlog;
2626+ (Miou_unix.of_file_descr ~non_blocking:true socket, addr, tmp)
2727+2828+module Ca = struct
2929+ open Rresult
3030+3131+ let prefix =
3232+ X509.Distinguished_name.
3333+ [ Relative_distinguished_name.singleton (CN "Fuzzer") ]
3434+3535+ let cacert_dn =
3636+ X509.Distinguished_name.(
3737+ prefix
3838+ @ [ Relative_distinguished_name.singleton (CN "Ephemeral CA for fuzzer") ])
3939+4040+ let cacert_lifetime = Ptime.Span.v (365, 0L)
4141+ let _10s = Ptime.Span.of_int_s 10
4242+4343+ let make domain_name seed =
4444+ let valid_from = Option.get Ptime.(sub_span (v (Ptime_clock.now_d_ps ())) _10s) in
4545+ Domain_name.of_string domain_name >>= Domain_name.host
4646+ >>= fun domain_name ->
4747+ let private_key =
4848+ let seed = Base64.decode_exn ~pad:false seed in
4949+ let g = Mirage_crypto_rng.(create ~seed (module Fortuna)) in
5050+ Mirage_crypto_pk.Rsa.generate ~g ~bits:2048 ()
5151+ in
5252+ Ptime.add_span valid_from cacert_lifetime
5353+ |> Option.to_result ~none:(R.msgf "End time out of range")
5454+ >>= fun valid_until ->
5555+ X509.Signing_request.create cacert_dn (`RSA private_key) >>= fun ca_csr ->
5656+ let extensions =
5757+ let open X509.Extension in
5858+ let key_id =
5959+ X509.Public_key.id X509.Signing_request.((info ca_csr).public_key)
6060+ in
6161+ empty
6262+ |> add Subject_alt_name
6363+ ( true,
6464+ X509.General_name.(
6565+ singleton DNS [ Domain_name.to_string domain_name ]) )
6666+ |> add Basic_constraints (true, (false, None))
6767+ |> add Key_usage
6868+ (true, [ `Digital_signature; `Content_commitment; `Key_encipherment ])
6969+ |> add Subject_key_id (false, key_id)
7070+ in
7171+ X509.Signing_request.sign ~valid_from ~valid_until ~extensions
7272+ ca_csr (`RSA private_key) cacert_dn
7373+ |> R.reword_error (R.msgf "%a" X509.Validation.pp_signature_error)
7474+ >>= fun certificate ->
7575+ let fingerprint = X509.Certificate.fingerprint `SHA256 certificate in
7676+ let time () = Some (Ptime_clock.now ()) in
7777+ let authenticator =
7878+ X509.Authenticator.cert_fingerprint ~time ~hash:`SHA256
7979+ ~fingerprint
8080+ in
8181+ Ok (certificate, `RSA private_key, authenticator)
8282+end
8383+8484+let fuzz_coop = "fuzz.coop"
8585+let mutex = Miou.Mutex.create ()
8686+let epr fmt = Miou.Mutex.protect mutex @@ fun () -> Fmt.epr fmt
8787+8888+type operation =
8989+ | Send of string
9090+ | Recv of int
9191+ | Shutdown of [ `read | `write ]
9292+ | Close
9393+ | Noop
9494+9595+module Stop = struct
9696+ type t = {
9797+ mutex : Miou.Mutex.t;
9898+ condition : Miou.Condition.t;
9999+ mutable stop : bool;
100100+ }
101101+102102+ let create () =
103103+ let mutex = Miou.Mutex.create () in
104104+ let condition = Miou.Condition.create () in
105105+ { mutex; condition; stop = false }
106106+107107+ let stop t =
108108+ Miou.Mutex.protect t.mutex @@ fun () ->
109109+ t.stop <- true;
110110+ Miou.Condition.broadcast t.condition
111111+112112+ let wait t =
113113+ Miou.Mutex.protect t.mutex @@ fun () ->
114114+ while t.stop = false do
115115+ Miou.Condition.wait t.condition t.mutex
116116+ done
117117+end
118118+119119+let inhibit fn = try fn () with _exn -> ()
120120+121121+let run ~role:_ actions tls =
122122+ let rec go buf tls = function
123123+ | [] -> Buffer.contents buf
124124+ | Noop :: actions ->
125125+ Miou.yield ();
126126+ go buf tls actions
127127+ | Send str :: actions ->
128128+ Tls_miou_unix.write tls str;
129129+ go buf tls actions
130130+ | Close :: actions ->
131131+ Tls_miou_unix.close tls;
132132+ go buf tls actions
133133+ | Shutdown cmd :: actions ->
134134+ Tls_miou_unix.shutdown tls (cmd :> [ `read | `write | `read_write ]);
135135+ go buf tls actions
136136+ | Recv len :: actions ->
137137+ let tmp = Bytes.make len '\000' in
138138+ Tls_miou_unix.really_read tls tmp;
139139+ Buffer.add_subbytes buf tmp 0 len;
140140+ go buf tls actions
141141+ in
142142+ let buf = Buffer.create 0x100 in
143143+ try go buf tls actions with
144144+ | End_of_file | Tls_miou_unix.Closed_by_peer | Tls_miou_unix.Tls_alert _
145145+ | Tls_miou_unix.Tls_failure _ ->
146146+ inhibit (fun () -> Miou_unix.close (Tls_miou_unix.file_descr tls));
147147+ Buffer.contents buf
148148+ | exn ->
149149+ inhibit (fun () -> Miou_unix.close (Tls_miou_unix.file_descr tls));
150150+ raise exn
151151+152152+let run_client ~to_client:actions cfg addr =
153153+ let domain = Unix.domain_of_sockaddr addr in
154154+ let socket = Unix.socket ~cloexec:true domain Unix.SOCK_STREAM 0 in
155155+ Unix.connect socket addr;
156156+ let fd = Miou_unix.of_file_descr ~non_blocking:true socket in
157157+ let tls = Tls_miou_unix.client_of_fd cfg fd in
158158+ let finally () =
159159+ inhibit (fun () -> Unix.close socket)
160160+ in
161161+ Fun.protect ~finally @@ fun () -> run ~role:"client" actions tls
162162+163163+let rec cleanup orphans clients =
164164+ match Miou.care orphans with
165165+ | None | Some None -> clients
166166+ | Some (Some prm) ->
167167+ let clients = Miou.await prm :: clients in
168168+ cleanup orphans clients
169169+170170+let rec terminate orphans clients =
171171+ match Miou.care orphans with
172172+ | None -> List.rev clients
173173+ | Some None ->
174174+ Miou.yield ();
175175+ terminate orphans clients
176176+ | Some (Some prm) ->
177177+ let clients = Miou.await prm :: clients in
178178+ terminate orphans clients
179179+180180+exception Stop
181181+182182+let run_server ~to_server:actions ~stop fd cfg =
183183+ let rec go orphans clients =
184184+ let clients = cleanup orphans clients in
185185+ let accept = Miou.async @@ fun () -> Miou_unix.accept ~cloexec:true fd in
186186+ let stop =
187187+ Miou.async @@ fun () ->
188188+ Stop.wait stop;
189189+ raise Stop
190190+ in
191191+ match Miou.await_first [ accept; stop ] with
192192+ | Error _ ->
193193+ inhibit (fun () -> Miou_unix.close fd);
194194+ terminate orphans clients
195195+ | Ok (fd, _) ->
196196+ ignore
197197+ ( Miou.async ~orphans @@ fun () ->
198198+ match Tls_miou_unix.server_of_fd cfg fd with
199199+ | tls ->
200200+ let str = run ~role:"server" actions tls in
201201+ inhibit (fun () -> Miou_unix.close fd); str
202202+ | exception _ ->
203203+ Miou_unix.close fd;
204204+ String.empty );
205205+ go orphans clients
206206+ in
207207+ go (Miou.orphans ()) []
208208+209209+let compile to_client to_server =
210210+ let close_client close = function
211211+ | Close -> close lor 0b1100
212212+ | Shutdown `read -> close lor 0b1000
213213+ | Shutdown `write -> close lor 0b0100
214214+ | _ -> close
215215+ in
216216+ let close_server close = function
217217+ | Close -> close lor 0b0011
218218+ | Shutdown `read -> close lor 0b0010
219219+ | Shutdown `write -> close lor 0b0001
220220+ | _ -> close
221221+ in
222222+ let client = Buffer.create 0x100 in
223223+ let server = Buffer.create 0x100 in
224224+ let rec go close to_client to_server =
225225+ match (close, to_client, to_server) with
226226+ | _, [], _ | _, _, [] -> ()
227227+ | close, ((Shutdown _ | Close) as operation) :: to_client, _ ->
228228+ go (close_client close operation) to_client to_server
229229+ | close, _, ((Shutdown _ | Close) as operation) :: to_server ->
230230+ go (close_server close operation) to_client to_server
231231+ | close, Noop :: to_client, to_server | close, to_client, Noop :: to_server
232232+ ->
233233+ go close to_client to_server
234234+ | close, Send str :: to_client, Recv n :: to_server ->
235235+ assert (String.length str = n);
236236+ if close land 0b0100 = 0 && close land 0b0010 = 0 then
237237+ Buffer.add_string server str;
238238+ if close land 0b0100 = 0 && close land 0b0010 = 0 then
239239+ go close to_client to_server
240240+ | close, Recv n :: to_client, Send str :: to_server ->
241241+ assert (String.length str = n);
242242+ if close land 0b1000 = 0 && close land 0b0001 = 0 then
243243+ Buffer.add_string client str;
244244+ if close land 0b1000 = 0 && close land 0b0001 = 0 then
245245+ go close to_client to_server
246246+ | _, Send _ :: _, Send _ :: _ | _, Recv _ :: _, Recv _ :: _ ->
247247+ assert false (* GADT? *)
248248+ in
249249+ go 0x0 to_client to_server;
250250+ (Buffer.contents client, Buffer.contents server)
251251+252252+let pp_exn ppf exn = Fmt.string ppf (Printexc.to_string exn)
253253+let pp_str ppf str = Hxd_string.pp Hxd.default ppf str
254254+255255+let run seed operations =
256256+ Miou_unix.run ~domains:1 @@ fun () ->
257257+ let rng = Mirage_crypto_rng_miou_unix.(initialize (module Pfortuna)) in
258258+ let fd, addr, path = bind_and_listen () in
259259+ let finally () = Unix.unlink path in
260260+ Fun.protect ~finally @@ fun () ->
261261+ let cert, pk, authenticator =
262262+ Rresult.R.failwith_error_msg (Ca.make fuzz_coop seed)
263263+ in
264264+ let cfg_server =
265265+ Result.get_ok (Tls.Config.server ~certificates:(`Single ([ cert ], pk)) ())
266266+ in
267267+ let cfg_client = Result.get_ok (Tls.Config.client ~authenticator ()) in
268268+ let to_client, to_server = List.split operations in
269269+ let stop = Stop.create () in
270270+ let prm0 = Miou.async @@ fun () -> run_server ~to_server ~stop fd cfg_server in
271271+ let prm1 =
272272+ Miou.async @@ fun () ->
273273+ let finally () = Stop.stop stop in
274274+ Fun.protect ~finally @@ fun () -> run_client ~to_client cfg_client addr
275275+ in
276276+ let send_to_client, send_to_server = compile to_client to_server in
277277+ match (Miou.await prm0, Miou.await prm1) with
278278+ | Ok [ Ok send_to_server' ], Ok send_to_client' ->
279279+ Crowbar.check (String.equal send_to_client send_to_client');
280280+ Crowbar.check (String.equal send_to_server send_to_server');
281281+ let n = String.length send_to_client in
282282+ let m = String.length send_to_server in
283283+ Mirage_crypto_rng_miou_unix.kill rng;
284284+ epr "[%a] %db %db transmitted\n%!" Fmt.(styled `Green string) "OK" n m
285285+ | a, b ->
286286+ Mirage_crypto_rng_miou_unix.kill rng;
287287+ Crowbar.failf "[%a] Unexpected result: %a & %a\n%!"
288288+ Fmt.(styled `Red string) "ERROR"
289289+ Fmt.(Dump.result ~error:pp_exn ~ok:Fmt.(Dump.list (Dump.result ~error:pp_exn ~ok:pp_str))) a
290290+ Fmt.(Dump.result ~error:pp_exn ~ok:pp_str) b
291291+292292+let label name gen = Crowbar.with_printer Fmt.(const string name) gen
293293+294294+let direction =
295295+ let open Crowbar in
296296+ choose
297297+ [
298298+ label "server-to-client" (const `To_client);
299299+ label "client-to-server" (const `To_server);
300300+ ]
301301+302302+let shutdown =
303303+ let open Crowbar in
304304+ choose
305305+ [
306306+ label "close" (const Close);
307307+ label "shutdown-recv" (const (Shutdown `read));
308308+ label "shutdown-send" (const (Shutdown `write));
309309+ label "noop" (const Noop);
310310+ ]
311311+312312+let operation =
313313+ let open Crowbar in
314314+ map [ direction; bytes ] @@ fun direction str ->
315315+ match (direction, str) with
316316+ | _, "" -> (Noop, Noop)
317317+ | `To_server, str -> (Send str, Recv (String.length str))
318318+ | `To_client, str -> (Recv (String.length str), Send str)
319319+320320+let counter = Atomic.make 0
321321+322322+let operations =
323323+ let open Crowbar in
324324+ fix @@ fun m ->
325325+ let continue (to_client, to_server) =
326326+ if Atomic.fetch_and_add counter 1 >= 4 then const [ (Close, Close) ]
327327+ else map [ m ] @@ fun ops -> (to_client, to_server) :: ops
328328+ in
329329+ map
330330+ [ list1 operation; dynamic_bind (pair shutdown shutdown) continue ]
331331+ List.rev_append
332332+333333+let seed = Crowbar.(map [ bytes ] Base64.encode_exn)
334334+335335+let () =
336336+ Sys.set_signal Sys.sigpipe Sys.Signal_ignore;
337337+ Crowbar.add_test ~name:"run" Crowbar.[ seed; operations ] @@ fun seed operations ->
338338+ run seed operations;
339339+ Atomic.set counter 0
+332
vendor/opam/tls/miou/tls_miou_unix.ml
···11+(* NOTE: the unix/tls_unix.ml is mostly copied from here, so any change should be synchronized. *)
22+33+let src = Logs.Src.create "tls-miou"
44+55+module Log = (val Logs.src_log src : Logs.LOG)
66+77+external reraise : exn -> 'a = "%reraise"
88+99+let ( $ ) f x = f x
1010+1111+exception Tls_alert of Tls.Packet.alert_type
1212+exception Tls_failure of Tls.Engine.failure
1313+exception Closed_by_peer
1414+1515+let () =
1616+ Printexc.register_printer @@ function
1717+ | Closed_by_peer -> Some "Connection closed by peer"
1818+ | Tls_alert alert -> Some (Tls.Packet.alert_type_to_string alert)
1919+ | Tls_failure failure -> Some (Tls.Engine.string_of_failure failure)
2020+ | _ -> None
2121+2222+type state =
2323+ [ `Active of Tls.Engine.state
2424+ | `Read_closed of Tls.Engine.state
2525+ | `Write_closed of Tls.Engine.state
2626+ | `Closed
2727+ | `Error of exn ]
2828+2929+type t = {
3030+ role : [ `Server | `Client ];
3131+ fd : Miou_unix.file_descr;
3232+ mutable state : state;
3333+ mutable linger : string option;
3434+ read_buffer_size : int;
3535+ buf : bytes;
3636+ mutable rd_closed : bool;
3737+}
3838+3939+let file_descr { fd; _ } = fd
4040+4141+let half_close state mode =
4242+ match (state, mode) with
4343+ | `Active tls, `read -> `Read_closed tls
4444+ | `Active tls, `write -> `Write_closed tls
4545+ | `Active _, `read_write -> `Closed
4646+ | `Read_closed tls, `read -> `Read_closed tls
4747+ | `Read_closed _, (`write | `read_write) -> `Closed
4848+ | `Write_closed tls, `write -> `Write_closed tls
4949+ | `Write_closed _, (`read | `read_write) -> `Closed
5050+ | ((`Closed | `Error _) as e), (`read | `write | `read_write) -> e
5151+5252+let inject_state tls = function
5353+ | `Active _ -> `Active tls
5454+ | `Read_closed _ -> `Read_closed tls
5555+ | `Write_closed _ -> `Write_closed tls
5656+ | (`Closed | `Error _) as e -> e
5757+5858+let tls_alert a = Tls_alert a
5959+let tls_fail f = Tls_failure f
6060+let inhibit fn v = try fn v with _ -> ()
6161+6262+let write flow str =
6363+ Log.debug (fun m -> m "try to write %d byte(s)" (String.length str));
6464+ try Miou_unix.write flow.fd str with
6565+ | Unix.Unix_error ((Unix.EPIPE | Unix.ECONNRESET), _, _) ->
6666+ flow.state <- half_close flow.state `write;
6767+ raise Closed_by_peer
6868+ | Unix.Unix_error (_, _, _) as exn ->
6969+ flow.state <- `Error exn;
7070+ reraise exn
7171+7272+let handle flow tls str =
7373+ match Tls.Engine.handle_tls tls str with
7474+ | Ok (state, eof, `Response resp, `Data data) ->
7575+ Log.debug (fun m -> m "We handled %d byte(s)" (String.length str));
7676+ let state = inject_state state flow.state in
7777+ let state = Option.(value ~default:state (map (fun `Eof -> half_close state `read) eof)) in
7878+ flow.state <- state;
7979+ let to_close = flow.state = `Closed in
8080+ Option.iter (inhibit $ write flow) resp;
8181+ (* NOTE(dinosaure): [write flow] can set [flow.state]. So we must
8282+ check if the actual [flow.state] or the [flow.state] after [write flow]
8383+ want to close the underlying file-descriptor. *)
8484+ if to_close || flow.state = `Closed then Miou_unix.close flow.fd;
8585+ data
8686+ | Error (fail, `Response resp) ->
8787+ let exn = match fail with
8888+ | `Alert a -> tls_alert a | f -> tls_fail f in
8989+ flow.state <- `Error exn;
9090+ let _ = inhibit (write flow) resp in
9191+ raise exn
9292+9393+let read flow =
9494+ match Miou_unix.read flow.fd flow.buf ~off:0 ~len:(Bytes.length flow.buf) with
9595+ | 0 -> Ok String.empty
9696+ | len -> Ok (Bytes.sub_string flow.buf 0 len)
9797+ | exception Unix.Unix_error (Unix.ECONNRESET, _, _) -> Ok String.empty
9898+ | exception exn -> Error exn
9999+100100+let not_errored = function `Error _ -> false | _ -> true
101101+102102+let garbage flow = match flow.linger with
103103+ | Some "" | None -> false
104104+ | _ -> true
105105+106106+let read_react flow =
107107+ match flow.state with
108108+ | `Error exn -> raise exn
109109+ | `Read_closed _ | `Closed when garbage flow ->
110110+ (* XXX(dinosaure): [`Closed] can appear "at the same time" than some
111111+ application-data. In that case, we stored them into [t.linger]. Depending
112112+ on who closed the connection, [read_react] gives this /garbage/ in any
113113+ situation (even if the user closed the connection).
114114+115115+ An extra layer with [read] below check if [`Read_closed]/[`Close] comes
116116+ from the network (the peer closed the connection) or the user. In the
117117+ first case, we must give pending application-data. In the second case,
118118+ we must return [0] (or raise [End_of_file]). *)
119119+ let mbuf = flow.linger in
120120+ flow.linger <- None;
121121+ mbuf
122122+ | `Read_closed _ | `Closed ->
123123+ (* XXX(dinosaure): the goal of [read_react] is to read some encrypted bytes
124124+ and try to decrypt them with [handle]. If the linger is empty, this means
125125+ that we're trying to get more data (to decrypt) when we can't get any
126126+ more. From this point of view, it's an error that needs to be notified.
127127+ However, this error can be interpreted in 2 ways:
128128+ - we want to have more data decrypted. In this case, this error is
129129+ expected and may result in the user being told that there is nothing
130130+ left to read (for example, returning 0).
131131+ - we attempt a handshake. In this case, we are dealing with an unexpected
132132+ error. *)
133133+ raise End_of_file
134134+ | `Active _ | `Write_closed _ ->
135135+ Log.debug (fun m -> m "read something from the TLS session");
136136+ match read flow with
137137+ | Error exn ->
138138+ if not_errored flow.state then flow.state <- `Error exn;
139139+ raise exn
140140+ | Ok "" ->
141141+ (* XXX(dinosaure): see [`Read_closed _ | `Closed] case. *)
142142+ raise End_of_file
143143+ | Ok str ->
144144+ Log.debug (fun m -> m "got %d byte(s)" (String.length str));
145145+ match flow.state with
146146+ | `Active tls | `Read_closed tls | `Write_closed tls -> handle flow tls str
147147+ | `Closed -> raise End_of_file
148148+ | `Error exn -> raise exn
149149+[@@ocamlformat "disable"]
150150+151151+let rec read_in flow ?(off= 0) ?len buf =
152152+ let len = Option.value ~default:(Bytes.length buf - off) len in
153153+ let write_in res =
154154+ let rlen = String.length res in
155155+ let mlen = min len rlen in
156156+ Bytes.blit_string res 0 buf off mlen;
157157+ let linger = if mlen < rlen
158158+ then Some (String.sub res mlen (rlen - mlen))
159159+ else None in
160160+ flow.linger <- linger; mlen
161161+ in
162162+ match flow.linger with
163163+ | Some res -> write_in res
164164+ | None -> (
165165+ match read_react flow with
166166+ | None -> read_in ~off ~len flow buf
167167+ | Some res -> write_in res)
168168+169169+let writev flow bufs =
170170+ match flow.state with
171171+ | `Closed | `Write_closed _ -> raise Closed_by_peer
172172+ | `Error exn -> reraise exn
173173+ | `Active tls | `Read_closed tls -> (
174174+ match Tls.Engine.send_application_data tls bufs with
175175+ | Some (tls, answer) ->
176176+ flow.state <- inject_state tls flow.state;
177177+ write flow answer
178178+ | None -> assert false)
179179+180180+let rec drain_handshake flow =
181181+ let push_linger flow mcs =
182182+ match (mcs, flow.linger) with
183183+ | None, _ -> ()
184184+ | scs, None -> flow.linger <- scs
185185+ | Some cs, Some l -> flow.linger <- Some (l ^ cs)
186186+ in
187187+ match flow.state with
188188+ | `Active tls when not (Tls.Engine.handshake_in_progress tls) -> flow
189189+ | (`Read_closed _ | `Closed) when garbage flow -> flow
190190+ | _ ->
191191+ Log.debug (fun m -> m "start to read something from the TLS session");
192192+ let mcs = read_react flow in
193193+ push_linger flow mcs;
194194+ drain_handshake flow
195195+196196+let close flow =
197197+ match flow.state with
198198+ | `Active tls | `Read_closed tls ->
199199+ let tls, str = Tls.Engine.send_close_notify tls in
200200+ flow.rd_closed <- true;
201201+ flow.state <- inject_state tls flow.state;
202202+ flow.state <- `Closed;
203203+ inhibit (write flow) str;
204204+ Miou_unix.close flow.fd
205205+ | `Write_closed _ ->
206206+ flow.rd_closed <- true;
207207+ flow.state <- `Closed;
208208+ Miou_unix.close flow.fd
209209+ | `Closed -> flow.rd_closed <- true
210210+ | `Error _ ->
211211+ flow.rd_closed <- true;
212212+ Miou_unix.close flow.fd
213213+214214+let closed_by_user flow = function
215215+ | `read | `read_write -> flow.rd_closed <- true
216216+ | `write -> ()
217217+218218+let shutdown flow mode =
219219+ closed_by_user flow mode;
220220+ match (flow.state, mode) with
221221+ | `Active tls, `read ->
222222+ Log.debug (fun m -> m "shutdown `read");
223223+ flow.state <- inject_state tls (half_close flow.state mode)
224224+ | (`Active tls | `Read_closed tls), (`write | `read_write) ->
225225+ let tls, str = Tls.Engine.send_close_notify tls in
226226+ flow.state <- inject_state tls (half_close flow.state mode);
227227+ (* NOTE(dinosaure): [write flow] can set [flow.state]. So we must
228228+ check if the actual [flow.state] or the [flow.state] after [write flow]
229229+ want to close the underlying file-descriptor. *)
230230+ let to_close = flow.state = `Closed in
231231+ inhibit (write flow) str;
232232+ if to_close || flow.state = `Closed then Miou_unix.close flow.fd
233233+ | `Write_closed tls, (`read | `read_write) ->
234234+ flow.state <- inject_state tls (half_close flow.state mode);
235235+ if flow.state = `Closed then Miou_unix.close flow.fd
236236+ | `Error _, _ -> Miou_unix.close flow.fd
237237+ | `Read_closed _, `read -> ()
238238+ | `Write_closed _, `write -> ()
239239+ | `Closed, _ -> ()
240240+241241+let client_of_fd conf ?(read_buffer_size = 0x1000) ?host fd =
242242+ let conf' =
243243+ match host with None -> conf | Some host -> Tls.Config.peer conf host
244244+ in
245245+ let tls, init = Tls.Engine.client conf' in
246246+ let tls_flow =
247247+ {
248248+ role = `Client;
249249+ fd;
250250+ state = `Active tls;
251251+ linger = None;
252252+ read_buffer_size;
253253+ buf = Bytes.make read_buffer_size '\000';
254254+ rd_closed = false;
255255+ }
256256+ in
257257+ write tls_flow init;
258258+ drain_handshake tls_flow
259259+260260+let server_of_fd conf ?(read_buffer_size = 0x1000) fd =
261261+ let tls = Tls.Engine.server conf in
262262+ let tls_flow =
263263+ {
264264+ role = `Server;
265265+ fd;
266266+ state = `Active tls;
267267+ linger = None;
268268+ read_buffer_size;
269269+ buf = Bytes.make read_buffer_size '\000';
270270+ rd_closed = false;
271271+ }
272272+ in
273273+ drain_handshake tls_flow
274274+275275+let write flow ?(off = 0) ?len str =
276276+ let len = Option.value ~default:(String.length str - off) len in
277277+ if off < 0 || len < 0 || off > String.length str - len
278278+ then invalid_arg "Tls_miou.write";
279279+ if len > 0 then writev flow [ String.sub str off len ]
280280+281281+let read t ?(off= 0) ?len buf =
282282+ let len = Option.value ~default:(Bytes.length buf - off) len in
283283+ if off < 0 || len < 0 || off > Bytes.length buf - len
284284+ then invalid_arg "Tls_miou.read";
285285+ if t.rd_closed then 0
286286+ else try read_in t ~off ~len buf with End_of_file -> 0
287287+288288+let rec really_read_go t off len buf =
289289+ let len' = read t buf ~off ~len in
290290+ if len' == 0 then raise End_of_file
291291+ else if len - len' > 0
292292+ then really_read_go t (off + len') (len - len') buf
293293+294294+let really_read t ?(off= 0) ?len buf =
295295+ let len = Option.value ~default:(Bytes.length buf - off) len in
296296+ if off < 0 || len < 0 || off > Bytes.length buf - len
297297+ then invalid_arg "Tls_miou.really_read";
298298+ if len > 0 then really_read_go t off len buf
299299+300300+let resolve host service =
301301+ let tcp = Unix.getprotobyname "tcp" in
302302+ match Unix.getaddrinfo host service [ AI_PROTOCOL tcp.p_proto ] with
303303+ | [] -> Fmt.invalid_arg "No address for %s:%s" host service
304304+ | ai :: _ -> ai.ai_addr
305305+306306+let connect authenticator (v, port) =
307307+ let conf =
308308+ match Tls.Config.client ~authenticator () with
309309+ | Ok config -> config
310310+ | Error `Msg msg -> Fmt.invalid_arg "Configuration failure: %s" msg
311311+ in
312312+ let addr = resolve v (string_of_int port) in
313313+ let fd =
314314+ match addr with
315315+ | Unix.ADDR_UNIX _ -> invalid_arg "Tls_miou.connect: Invalid UNIX socket"
316316+ | Unix.ADDR_INET (inet_addr, _) ->
317317+ if Unix.is_inet6_addr inet_addr then Miou_unix.tcpv6 ()
318318+ else Miou_unix.tcpv4 ()
319319+ in
320320+ let host = Result.to_option Domain_name.(Result.bind (of_string v) host) in
321321+ match Miou_unix.connect fd addr with
322322+ | () -> client_of_fd conf ?host fd
323323+ | exception exn ->
324324+ Miou_unix.close fd;
325325+ raise exn
326326+327327+let epoch flow = match flow.state with
328328+ | `Active tls | `Read_closed tls | `Write_closed tls ->
329329+ ( match Tls.Engine.epoch tls with
330330+ | Error () -> assert false
331331+ | Ok data -> Some data )
332332+ | _ -> None
+96
vendor/opam/tls/miou/tls_miou_unix.mli
···11+(** Effectful operations using Miou for pure TLS.
22+33+ The pure TLS is state and buffer in, state and buffer out. This module uses
44+ Miou (and its Unix layer) for communication over the network. *)
55+66+exception Tls_alert of Tls.Packet.alert_type
77+exception Tls_failure of Tls.Engine.failure
88+exception Closed_by_peer
99+1010+type t
1111+(** Abstract type of a session. *)
1212+1313+val file_descr : t -> Miou_unix.file_descr
1414+(** [file_descr] returns the underlying file-descriptor used by the given
1515+ TLS {i socket}. *)
1616+1717+val read : t -> ?off:int -> ?len:int -> bytes -> int
1818+(** [read fd buf ~off ~len] reads up to [len] bytes (defaults to
1919+ [Bytes.length buf - off] from the given TLS {i socket} [fd], storing them in
2020+ byte sequence [buf], starting at position [off] in [buf] (defaults to [0]).
2121+ It returns the actual number of characters read, between 0 and [len]
2222+ (inclusive).
2323+2424+ @raise Unix_error raised by the system call {!val:Unix.read}. The function
2525+ handles {!val:Unix.EINTR}, {!val:Unix.EAGAIN} and {!val:Unix.EWOULDBLOCK}
2626+ exceptions and redo the system call.
2727+2828+ @raise Invalid_argument if [off] and [len] do not designate a valid range of
2929+ [buf]. *)
3030+3131+val really_read : t -> ?off:int -> ?len:int -> bytes -> unit
3232+(** [really_read fd buf ~off ~len] reads [len] bytes (defaults to
3333+ [Bytes.length buf - off]) from the given TLS {i socket} [fd], storing them
3434+ in byte sequence [buf], starting at position [off] in [buf] (defaults to
3535+ [0]). If [len = 0], [really_read] does nothing.
3636+3737+ @raise Unix_error raised by the system call {!val:Unix.read}. The function
3838+ handles {!val:Unix.EINTR}, {!val:Unix.EAGAIN} and {!val:Unix.EWOULDBLOCK}
3939+ exceptions and redo the system call.
4040+4141+ @raise End_of_file if {!val:Unix.read} returns [0] before [len] characters
4242+ have been read.
4343+4444+ @raise Invalid_argument if [off] and [len] do not designate a valid range of
4545+ [buf]. *)
4646+4747+val write : t -> ?off:int -> ?len:int -> string -> unit
4848+(** [write t str ~off ~len] writes [len] bytes (defaults to
4949+ [String.length str - off]) from byte sequence [str], starting at offset
5050+ [off] (defaults to [0]), to the given TLS {i socket} [fd].
5151+5252+ @raise Unix_error raised by the syscall call {!val:Unix.write}. The function
5353+ handles {!val:Unix.EINTR}, {!val:Unix.EAGAIN} and {!val:Unix.EWOULDBLOCK}
5454+ exceptions and redo the system call.
5555+5656+ @raise Closed_by_peer if [t] is connected to a peer whose reading end is
5757+ closed. Similar to the {!val:EPIPE} error for pipe/socket connected.
5858+5959+ @raise Invalid_argument if [off] and [len] do not designate a valid range of
6060+ [buf]. *)
6161+6262+val close : t -> unit
6363+(** [close flow] closes the TLS session and the underlying file-descriptor. *)
6464+6565+val shutdown : t -> [ `read | `write | `read_write ] -> unit
6666+(** [shutdown t direction] closes the direction of the TLS session [t]. If
6767+ [`read_write] or [`write] is closed, a TLS close-notify is sent to the other
6868+ endpoint. If this results in a fully-closed session (or an errorneous
6969+ session), the underlying file descriptor is closed. *)
7070+7171+val client_of_fd :
7272+ Tls.Config.client ->
7373+ ?read_buffer_size:int ->
7474+ ?host:[ `host ] Domain_name.t ->
7575+ Miou_unix.file_descr ->
7676+ t
7777+(** [client_of_flow client ~host fd] is [t], after client-side TLS handshake of
7878+ [fd] using [client] configuration and [host].
7979+8080+ @raise End_of_file if we are not able to complete the handshake. *)
8181+8282+val server_of_fd :
8383+ Tls.Config.server -> ?read_buffer_size:int -> Miou_unix.file_descr -> t
8484+(** [server_of_fd server fd] is [t], after server-side TLS handshake of [fd]
8585+ using [server] configuration.
8686+8787+ @raise End_of_file if we are not able to complete the handshake. *)
8888+8989+val connect : X509.Authenticator.t -> string * int -> t
9090+(** [connect authenticator (host, port)] is [t], a connected TLS connection
9191+ to [host] on [port] using the default configuration and the
9292+ [authenticator]. *)
9393+9494+val epoch : t -> Tls.Core.epoch_data option
9595+(** [epoch t] returns [epoch], which contains information of the active
9696+ session. *)
···11+open Lwt.Infix
22+33+module Main (S : Mirage_stack.V4)
44+ (KV : Mirage_kv.RO)
55+ (CL : Mirage_clock.PCLOCK) =
66+struct
77+88+ module TLS = Tls_mirage.Make (S.TCPV4)
99+ module X509 = Tls_mirage.X509 (KV) (CL)
1010+ module Http = Cohttp_mirage.Server (TLS)
1111+1212+ module Body = Cohttp_lwt.Body
1313+1414+ let callback _conn req body =
1515+ let resp = Cohttp.Response.make ~status:`OK () in
1616+ (match Cohttp.Request.meth req with
1717+ | `POST ->
1818+ Body.to_string body >|= fun contents ->
1919+ "<pre>" ^ contents ^ "</pre>"
2020+ | _ -> Lwt.return "") >|= fun inlet ->
2121+ let body = Body.of_string @@
2222+ "<html><head><title>ohai</title></head> \
2323+ <body><h3>Secure CoHTTP on-line.</h3>"
2424+ ^ inlet ^ "</body></html>\r\n"
2525+ in
2626+ (resp, body)
2727+2828+ let upgrade conf tcp =
2929+ TLS.server_of_flow conf tcp >>= function
3030+ | Error _ -> Lwt.fail (Failure "tls init")
3131+ | Ok tls ->
3232+ let t = Http.make ~callback () in
3333+ Http.listen t tls
3434+3535+ let start stack kv _ _ =
3636+ X509.certificate kv `Default >>= fun cert ->
3737+ let conf = Tls.Config.server ~certificates:(`Single cert) () in
3838+ S.listen_tcpv4 stack ~port:4433 (upgrade conf) ;
3939+ S.listen stack
4040+4141+end
+299
vendor/opam/tls/mirage/tls_mirage.ml
···11+open Lwt.Infix
22+33+module Make (F : Mirage_flow.S) = struct
44+55+ type error = [ `Tls_alert of Tls.Packet.alert_type
66+ | `Tls_failure of Tls.Engine.failure
77+ | `Read of F.error
88+ | `Write of F.write_error ]
99+1010+ type write_error = [ Mirage_flow.write_error | error ]
1111+1212+ let pp_error ppf = function
1313+ | `Tls_failure f -> Tls.Engine.pp_failure ppf f
1414+ | `Tls_alert a -> Fmt.string ppf @@ Tls.Packet.alert_type_to_string a
1515+ | `Read e -> F.pp_error ppf e
1616+ | `Write e -> F.pp_write_error ppf e
1717+1818+ let pp_write_error ppf = function
1919+ | #Mirage_flow.write_error as e -> Mirage_flow.pp_write_error ppf e
2020+ | #error as e -> pp_error ppf e
2121+2222+ type flow = {
2323+ role : [ `Server | `Client ] ;
2424+ flow : F.flow ;
2525+ mutable state : [ `Active of Tls.Engine.state
2626+ | `Read_closed of Tls.Engine.state
2727+ | `Write_closed of Tls.Engine.state
2828+ | `Closed
2929+ | `Error of error ] ;
3030+ mutable linger : string list ;
3131+ }
3232+3333+ let half_close state mode =
3434+ match state, mode with
3535+ | `Active tls, `read -> `Read_closed tls
3636+ | `Active tls, `write -> `Write_closed tls
3737+ | `Active _, `read_write -> `Closed
3838+ | `Read_closed tls, `read -> `Read_closed tls
3939+ | `Read_closed _, (`write | `read_write) -> `Closed
4040+ | `Write_closed tls, `write -> `Write_closed tls
4141+ | `Write_closed _, (`read | `read_write) -> `Closed
4242+ | (`Closed | `Error _) as e, (`read | `write | `read_write) -> e
4343+4444+ let inject_state tls = function
4545+ | `Active _ -> `Active tls
4646+ | `Read_closed _ -> `Read_closed tls
4747+ | `Write_closed _ -> `Write_closed tls
4848+ | (`Closed | `Error _) as e -> e
4949+5050+ let tls_alert a = `Error (`Tls_alert a)
5151+ let tls_fail f = `Error (`Tls_failure f)
5252+5353+ let write_flow flow buf =
5454+ F.write flow.flow (Cstruct.of_string buf) >>= function
5555+ | Ok _ as o -> Lwt.return o
5656+ | Error `Closed ->
5757+ flow.state <- half_close flow.state `write;
5858+ Lwt.return (Error (`Write `Closed))
5959+ | Error e ->
6060+ flow.state <- `Error (`Write e);
6161+ Lwt.return (Error (`Write e))
6262+6363+ let read_react flow =
6464+ let handle tls buf =
6565+ match Tls.Engine.handle_tls tls buf with
6666+ | Ok (state, eof, `Response resp, `Data data) ->
6767+ let state = inject_state state flow.state in
6868+ let state = Option.(value ~default:state (map (fun `Eof -> half_close state `read) eof)) in
6969+ flow.state <- state;
7070+ ( match resp with
7171+ | None -> Lwt.return @@ Ok ()
7272+ | Some buf -> write_flow flow buf) >>= fun _ ->
7373+ Lwt.return @@ `Ok (Option.map Cstruct.of_string data)
7474+ | Error (fail, `Response resp) ->
7575+ let reason = match fail with
7676+ | `Alert a -> tls_alert a
7777+ | f -> tls_fail f
7878+ in
7979+ flow.state <- reason ;
8080+ F.write flow.flow (Cstruct.of_string resp) >>= fun _ ->
8181+ Lwt.return reason
8282+ in
8383+ match flow.state with
8484+ | `Error _ as e -> Lwt.return e
8585+ | `Read_closed _ | `Closed -> Lwt.return `Eof
8686+ | `Active _ | `Write_closed _ ->
8787+ F.read flow.flow >>= function
8888+ | Error e ->
8989+ flow.state <- `Error (`Read e);
9090+ Lwt.return (`Error (`Read e))
9191+ | Ok `Eof ->
9292+ flow.state <- half_close flow.state `read;
9393+ Lwt.return `Eof
9494+ | Ok `Data buf -> match flow.state with
9595+ | `Active tls | `Write_closed tls -> handle tls (Cstruct.to_string buf)
9696+ | `Read_closed _ | `Closed -> Lwt.return `Eof
9797+ | `Error _ as e -> Lwt.return e
9898+9999+ let rec read flow =
100100+ match flow.linger with
101101+ | [] ->
102102+ ( read_react flow >>= function
103103+ | `Ok None -> read flow
104104+ | `Ok (Some buf) -> Lwt.return @@ Ok (`Data buf)
105105+ | `Eof -> Lwt.return @@ Ok `Eof
106106+ | `Error e -> Lwt.return @@ Error e )
107107+ | bufs ->
108108+ flow.linger <- [] ;
109109+ let str = String.concat "" (List.rev bufs) in
110110+ Lwt.return @@ Ok (`Data (Cstruct.of_string str))
111111+112112+ let writev flow bufs =
113113+ match flow.state with
114114+ | `Closed | `Write_closed _ -> Lwt.return @@ Error `Closed
115115+ | `Error e -> Lwt.return @@ Error (e :> write_error)
116116+ | `Active tls | `Read_closed tls ->
117117+ let bufs = List.map Cstruct.to_string bufs in
118118+ match Tls.Engine.send_application_data tls bufs with
119119+ | Some (tls, answer) ->
120120+ flow.state <- `Active tls ;
121121+ write_flow flow answer
122122+ | None ->
123123+ (* "Impossible" due to handshake draining. *)
124124+ assert false
125125+126126+ let write flow buf = writev flow [buf]
127127+128128+ (*
129129+ * XXX bad XXX
130130+ * This is a point that should particularly be protected from concurrent r/w.
131131+ * Doing this before a `t` is returned is safe; redoing it during rekeying is
132132+ * not, as the API client already sees the `t` and can mistakenly interleave
133133+ * writes while this is in progress.
134134+ * *)
135135+ let rec drain_handshake flow =
136136+ match flow.state with
137137+ | `Active tls when not (Tls.Engine.handshake_in_progress tls) ->
138138+ Lwt.return @@ Ok flow
139139+ | _ ->
140140+ (* read_react re-throws *)
141141+ read_react flow >>= function
142142+ | `Ok mbuf ->
143143+ flow.linger <- Option.(to_list (map Cstruct.to_string mbuf)) @ flow.linger ;
144144+ drain_handshake flow
145145+ | `Error e -> Lwt.return @@ Error (e :> write_error)
146146+ | `Eof -> Lwt.return @@ Error `Closed
147147+148148+ type wr_or_msg = [ write_error | `Msg of string ]
149149+150150+ let underlying flow = flow.flow
151151+152152+ let reneg ?authenticator ?acceptable_cas ?cert ?(drop = true) flow =
153153+ match flow.state with
154154+ | `Closed | `Write_closed _ | `Read_closed _ -> Lwt.return @@ Error `Closed
155155+ | `Error e -> Lwt.return @@ Error (e :> wr_or_msg)
156156+ | `Active tls ->
157157+ match Tls.Engine.reneg ?authenticator ?acceptable_cas ?cert tls with
158158+ | None -> Lwt.return (Error (`Msg "Renegotiation already in progress"))
159159+ | Some (tls', buf) ->
160160+ if drop then flow.linger <- [] ;
161161+ flow.state <- `Active tls' ;
162162+ write_flow flow buf >>= fun _ ->
163163+ drain_handshake flow >|= function
164164+ | Ok _ -> Ok ()
165165+ | Error e -> Error (e :> wr_or_msg)
166166+167167+ let key_update ?request flow =
168168+ match flow.state with
169169+ | `Closed | `Write_closed _ -> Lwt.return @@ Error `Closed
170170+ | `Error e -> Lwt.return @@ Error (e :> wr_or_msg)
171171+ | `Active tls | `Read_closed tls ->
172172+ match Tls.Engine.key_update ?request tls with
173173+ | Error _ -> Lwt.return (Error (`Msg "Key update failed"))
174174+ | Ok (tls', buf) ->
175175+ flow.state <- `Active tls' ;
176176+ write_flow flow buf >|= function
177177+ | Ok _ as o -> o
178178+ | Error e -> Error (e :> wr_or_msg)
179179+180180+ let close flow =
181181+ (match flow.state with
182182+ | `Active tls | `Read_closed tls ->
183183+ let tls, buf = Tls.Engine.send_close_notify tls in
184184+ flow.state <- inject_state tls flow.state;
185185+ flow.state <- `Closed;
186186+ write_flow flow buf >|= fun _ ->
187187+ ()
188188+ | `Write_closed _ ->
189189+ flow.state <- `Closed;
190190+ Lwt.return_unit
191191+ | _ -> Lwt.return_unit) >>= fun () ->
192192+ F.close flow.flow
193193+194194+ let shutdown flow mode =
195195+ match flow.state with
196196+ | `Active tls | `Read_closed tls | `Write_closed tls ->
197197+ let tls, buf =
198198+ match flow.state, mode with
199199+ | (`Active tls | `Read_closed tls), (`write | `read_write) ->
200200+ let tls, buf = Tls.Engine.send_close_notify tls in
201201+ tls, Some buf
202202+ | _, _ -> tls, None
203203+ in
204204+ flow.state <- inject_state tls (half_close flow.state mode);
205205+ (* as outlined above, this may fail since the TCP flow may already be (half-)closed *)
206206+ Option.fold
207207+ ~none:Lwt.return_unit
208208+ ~some:(fun b -> write_flow flow b >|= fun _ -> ())
209209+ buf >>= fun () ->
210210+ (match flow.state with
211211+ | `Closed -> F.close flow.flow
212212+ | _ -> Lwt.return_unit)
213213+ | `Error _ | `Closed ->
214214+ F.close flow.flow
215215+216216+ let client_of_flow conf ?host flow =
217217+ let conf' = match host with
218218+ | None -> conf
219219+ | Some host -> Tls.Config.peer conf host
220220+ in
221221+ let (tls, init) = Tls.Engine.client conf' in
222222+ let tls_flow = {
223223+ role = `Client ;
224224+ flow = flow ;
225225+ state = `Active tls ;
226226+ linger = [] ;
227227+ } in
228228+ write_flow tls_flow init >>= fun _ ->
229229+ drain_handshake tls_flow
230230+231231+ let server_of_flow conf flow =
232232+ let tls_flow = {
233233+ role = `Server ;
234234+ flow = flow ;
235235+ state = `Active (Tls.Engine.server conf) ;
236236+ linger = [] ;
237237+ } in
238238+ drain_handshake tls_flow
239239+240240+ let epoch flow =
241241+ match flow.state with
242242+ | `Closed | `Error _ -> Error ()
243243+ | `Active tls | `Read_closed tls | `Write_closed tls -> Tls.Engine.epoch tls
244244+245245+(* let create_connection t tls_params host (addr, port) =
246246+ |+ XXX addr -> (host : string) +|
247247+ TCP.create_connection t (addr, port) >>= function
248248+ | `Error _ as e -> return e
249249+ | `Ok flow -> client_of_tcp_flow tls_params host flow *)
250250+251251+(* let listen_ssl t cert ~port callback =
252252+ let cb flow =
253253+ server_of_tcp_flow cert flow >>= callback in
254254+ TCP.input t ~listeners:(fun p -> if p = port then Some cb else None) *)
255255+256256+end
257257+258258+module X509 (KV : Mirage_kv.RO) = struct
259259+260260+ let ca_roots_file = Mirage_kv.Key.v "ca-roots.crt"
261261+ let default_cert = "server"
262262+263263+ let err_fail pp = function
264264+ | Ok x -> Lwt.return x
265265+ | Error e -> Fmt.kstr Lwt.fail_with "%a" pp e
266266+267267+ let pp_msg ppf = function `Msg m -> Fmt.string ppf m
268268+269269+ let decode_or_fail f cs = err_fail pp_msg (f cs)
270270+271271+ let read kv name =
272272+ KV.get kv name >>= err_fail KV.pp_error >|= Cstruct.of_string
273273+274274+ let read_crl kv = function
275275+ | None -> Lwt.return None
276276+ | Some filename ->
277277+ read kv (Mirage_kv.Key.v filename) >>= fun data ->
278278+ err_fail pp_msg (X509.CRL.decode_der (Cstruct.to_string data)) >|= fun crl ->
279279+ Some [ crl ]
280280+281281+ let authenticator ?allowed_hashes ?crl kv =
282282+ let time () = Some (Mirage_ptime.now ()) in
283283+ let now = Mirage_ptime.now () in
284284+ read kv ca_roots_file >|= Cstruct.to_string >>=
285285+ decode_or_fail X509.Certificate.decode_pem_multiple >>= fun cas ->
286286+ let ta = X509.Validation.valid_cas ~time:now cas in
287287+ read_crl kv crl >|= fun crls ->
288288+ X509.Authenticator.chain_of_trust ?crls ?allowed_hashes ~time ta
289289+290290+ let certificate kv =
291291+ let read name =
292292+ read kv (Mirage_kv.Key.v (name ^ ".pem")) >|= Cstruct.to_string >>=
293293+ decode_or_fail X509.Certificate.decode_pem_multiple >>= fun certs ->
294294+ read kv (Mirage_kv.Key.v (name ^ ".key")) >|= Cstruct.to_string >>=
295295+ decode_or_fail X509.Private_key.decode_pem >|= fun pk ->
296296+ (certs, pk)
297297+ in function | `Default -> read default_cert
298298+ | `Name name -> read name
299299+end
+70
vendor/opam/tls/mirage/tls_mirage.mli
···11+(** Effectful operations using Mirage for pure TLS. *)
22+33+(** TLS module given a flow *)
44+module Make (F : Mirage_flow.S) : sig
55+66+ (** possible errors: incoming alert, processing failure, or a
77+ problem in the underlying flow. *)
88+ type error = [ `Tls_alert of Tls.Packet.alert_type
99+ | `Tls_failure of Tls.Engine.failure
1010+ | `Read of F.error
1111+ | `Write of F.write_error ]
1212+1313+ type write_error = [ `Closed | error ]
1414+ (** The type for write errors. *)
1515+1616+ (** we provide the FLOW interface *)
1717+ include Mirage_flow.S
1818+ with type error := error
1919+ and type write_error := write_error
2020+2121+ (** [underlying t] returns the underlying flow. This is useful to extract
2222+ information such as [src] and [dst] of that flow. *)
2323+ val underlying : flow -> F.flow
2424+2525+ (** [reneg ~authenticator ~acceptable_cas ~cert ~drop t] renegotiates the
2626+ session, and blocks until the renegotiation finished. Optionally, a new
2727+ [authenticator] and [acceptable_cas] can be used. The own certificate can
2828+ be adjusted by [cert]. If [drop] is [true] (the default),
2929+ application data received before the renegotiation finished is dropped. *)
3030+ val reneg : ?authenticator:X509.Authenticator.t ->
3131+ ?acceptable_cas:X509.Distinguished_name.t list -> ?cert:Tls.Config.own_cert ->
3232+ ?drop:bool -> flow -> (unit, [ write_error | `Msg of string ]) result Lwt.t
3333+3434+ (** [key_update ~request t] updates the traffic key and requests a traffic key
3535+ update from the peer if [request] is provided and [true] (the default).
3636+ This is only supported in TLS 1.3. *)
3737+ val key_update : ?request:bool -> flow -> (unit, [ write_error | `Msg of string ]) result Lwt.t
3838+3939+ (** [client_of_flow client ~host flow] upgrades the existing connection
4040+ to TLS using the [client] configuration, using [host] as peer name. *)
4141+ val client_of_flow : Tls.Config.client -> ?host:[ `host ] Domain_name.t ->
4242+ F.flow -> (flow, write_error) result Lwt.t
4343+4444+ (** [server_of_flow server flow] upgrades the flow to a TLS
4545+ connection using the [server] configuration. *)
4646+ val server_of_flow : Tls.Config.server -> F.flow ->
4747+ (flow, write_error) result Lwt.t
4848+4949+ (** [epoch flow] extracts information of the established session. *)
5050+ val epoch : flow -> (Tls.Core.epoch_data, unit) result
5151+5252+end
5353+5454+(** X.509 handling given a key value store and a clock *)
5555+module X509 (KV : Mirage_kv.RO) : sig
5656+ (** [authenticator ~allowed_hashes ~crl store] creates an [authenticator],
5757+ using the given certificate authorities in the [store] as
5858+ value for key "ca_roots.crt". If [allowed_hashes] is provided,
5959+ only these hash algorithms are allowed for signatures of the certificate chain.
6060+ If [crl] is provided, the corresponding file is read and used as
6161+ revocation list (DER encoded). Both options only apply if [`CAs] is used.
6262+ *)
6363+ val authenticator : ?allowed_hashes:Digestif.hash' list -> ?crl:string ->
6464+ KV.t -> X509.Authenticator.t Lwt.t
6565+6666+ (** [certificate store typ] unmarshals a certificate chain and
6767+ private key material from the [store]. *)
6868+ val certificate : KV.t -> [< `Default | `Name of string ]
6969+ -> Tls.Config.certchain Lwt.t
7070+end
+61
vendor/opam/tls/sni.md
···11+### Server Name Indication
22+33+Some TLS servers might want to provide service for various services,
44+all on the same port, but with different names. The SNI extension
55+allows a client to request a specific server name. The server may use
66+the requested server name to select the X.509 certificate chain which
77+it presents to the client.
88+99+### Configuration interface
1010+1111+A user provides a full certificate chain and a private key
1212+corresponding to the first certificate in the list to OCaml-TLS,
1313+captured by the type `certchain`.
1414+1515+````
1616+type certchain = Certificate.certificate list * Nocrypto.Rsa.priv
1717+````
1818+1919+The `own_cert` polymorphic variant covers the various configuration
2020+options: either no certificate is provided, a single one, multiple
2121+ones (whose common name/subject alternative name are used for
2222+disambiguation), and multiple with a default one.
2323+2424+````
2525+type own_cert = [
2626+ | `None
2727+ | `Single of certchain
2828+ | `Multiple of certchain list
2929+ | `Multiple_default of certchain * certchain list
3030+]
3131+3232+````
3333+3434+### Validation
3535+3636+The configuration of certificates is intertwined with ciphersuites:
3737+each ciphersuite which requires a certificate furthermore depends on
3838+properties of this certificate - RSA and DHE_RSA require the key to be
3939+RSA, RSA requires the X.509v3 extension key_usage to contain
4040+encipherment, DHE_RSA requires key_usage to contain digital_signature.
4141+There must exist at least one certificate with the mentioned
4242+properties for each configured ciphersuite.
4343+4444+Furthermore, to avoid ambiguity, the hostnames in ``Multiple` and
4545+``Multiple_default` certificate lists must be non-overlapping.
4646+4747+### Certificate selection
4848+4949+If the server is configured with only a default certificate, this is
5050+always used.
5151+5252+If the client does not request for a server name, the default
5353+certificate is used.
5454+5555+If the client requests a specific server name:
5656+ - find a strict match
5757+ - find a wildcard match
5858+ - use the default one if present
5959+6060+Only after a certificate is set for the session, the ciphersuite is
6161+selected, depending on the properties of the certificate.
···11+open OUnit2
22+33+let () = Mirage_crypto_rng_unix.use_default ()
44+55+let time f =
66+ let t1 = Sys.time () in
77+ let r = f () in
88+ let t2 = Sys.time () in
99+ ( Printf.eprintf "[time] %f.04 s\n%!" (t2 -. t1) ; r )
1010+1111+let list_to_cstruct xs =
1212+ let buf = Bytes.create (List.length xs) in
1313+ List.iteri (Bytes.set_uint8 buf) xs ;
1414+ Bytes.unsafe_to_string buf
1515+1616+let uint16_to_cstruct i =
1717+ let buf = Bytes.create 2 in
1818+ Bytes.set_uint16_be buf 0 i;
1919+ buf
2020+2121+let hexdump_to_str cs =
2222+ Ohex.encode cs
2323+2424+let assert_cs_eq ?msg cs1 cs2 =
2525+ assert_equal
2626+ ~cmp:String.equal
2727+ ~printer:hexdump_to_str
2828+ ?msg
2929+ cs1 cs2
3030+3131+let rec assert_lists_eq comparison a b =
3232+ match a, b with
3333+ | [], [] -> ()
3434+ | a::r1, b::r2 -> comparison a b ; assert_lists_eq comparison r1 r2
3535+ | _ -> assert_failure "lists not equal"
3636+3737+3838+let assert_sessionid_equal a b =
3939+ match a, b with
4040+ | None, None -> ()
4141+ | Some x, Some y -> assert_cs_eq x y
4242+ | _ -> assert_failure "session id not equal"
4343+4444+let assert_client_extension_equal a b =
4545+ match a, b with
4646+ | `Hostname a, `Hostname b -> assert_equal a b
4747+ | `MaxFragmentLength a, `MaxFragmentLength b -> assert_equal a b
4848+ | `SupportedGroups a, `SupportedGroups b -> assert_lists_eq assert_equal a b
4949+ | `SecureRenegotiation a, `SecureRenegotiation b -> assert_cs_eq a b
5050+ | `Padding a, `Padding b -> assert_equal a b
5151+ | `SignatureAlgorithms a, `SignatureAlgorithms b ->
5252+ assert_lists_eq (fun sa sa' -> assert_equal sa sa') a b
5353+ | `ALPN a, `ALPN b -> assert_lists_eq assert_equal a b
5454+ | _ -> assert_failure "extensions did not match"
5555+5656+let assert_server_extension_equal a b =
5757+ match a, b with
5858+ | `Hostname, `Hostname -> ()
5959+ | `MaxFragmentLength a, `MaxFragmentLength b -> assert_equal a b
6060+ | `SecureRenegotiation a, `SecureRenegotiation b -> assert_cs_eq a b
6161+ | `ALPN a, `ALPN b -> assert_equal a b
6262+ | _ -> assert_failure "extensions did not match"
6363+6464+let make_hostname_ext h =
6565+ (`Hostname (Domain_name.of_string_exn h |> Domain_name.host_exn))
···11+(* NOTE: mostly copied from miou/tls_miou_unix.ml, so any change should be synchronized. *)
22+33+let src = Logs.Src.create "tls-unix"
44+55+module Log = (val Logs.src_log src : Logs.LOG)
66+77+external reraise : exn -> 'a = "%reraise"
88+99+let ( $ ) f x = f x
1010+1111+exception Tls_alert of Tls.Packet.alert_type
1212+exception Tls_failure of Tls.Engine.failure
1313+exception Closed_by_peer
1414+1515+let () =
1616+ Printexc.register_printer @@ function
1717+ | Closed_by_peer -> Some "Connection closed by peer"
1818+ | Tls_alert alert -> Some (Tls.Packet.alert_type_to_string alert)
1919+ | Tls_failure failure -> Some (Tls.Engine.string_of_failure failure)
2020+ | _ -> None
2121+2222+type state =
2323+ [ `Active of Tls.Engine.state
2424+ | `Read_closed of Tls.Engine.state
2525+ | `Write_closed of Tls.Engine.state
2626+ | `Closed
2727+ | `Error of exn ]
2828+2929+type t = {
3030+ role : [ `Server | `Client ];
3131+ fd : Unix.file_descr;
3232+ mutable state : state;
3333+ mutable linger : string option;
3434+ read_buffer_size : int;
3535+ buf : bytes;
3636+ mutable rd_closed : bool;
3737+}
3838+3939+let file_descr { fd; _ } = fd
4040+4141+let half_close state mode =
4242+ match (state, mode) with
4343+ | `Active tls, `read -> `Read_closed tls
4444+ | `Active tls, `write -> `Write_closed tls
4545+ | `Active _, `read_write -> `Closed
4646+ | `Read_closed tls, `read -> `Read_closed tls
4747+ | `Read_closed _, (`write | `read_write) -> `Closed
4848+ | `Write_closed tls, `write -> `Write_closed tls
4949+ | `Write_closed _, (`read | `read_write) -> `Closed
5050+ | ((`Closed | `Error _) as e), (`read | `write | `read_write) -> e
5151+5252+let inject_state tls = function
5353+ | `Active _ -> `Active tls
5454+ | `Read_closed _ -> `Read_closed tls
5555+ | `Write_closed _ -> `Write_closed tls
5656+ | (`Closed | `Error _) as e -> e
5757+5858+let tls_alert a = Tls_alert a
5959+let tls_fail f = Tls_failure f
6060+let inhibit fn v = try fn v with _ -> ()
6161+6262+let rec unix_write fd str off len =
6363+ let written = Unix.write_substring fd str off len in
6464+ if not (Int.equal written len) then
6565+ unix_write fd str (off + written) (len - written)
6666+6767+let write flow str =
6868+ Log.debug (fun m -> m "try to write %d byte(s)" (String.length str));
6969+ try unix_write flow.fd str 0 (String.length str) with
7070+ | Unix.Unix_error ((Unix.EPIPE | Unix.ECONNRESET), _, _) ->
7171+ flow.state <- half_close flow.state `write;
7272+ raise Closed_by_peer
7373+ | Unix.Unix_error (_, _, _) as exn ->
7474+ flow.state <- `Error exn;
7575+ reraise exn
7676+7777+let handle flow tls str =
7878+ match Tls.Engine.handle_tls tls str with
7979+ | Ok (state, eof, `Response resp, `Data data) ->
8080+ Log.debug (fun m -> m "We handled %d byte(s)" (String.length str));
8181+ let state = inject_state state flow.state in
8282+ let state = Option.(value ~default:state (map (fun `Eof -> half_close state `read) eof)) in
8383+ flow.state <- state;
8484+ let to_close = flow.state = `Closed in
8585+ Option.iter (inhibit $ write flow) resp;
8686+ (* NOTE(dinosaure): [write flow] can set [flow.state]. So we must
8787+ check if the actual [flow.state] or the [flow.state] after [write flow]
8888+ want to close the underlying file-descriptor. *)
8989+ if to_close || flow.state = `Closed then Unix.close flow.fd;
9090+ data
9191+ | Error (fail, `Response resp) ->
9292+ let exn = match fail with
9393+ | `Alert a -> tls_alert a | f -> tls_fail f in
9494+ flow.state <- `Error exn;
9595+ let _ = inhibit (write flow) resp in
9696+ raise exn
9797+9898+let read flow =
9999+ match Unix.read flow.fd flow.buf 0 (Bytes.length flow.buf) with
100100+ | 0 -> Ok String.empty
101101+ | len -> Ok (Bytes.sub_string flow.buf 0 len)
102102+ | exception Unix.Unix_error (Unix.ECONNRESET, _, _) -> Ok String.empty
103103+ | exception exn -> Error exn
104104+105105+let not_errored = function `Error _ -> false | _ -> true
106106+107107+let garbage flow = match flow.linger with
108108+ | Some "" | None -> false
109109+ | _ -> true
110110+111111+let read_react flow =
112112+ match flow.state with
113113+ | `Error exn -> raise exn
114114+ | `Read_closed _ | `Closed when garbage flow ->
115115+ (* XXX(dinosaure): [`Closed] can appear "at the same time" than some
116116+ application-data. In that case, we stored them into [t.linger]. Depending
117117+ on who closed the connection, [read_react] gives this /garbage/ in any
118118+ situation (even if the user closed the connection).
119119+120120+ An extra layer with [read] below check if [`Read_closed]/[`Close] comes
121121+ from the network (the peer closed the connection) or the user. In the
122122+ first case, we must give pending application-data. In the second case,
123123+ we must return [0] (or raise [End_of_file]). *)
124124+ let mbuf = flow.linger in
125125+ flow.linger <- None;
126126+ mbuf
127127+ | `Read_closed _ | `Closed ->
128128+ (* XXX(dinosaure): the goal of [read_react] is to read some encrypted bytes
129129+ and try to decrypt them with [handle]. If the linger is empty, this means
130130+ that we're trying to get more data (to decrypt) when we can't get any
131131+ more. From this point of view, it's an error that needs to be notified.
132132+ However, this error can be interpreted in 2 ways:
133133+ - we want to have more data decrypted. In this case, this error is
134134+ expected and may result in the user being told that there is nothing
135135+ left to read (for example, returning 0).
136136+ - we attempt a handshake. In this case, we are dealing with an unexpected
137137+ error. *)
138138+ raise End_of_file
139139+ | `Active _ | `Write_closed _ ->
140140+ Log.debug (fun m -> m "read something from the TLS session");
141141+ match read flow with
142142+ | Error exn ->
143143+ if not_errored flow.state then flow.state <- `Error exn;
144144+ raise exn
145145+ | Ok "" ->
146146+ (* XXX(dinosaure): see [`Read_closed _ | `Closed] case. *)
147147+ raise End_of_file
148148+ | Ok str ->
149149+ Log.debug (fun m -> m "got %d byte(s)" (String.length str));
150150+ match flow.state with
151151+ | `Active tls | `Read_closed tls | `Write_closed tls -> handle flow tls str
152152+ | `Closed -> raise End_of_file
153153+ | `Error exn -> raise exn
154154+[@@ocamlformat "disable"]
155155+156156+let rec read_in flow ?(off= 0) ?len buf =
157157+ let len = Option.value ~default:(Bytes.length buf - off) len in
158158+ let write_in res =
159159+ let rlen = String.length res in
160160+ let mlen = min len rlen in
161161+ Bytes.blit_string res 0 buf off mlen;
162162+ let linger = if mlen < rlen
163163+ then Some (String.sub res mlen (rlen - mlen))
164164+ else None in
165165+ flow.linger <- linger; mlen
166166+ in
167167+ match flow.linger with
168168+ | Some res -> write_in res
169169+ | None -> (
170170+ match read_react flow with
171171+ | None -> read_in ~off ~len flow buf
172172+ | Some res -> write_in res)
173173+174174+let writev flow bufs =
175175+ match flow.state with
176176+ | `Closed | `Write_closed _ -> raise Closed_by_peer
177177+ | `Error exn -> reraise exn
178178+ | `Active tls | `Read_closed tls -> (
179179+ match Tls.Engine.send_application_data tls bufs with
180180+ | Some (tls, answer) ->
181181+ flow.state <- inject_state tls flow.state;
182182+ write flow answer
183183+ | None -> assert false)
184184+185185+let rec drain_handshake flow =
186186+ let push_linger flow mcs =
187187+ match (mcs, flow.linger) with
188188+ | None, _ -> ()
189189+ | scs, None -> flow.linger <- scs
190190+ | Some cs, Some l -> flow.linger <- Some (l ^ cs)
191191+ in
192192+ match flow.state with
193193+ | `Active tls when not (Tls.Engine.handshake_in_progress tls) -> flow
194194+ | (`Read_closed _ | `Closed) when garbage flow -> flow
195195+ | _ ->
196196+ Log.debug (fun m -> m "start to read something from the TLS session");
197197+ let mcs = read_react flow in
198198+ push_linger flow mcs;
199199+ drain_handshake flow
200200+201201+let close flow =
202202+ match flow.state with
203203+ | `Active tls | `Read_closed tls ->
204204+ let tls, str = Tls.Engine.send_close_notify tls in
205205+ flow.rd_closed <- true;
206206+ flow.state <- inject_state tls flow.state;
207207+ flow.state <- `Closed;
208208+ inhibit (write flow) str;
209209+ Unix.close flow.fd
210210+ | `Write_closed _ ->
211211+ flow.rd_closed <- true;
212212+ flow.state <- `Closed;
213213+ Unix.close flow.fd
214214+ | `Closed -> flow.rd_closed <- true
215215+ | `Error _ ->
216216+ flow.rd_closed <- true;
217217+ Unix.close flow.fd
218218+219219+let closed_by_user flow = function
220220+ | `read | `read_write -> flow.rd_closed <- true
221221+ | `write -> ()
222222+223223+let shutdown flow mode =
224224+ closed_by_user flow mode;
225225+ match (flow.state, mode) with
226226+ | `Active tls, `read ->
227227+ Log.debug (fun m -> m "shutdown `read");
228228+ flow.state <- inject_state tls (half_close flow.state mode)
229229+ | (`Active tls | `Read_closed tls), (`write | `read_write) ->
230230+ let tls, str = Tls.Engine.send_close_notify tls in
231231+ flow.state <- inject_state tls (half_close flow.state mode);
232232+ (* NOTE(dinosaure): [write flow] can set [flow.state]. So we must
233233+ check if the actual [flow.state] or the [flow.state] after [write flow]
234234+ want to close the underlying file-descriptor. *)
235235+ let to_close = flow.state = `Closed in
236236+ inhibit (write flow) str;
237237+ if to_close || flow.state = `Closed then Unix.close flow.fd
238238+ | `Write_closed tls, (`read | `read_write) ->
239239+ flow.state <- inject_state tls (half_close flow.state mode);
240240+ if flow.state = `Closed then Unix.close flow.fd
241241+ | `Error _, _ -> Unix.close flow.fd
242242+ | `Read_closed _, `read -> ()
243243+ | `Write_closed _, `write -> ()
244244+ | `Closed, _ -> ()
245245+246246+let client_of_fd conf ?(read_buffer_size = 0x1000) ?host fd =
247247+ let conf' =
248248+ match host with None -> conf | Some host -> Tls.Config.peer conf host
249249+ in
250250+ let tls, init = Tls.Engine.client conf' in
251251+ let tls_flow =
252252+ {
253253+ role = `Client;
254254+ fd;
255255+ state = `Active tls;
256256+ linger = None;
257257+ read_buffer_size;
258258+ buf = Bytes.make read_buffer_size '\000';
259259+ rd_closed = false;
260260+ }
261261+ in
262262+ write tls_flow init;
263263+ drain_handshake tls_flow
264264+265265+let server_of_fd conf ?(read_buffer_size = 0x1000) fd =
266266+ let tls = Tls.Engine.server conf in
267267+ let tls_flow =
268268+ {
269269+ role = `Server;
270270+ fd;
271271+ state = `Active tls;
272272+ linger = None;
273273+ read_buffer_size;
274274+ buf = Bytes.make read_buffer_size '\000';
275275+ rd_closed = false;
276276+ }
277277+ in
278278+ drain_handshake tls_flow
279279+280280+let write flow ?(off = 0) ?len str =
281281+ let len = Option.value ~default:(String.length str - off) len in
282282+ if off < 0 || len < 0 || off > String.length str - len
283283+ then invalid_arg "Tls_unix.write";
284284+ if len > 0 then writev flow [ String.sub str off len ]
285285+286286+let read t ?(off= 0) ?len buf =
287287+ let len = Option.value ~default:(Bytes.length buf - off) len in
288288+ if off < 0 || len < 0 || off > Bytes.length buf - len
289289+ then invalid_arg "Tls_unix.read";
290290+ if t.rd_closed then 0
291291+ else try read_in t ~off ~len buf with End_of_file -> 0
292292+293293+let rec really_read_go t off len buf =
294294+ let len' = read t buf ~off ~len in
295295+ if len' == 0 then raise End_of_file
296296+ else if len - len' > 0
297297+ then really_read_go t (off + len') (len - len') buf
298298+299299+let really_read t ?(off= 0) ?len buf =
300300+ let len = Option.value ~default:(Bytes.length buf - off) len in
301301+ if off < 0 || len < 0 || off > Bytes.length buf - len
302302+ then invalid_arg "Tls_unix.really_read";
303303+ if len > 0 then really_read_go t off len buf
304304+305305+let resolve host service =
306306+ let tcp = Unix.getprotobyname "tcp" in
307307+ match Unix.getaddrinfo host service [ AI_PROTOCOL tcp.p_proto ] with
308308+ | [] -> Fmt.invalid_arg "No address for %s:%s" host service
309309+ | ai :: _ -> ai.ai_addr
310310+311311+let connect authenticator (v, port) =
312312+ let conf =
313313+ match Tls.Config.client ~authenticator () with
314314+ | Ok config -> config
315315+ | Error `Msg msg -> Fmt.invalid_arg "Configuration failure: %s" msg
316316+ in
317317+ let addr = resolve v (string_of_int port) in
318318+ let fd =
319319+ match addr with
320320+ | Unix.ADDR_UNIX _ -> invalid_arg "Tls_unix.connect: Invalid UNIX socket"
321321+ | Unix.ADDR_INET (inet_addr, _) ->
322322+ if Unix.is_inet6_addr inet_addr then
323323+ Unix.socket Unix.PF_INET6 Unix.SOCK_STREAM 0
324324+ else
325325+ Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0
326326+ in
327327+ let host = Result.to_option Domain_name.(Result.bind (of_string v) host) in
328328+ match Unix.connect fd addr with
329329+ | () -> client_of_fd conf ?host fd
330330+ | exception exn ->
331331+ Unix.close fd;
332332+ raise exn
333333+334334+let epoch flow = match flow.state with
335335+ | `Active tls | `Read_closed tls | `Write_closed tls ->
336336+ ( match Tls.Engine.epoch tls with
337337+ | Error () -> assert false
338338+ | Ok data -> Some data )
339339+ | _ -> None
+96
vendor/opam/tls/unix/tls_unix.mli
···11+(** Effectful operations using Unix for pure TLS.
22+33+ The pure TLS is state and buffer in, state and buffer out. This module uses
44+ the Unix layer for communication over the network. *)
55+66+exception Tls_alert of Tls.Packet.alert_type
77+exception Tls_failure of Tls.Engine.failure
88+exception Closed_by_peer
99+1010+type t
1111+(** Abstract type of a session. *)
1212+1313+val file_descr : t -> Unix.file_descr
1414+(** [file_descr] returns the underlying file-descriptor used by the given
1515+ TLS {i socket}. *)
1616+1717+val read : t -> ?off:int -> ?len:int -> bytes -> int
1818+(** [read fd buf ~off ~len] reads up to [len] bytes (defaults to
1919+ [Bytes.length buf - off] from the given TLS {i socket} [fd], storing them in
2020+ byte sequence [buf], starting at position [off] in [buf] (defaults to [0]).
2121+ It returns the actual number of characters read, between 0 and [len]
2222+ (inclusive).
2323+2424+ @raise Unix_error raised by the system call {!val:Unix.read}. The function
2525+ handles {!val:Unix.EINTR}, {!val:Unix.EAGAIN} and {!val:Unix.EWOULDBLOCK}
2626+ exceptions and redo the system call.
2727+2828+ @raise Invalid_argument if [off] and [len] do not designate a valid range of
2929+ [buf]. *)
3030+3131+val really_read : t -> ?off:int -> ?len:int -> bytes -> unit
3232+(** [really_read fd buf ~off ~len] reads [len] bytes (defaults to
3333+ [Bytes.length buf - off]) from the given TLS {i socket} [fd], storing them
3434+ in byte sequence [buf], starting at position [off] in [buf] (defaults to
3535+ [0]). If [len = 0], [really_read] does nothing.
3636+3737+ @raise Unix_error raised by the system call {!val:Unix.read}. The function
3838+ handles {!val:Unix.EINTR}, {!val:Unix.EAGAIN} and {!val:Unix.EWOULDBLOCK}
3939+ exceptions and redo the system call.
4040+4141+ @raise End_of_file if {!val:Unix.read} returns [0] before [len] characters
4242+ have been read.
4343+4444+ @raise Invalid_argument if [off] and [len] do not designate a valid range of
4545+ [buf]. *)
4646+4747+val write : t -> ?off:int -> ?len:int -> string -> unit
4848+(** [write t str ~off ~len] writes [len] bytes (defaults to
4949+ [String.length str - off]) from byte sequence [str], starting at offset
5050+ [off] (defaults to [0]), to the given TLS {i socket} [fd].
5151+5252+ @raise Unix_error raised by the syscall call {!val:Unix.write}. The function
5353+ handles {!val:Unix.EINTR}, {!val:Unix.EAGAIN} and {!val:Unix.EWOULDBLOCK}
5454+ exceptions and redo the system call.
5555+5656+ @raise Closed_by_peer if [t] is connected to a peer whose reading end is
5757+ closed. Similar to the {!val:EPIPE} error for pipe/socket connected.
5858+5959+ @raise Invalid_argument if [off] and [len] do not designate a valid range of
6060+ [buf]. *)
6161+6262+val close : t -> unit
6363+(** [close flow] closes the TLS session and the underlying file-descriptor. *)
6464+6565+val shutdown : t -> [ `read | `write | `read_write ] -> unit
6666+(** [shutdown t direction] closes the direction of the TLS session [t]. If
6767+ [`read_write] or [`write] is closed, a TLS close-notify is sent to the other
6868+ endpoint. If this results in a fully-closed session (or an errorneous
6969+ session), the underlying file descriptor is closed. *)
7070+7171+val client_of_fd :
7272+ Tls.Config.client ->
7373+ ?read_buffer_size:int ->
7474+ ?host:[ `host ] Domain_name.t ->
7575+ Unix.file_descr ->
7676+ t
7777+(** [client_of_flow client ~host fd] is [t], after client-side TLS handshake of
7878+ [fd] using [client] configuration and [host].
7979+8080+ @raise End_of_file if we are not able to complete the handshake. *)
8181+8282+val server_of_fd :
8383+ Tls.Config.server -> ?read_buffer_size:int -> Unix.file_descr -> t
8484+(** [server_of_fd server fd] is [t], after server-side TLS handshake of [fd]
8585+ using [server] configuration.
8686+8787+ @raise End_of_file if we are not able to complete the handshake. *)
8888+8989+val connect : X509.Authenticator.t -> string * int -> t
9090+(** [connect authenticator (host, port)] is [t], a connected TLS connection
9191+ to [host] on [port] using the default configuration and the
9292+ [authenticator]. *)
9393+9494+val epoch : t -> Tls.Core.epoch_data option
9595+(** [epoch t] returns [epoch], which contains information of the active
9696+ session. *)