···66open Handshake_crypto13
77open Result.Syntax
8899-let answer_client_hello ~hrr state ch raw =
1010- let* () = client_hello_valid `TLS_1_3 ch in
1111- let* () =
1212- guard (not (hrr && List.mem `EarlyDataIndication ch.extensions))
1313- (`Fatal (`Handshake (`Message "has 0RTT after hello retry request")))
99+let extract_supported_groups exts =
1010+ let* gs =
1111+ Option.to_result
1212+ ~none:(`Fatal (`Missing_extension "supported group"))
1313+ (Utils.map_find ~f:(function `SupportedGroups gs -> Some gs | _ -> None) exts)
1414 in
1515- Tracing.debug (fun m -> m "version %a" pp_tls_version `TLS_1_3) ;
1515+ Ok (List.filter_map Core.named_group_to_group gs)
16161717- let ciphers =
1818- List.filter_map Ciphersuite.any_ciphersuite_to_ciphersuite13 ch.ciphersuites
1717+let extract_keyshares exts =
1818+ let* ks =
1919+ Option.to_result
2020+ ~none:(`Fatal (`Missing_extension "key share"))
2121+ (Utils.map_find ~f:(function `KeyShare ks -> Some ks | _ -> None) exts)
1922 in
2323+ List.fold_left (fun acc (g, ks) ->
2424+ let* acc = acc in
2525+ match Core.named_group_to_group g with
2626+ | None -> Ok acc
2727+ | Some g -> Ok ((g, ks) :: acc))
2828+ (Ok []) ks
20292121- let* groups =
2222- let* gs =
2323- Option.to_result
2424- ~none:(`Fatal (`Missing_extension "supported group"))
2525- (Utils.map_find ~f:(function `SupportedGroups gs -> Some gs | _ -> None) ch.extensions)
2626- in
2727- Ok (List.filter_map Core.named_group_to_group gs)
3030+let make_server_hello13 (ch : client_hello) cipher epoch extensions =
3131+ let ciphersuite = (cipher :> Ciphersuite.ciphersuite) in
3232+ let sh =
3333+ { server_version = `TLS_1_3 ;
3434+ server_random = Crypto_rng.generate 32 ;
3535+ sessionid = ch.sessionid ;
3636+ ciphersuite ;
3737+ extensions }
3838+ in
3939+ let session : session_data13 =
4040+ let base = match epoch with None -> empty_session13 cipher | Some e -> session13_of_epoch cipher e in
4141+ let common_session_data13 = {
4242+ base.common_session_data13 with
4343+ server_random = sh.server_random ;
4444+ client_random = ch.client_random ;
4545+ } in
4646+ let resumed = match epoch with None -> false | Some _ -> true in
4747+ { base with common_session_data13 ; ciphersuite13 = cipher ; resumed }
2848 in
4949+ (sh, session)
29503030- let* keyshares =
3131- let* ks =
3232- Option.to_result
3333- ~none:(`Fatal (`Missing_extension "key share"))
3434- (Utils.map_find ~f:(function `KeyShare ks -> Some ks | _ -> None) ch.extensions)
3535- in
3636- List.fold_left (fun acc (g, ks) ->
3737- let* acc = acc in
3838- match Core.named_group_to_group g with
3939- | None -> Ok acc
4040- | Some g -> Ok ((g, ks) :: acc))
4141- (Ok []) ks
5151+let send_hello_retry state config (ch : client_hello) raw cipher group =
5252+ let cookie =
5353+ let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 cipher)) in
5454+ H.(to_raw_string (digest_string raw))
4255 in
5656+ let hrr = { retry_version = `TLS_1_3 ; ciphersuite = cipher ; sessionid = ch.sessionid ; selected_group = group ; extensions = [ `Cookie cookie ] } in
5757+ let hrr_raw = Writer.assemble_handshake (HelloRetryRequest hrr) in
5858+ Tracing.hs ~tag:"handshake-out" (HelloRetryRequest hrr) ;
5959+ (* there is no early data anymore if HRR was sent (see 4.1.2) *)
6060+ (* but the client wouldn't know until it received the HRR *)
6161+ let early_data_left = if List.mem `EarlyDataIndication ch.extensions then config.Config.zero_rtt else 0l in
6262+ let machina = Server13 AwaitClientHelloHRR13 in
6363+ Ok ({ state with early_data_left ; machina },
6464+ `Record (Packet.HANDSHAKE, hrr_raw) ::
6565+ (match ch.sessionid with
6666+ | None -> []
6767+ | Some _ -> [`Record change_cipher_spec]))
43684444- let base_server_hello ?epoch cipher extensions =
4545- let ciphersuite = (cipher :> Ciphersuite.ciphersuite) in
4646- let sh =
4747- { server_version = `TLS_1_3 ;
4848- server_random = Crypto_rng.generate 32 ;
4949- sessionid = ch.sessionid ;
5050- ciphersuite ;
5151- extensions }
5252- in
5353- let session : session_data13 =
5454- let base = match epoch with None -> empty_session13 cipher | Some e -> session13_of_epoch cipher e in
5555- let common_session_data13 = {
5656- base.common_session_data13 with
5757- server_random = sh.server_random ;
5858- client_random = ch.client_random ;
5959- } in
6060- let resumed = match epoch with None -> false | Some _ -> true in
6161- { base with common_session_data13 ; ciphersuite13 = cipher ; resumed }
6262- in
6363- (sh, session)
6464- in
6565- let config = state.config in
6969+let resolve_psk config hrr cipher hostname (ch : client_hello) raw log hlen =
7070+ let secret ?(psk = String.make hlen '\x00') () = Handshake_crypto13.(derive (empty cipher) psk) in
7171+ let no_resume = secret (), None, [], false in
6672 match
6767- Utils.first_match (List.map fst keyshares) config.Config.groups,
6868- Utils.first_match ciphers (Config.ciphers13 config)
7373+ config.Config.ticket_cache,
7474+ Utils.map_find ~f:(function `PreSharedKeys ids -> Some ids | _ -> None) ch.extensions,
7575+ Utils.map_find ~f:(function `PskKeyExchangeModes ms -> Some ms | _ -> None) ch.extensions
6976 with
7070- | _, None -> Error (`Error (`NoConfiguredCiphersuite ciphers))
7171- | None, Some cipher ->
7272- if hrr then
7373- (* avoid loops CH -> HRR -> CH -> HRR -> ... *)
7474- Error (`Fatal (`Handshake (`Message "hello retry request already sent, still no supported group")))
7777+ | None, _, _ | _, None, _ -> no_resume
7878+ | Some _, Some _, None -> no_resume (* should this lead to an error instead? *)
7979+ | Some cache, Some ids, Some ms ->
8080+ if not (List.mem Packet.PSK_KE_DHE ms) then
8181+ no_resume
7582 else
7676- (* no keyshare, looks whether there's a supported group ++ send back HRR *)
7777- begin match Utils.first_match groups config.Config.groups with
7878- | None -> Error (`Fatal (`Handshake (`Message "no supported group found")))
7979- | Some group ->
8080- let cookie =
8181- let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 cipher)) in
8282- H.(to_raw_string (digest_string raw))
8383+ let idx_ids = List.mapi (fun i id -> (i, id)) ids in
8484+ match
8585+ List.filter (fun (_, ((id, _), _)) ->
8686+ match cache.Config.lookup id with None -> false | Some _ -> true)
8787+ idx_ids
8888+ with
8989+ | [] ->
9090+ Log.info (fun m -> m "found no id in psk cache") ;
9191+ no_resume
9292+ | (idx, ((id, obf_age), binder))::_ ->
9393+ (* need to verify binder, do the obf_age computations + checking,
9494+ figure out whether the id is in our psk cache, and use the resumption secret as input
9595+ and Ok the idx *)
9696+ let psk, old_epoch =
9797+ match cache.Config.lookup id with
9898+ | None -> assert false (* see above *)
9999+ | Some x -> x
100100+ in
101101+ match Ciphersuite.(any_ciphersuite_to_ciphersuite13 (ciphersuite_to_any_ciphersuite old_epoch.ciphersuite)) with
102102+ | None -> no_resume
103103+ | Some c' ->
104104+ if c' = cipher &&
105105+ match hostname, old_epoch.own_name with
106106+ | None, None -> true
107107+ | Some x, Some y -> Domain_name.equal x y
108108+ | _ -> false
109109+ then
110110+ let now = cache.Config.timestamp () in
111111+ let server_delta_t = Ptime.diff now psk.issued_at in
112112+ let client_delta_t =
113113+ match Ptime.Span.of_float_s Int32.(to_float (sub obf_age psk.obfuscation) /. 1000.) with
114114+ | None ->
115115+ Logs.debug (fun m -> m "client_delta is not computable, using 0") ;
116116+ Ptime.Span.zero
117117+ | Some x -> x
118118+ in
119119+ (* ensure server&client_delta_t are not too far off! *)
120120+ match Ptime.Span.(to_int_s (abs (sub server_delta_t client_delta_t))) with
121121+ | None ->
122122+ Logs.debug (fun m -> m "s_c_delta computation lead nowhere") ;
123123+ no_resume
124124+ | Some s_c_delta ->
125125+ if s_c_delta > 10 then begin
126126+ Logs.debug (fun m -> m "delta between client and server is %d seconds, ignoring this ticket!" s_c_delta);
127127+ no_resume
128128+ end else
129129+ (* if ticket_creation ts + lifetime > now, continue *)
130130+ let until = match Ptime.add_span psk.issued_at (Ptime.Span.of_int_s (Int32.to_int cache.Config.lifetime)) with
131131+ | None -> Ptime.epoch
132132+ | Some ts -> ts
133133+ in
134134+ if Ptime.is_earlier now ~than:until then
135135+ let early_secret = secret ~psk:psk.secret () in
136136+ let binder_key = Handshake_crypto13.derive_secret early_secret "res binder" "" in
137137+ let binders_len = binders_len ids in
138138+ let ch_part = String.(sub raw 0 (length raw - binders_len)) in
139139+ let log = log ^ ch_part in
140140+ let binder' = Handshake_crypto13.finished early_secret.hash binder_key log in
141141+ if String.equal binder binder' then begin
142142+ (* from 4.1.2 - earlydata is not allowed after hrr *)
143143+ let zero = idx = 0 && not hrr && List.mem `EarlyDataIndication ch.extensions in
144144+ early_secret, Some old_epoch, [ `PreSharedKey idx ], zero
145145+ end else
146146+ no_resume
147147+ else
148148+ no_resume
149149+ else
150150+ no_resume
151151+152152+let send_server_cert_messages config session cipher sigalgs chain priv log =
153153+ if session.resumed then
154154+ Ok ([], log, session)
155155+ else
156156+ let out, log, session = match config.Config.authenticator with
157157+ | None -> [], log, session
158158+ | Some _ ->
159159+ let certreq =
160160+ let exts =
161161+ `SignatureAlgorithms config.Config.signature_algorithms ::
162162+ (match config.Config.acceptable_cas with
163163+ | [] -> []
164164+ | cas -> [ `CertificateAuthorities cas ])
83165 in
8484- let hrr = { retry_version = `TLS_1_3 ; ciphersuite = cipher ; sessionid = ch.sessionid ; selected_group = group ; extensions = [ `Cookie cookie ] } in
8585- let hrr_raw = Writer.assemble_handshake (HelloRetryRequest hrr) in
8686- Tracing.hs ~tag:"handshake-out" (HelloRetryRequest hrr) ;
8787- (* there is no early data anymore if HRR was sent (see 4.1.2) *)
8888- (* but the client wouldn't know until it received the HRR *)
8989- let early_data_left = if List.mem `EarlyDataIndication ch.extensions then config.Config.zero_rtt else 0l in
9090- let machina = Server13 AwaitClientHelloHRR13 in
9191- Ok ({ state with early_data_left ; machina },
9292- `Record (Packet.HANDSHAKE, hrr_raw) ::
9393- (match ch.sessionid with
9494- | None -> []
9595- | Some _ -> [`Record change_cipher_spec]))
9696- end
9797- | Some group, Some cipher ->
9898- Log.debug (fun m -> m "cipher %a" Ciphersuite.pp_ciphersuite cipher) ;
9999- Log.debug (fun m -> m "group %a" pp_group group) ;
166166+ CertificateRequest (Writer.assemble_certificate_request_1_3 exts)
167167+ in
168168+ Tracing.hs ~tag:"handshake-out" certreq ;
169169+ let raw_cert_req = Writer.assemble_handshake certreq in
170170+ let common_session_data13 = { session.common_session_data13 with client_auth = true } in
171171+ [raw_cert_req], log ^ raw_cert_req, { session with common_session_data13 }
172172+ in
100173101101- if not (List.mem group groups) then
102102- Error (`Fatal (`Handshake (`Message "keyshare group not in group list")))
103103- else
104104- (* we already checked above in keyshares that group is present there *)
105105- let keyshare =
106106- snd (List.find (fun (g, _) -> g = group) keyshares)
107107- in
108108- (* DHE - full handshake *)
174174+ let certs = List.map X509.Certificate.encode_der chain in
175175+ let cert = Certificate (Writer.assemble_certificates_1_3 "" certs) in
176176+ let cert_raw = Writer.assemble_handshake cert in
177177+ Tracing.hs ~tag:"handshake-out" cert ;
178178+ let log = log ^ cert_raw in
109179110110- let* log =
111111- if hrr then
112112- let* c =
113113- Option.to_result
114114- ~none:(`Fatal (`Missing_extension "cookie"))
115115- (Utils.map_find ~f:(function `Cookie c -> Some c | _ -> None) ch.extensions)
116116- in
117117- (* log is: 254 00 00 length c :: HRR *)
118118- let hash_hdr = Writer.assemble_message_hash (String.length c) in
119119- let hrr = { retry_version = `TLS_1_3 ; ciphersuite = cipher ; sessionid = ch.sessionid ; selected_group = group ; extensions = [ `Cookie c ]} in
120120- let hs_buf = Writer.assemble_handshake (HelloRetryRequest hrr) in
121121- Ok (String.concat "" [ hash_hdr ; c ; hs_buf ])
122122- else
123123- Ok ""
124124- in
180180+ let tbs =
181181+ let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 cipher)) in
182182+ H.(to_raw_string (digest_string log))
183183+ in
184184+ let* signed =
185185+ signature `TLS_1_3
186186+ ~context_string:"TLS 1.3, server CertificateVerify"
187187+ tbs (Some sigalgs) config.Config.signature_algorithms priv
188188+ in
189189+ let cv = CertificateVerify signed in
190190+ let cv_raw = Writer.assemble_handshake cv in
191191+ Tracing.hs ~tag:"handshake-out" cv ;
192192+ let log = log ^ cv_raw in
193193+ Ok (out @ [cert_raw; cv_raw], log, session)
125194126126- let hostname = hostname ch in
127127- let hlen =
128128- let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 cipher)) in
129129- H.digest_size
130130- in
195195+let make_session_ticket config session =
196196+ match session.resumed, config.Config.ticket_cache with
197197+ | true, _ | _, None -> None, []
198198+ | false, Some cache ->
199199+ let age_add =
200200+ let cs = Crypto_rng.generate 4 in
201201+ String.get_int32_be cs 0
202202+ in
203203+ let psk_id = Crypto_rng.generate 32 in
204204+ let nonce = Crypto_rng.generate 4 in
205205+ let extensions = match config.Config.zero_rtt with
206206+ | 0l -> []
207207+ | x -> [ `EarlyDataIndication x ]
208208+ in
209209+ let st = { lifetime = cache.Config.lifetime ; age_add ; nonce ; ticket = psk_id ; extensions } in
210210+ Tracing.hs ~tag:"handshake-out" (SessionTicket st) ;
211211+ let st_raw = Writer.assemble_handshake (SessionTicket st) in
212212+ (Some st, [st_raw])
131213132132- let early_secret, epoch, exts, can_use_early_data =
133133- let secret ?(psk = String.make hlen '\x00') () = Handshake_crypto13.(derive (empty cipher) psk) in
134134- let no_resume = secret (), None, [], false in
135135- match
136136- config.Config.ticket_cache,
137137- Utils.map_find ~f:(function `PreSharedKeys ids -> Some ids | _ -> None) ch.extensions,
138138- Utils.map_find ~f:(function `PskKeyExchangeModes ms -> Some ms | _ -> None) ch.extensions
139139- with
140140- | None, _, _ | _, None, _ -> no_resume
141141- | Some _, Some _, None -> no_resume (* should this lead to an error instead? *)
142142- | Some cache, Some ids, Some ms ->
143143- if not (List.mem Packet.PSK_KE_DHE ms) then
144144- no_resume
145145- else
146146- let idx_ids = List.mapi (fun i id -> (i, id)) ids in
147147- match
148148- List.filter (fun (_, ((id, _), _)) ->
149149- match cache.Config.lookup id with None -> false | Some _ -> true)
150150- idx_ids
151151- with
152152- | [] ->
153153- Log.info (fun m -> m "found no id in psk cache") ;
154154- no_resume
155155- | (idx, ((id, obf_age), binder))::_ ->
156156- (* need to verify binder, do the obf_age computations + checking,
157157- figure out whether the id is in our psk cache, and use the resumption secret as input
158158- and Ok the idx *)
159159- let psk, old_epoch =
160160- match cache.Config.lookup id with
161161- | None -> assert false (* see above *)
162162- | Some x -> x
163163- in
164164- match Ciphersuite.(any_ciphersuite_to_ciphersuite13 (ciphersuite_to_any_ciphersuite old_epoch.ciphersuite)) with
165165- | None -> no_resume
166166- | Some c' ->
167167- if c' = cipher &&
168168- match hostname, old_epoch.own_name with
169169- | None, None -> true
170170- | Some x, Some y -> Domain_name.equal x y
171171- | _ -> false
172172- then
173173- let now = cache.Config.timestamp () in
174174- let server_delta_t = Ptime.diff now psk.issued_at in
175175- let client_delta_t =
176176- match Ptime.Span.of_float_s Int32.(to_float (sub obf_age psk.obfuscation) /. 1000.) with
177177- | None ->
178178- Logs.debug (fun m -> m "client_delta is not computable, using 0") ;
179179- Ptime.Span.zero
180180- | Some x -> x
181181- in
182182- (* ensure server&client_delta_t are not too far off! *)
183183- match Ptime.Span.(to_int_s (abs (sub server_delta_t client_delta_t))) with
184184- | None ->
185185- Logs.debug (fun m -> m "s_c_delta computation lead nowhere") ;
186186- no_resume
187187- | Some s_c_delta ->
188188- if s_c_delta > 10 then begin
189189- Logs.debug (fun m -> m "delta between client and server is %d seconds, ignoring this ticket!" s_c_delta);
190190- no_resume
191191- end else
192192- (* if ticket_creation ts + lifetime > now, continue *)
193193- let until = match Ptime.add_span psk.issued_at (Ptime.Span.of_int_s (Int32.to_int cache.Config.lifetime)) with
194194- | None -> Ptime.epoch
195195- | Some ts -> ts
196196- in
197197- if Ptime.is_earlier now ~than:until then
198198- let early_secret = secret ~psk:psk.secret () in
199199- let binder_key = Handshake_crypto13.derive_secret early_secret "res binder" "" in
200200- let binders_len = binders_len ids in
201201- let ch_part = String.(sub raw 0 (length raw - binders_len)) in
202202- let log = log ^ ch_part in
203203- let binder' = Handshake_crypto13.finished early_secret.hash binder_key log in
204204- if String.equal binder binder' then begin
205205- (* from 4.1.2 - earlydata is not allowed after hrr *)
206206- let zero = idx = 0 && not hrr && List.mem `EarlyDataIndication ch.extensions in
207207- early_secret, Some old_epoch, [ `PreSharedKey idx ], zero
208208- end else
209209- no_resume
210210- else
211211- no_resume
212212- else
213213- no_resume
214214- in
214214+let complete_server_handshake ~hrr state config (ch : client_hello) hlen hs_secret
215215+ server_hs_secret server_ctx client_hs_secret client_ctx early_traffic_ctx
216216+ can_use_early_data sh_raw ee_raw c_out session' log =
217217+ let master_secret = Handshake_crypto13.derive hs_secret (String.make hlen '\x00') in
218218+ Tracing.cs ~tag:"master-secret" master_secret.secret ;
219219+220220+ let f_data = finished hs_secret.hash server_hs_secret log in
221221+ let fin = Finished f_data in
222222+ let fin_raw = Writer.assemble_handshake fin in
223223+224224+ Tracing.hs ~tag:"handshake-out" fin ;
225225+226226+ let log = log ^ fin_raw in
227227+ let server_app_secret, server_app_ctx, client_app_secret, client_app_ctx =
228228+ app_ctx master_secret log
229229+ in
230230+ let exporter_master_secret = Handshake_crypto13.exporter master_secret log in
231231+ let session' = { session' with server_app_secret ; client_app_secret ; exporter_master_secret } in
215232216216- let _, early_traffic_ctx = Handshake_crypto13.early_traffic early_secret raw in
233233+ let* () =
234234+ guard (String.length state.hs_fragment = 0)
235235+ (`Fatal (`Handshake `Fragments))
236236+ in
217237218218- let secret, public = Handshake_crypto13.dh_gen_key group in
219219- let* es = Handshake_crypto13.dh_shared secret keyshare in
220220- let hs_secret = Handshake_crypto13.derive early_secret es in
221221- Tracing.cs ~tag:"hs secret" hs_secret.secret ;
238238+ (* send sessionticket early *)
239239+ (* TODO track the nonce across handshakes / newsessionticket messages (i.e. after post-handshake auth) - needs to be unique! *)
240240+ let st, st_raw = make_session_ticket config session' in
222241223223- let sh, session = base_server_hello ?epoch cipher (`KeyShare (group, public) :: exts) in
224224- let sh_raw = Writer.assemble_handshake (ServerHello sh) in
225225- Tracing.hs ~tag:"handshake-out" (ServerHello sh) ;
242242+ let session =
243243+ let common_session_data13 = { session'.common_session_data13 with master_secret = master_secret.secret } in
244244+ { session' with common_session_data13 ; master_secret }
245245+ in
246246+ let st, session =
247247+ if can_use_early_data then
248248+ (AwaitEndOfEarlyData13 (client_hs_secret, client_ctx, client_app_ctx, st, log),
249249+ `TLS13 { session with state = `ZeroRTT } :: state.session)
250250+ else if session.common_session_data13.client_auth then
251251+ (AwaitClientCertificate13 (session, client_hs_secret, client_app_ctx, st, log),
252252+ state.session)
253253+ else
254254+ (AwaitClientFinished13 (client_hs_secret, client_app_ctx, st, log),
255255+ `TLS13 session :: state.session)
256256+ in
257257+ let early_data_left = if List.mem `EarlyDataIndication ch.extensions then config.Config.zero_rtt else 0l in
258258+ Ok ({ state with machina = Server13 st ; session ; early_data_left },
259259+ `Record (Packet.HANDSHAKE, sh_raw) ::
260260+ (match ch.sessionid with
261261+ | Some _ when not hrr -> [`Record change_cipher_spec]
262262+ | _ -> []) @
263263+ [ `Change_enc server_ctx ;
264264+ `Change_dec (if can_use_early_data then early_traffic_ctx else client_ctx) ;
265265+ `Record (Packet.HANDSHAKE, ee_raw) ] @
266266+ List.map (fun data -> `Record (Packet.HANDSHAKE, data)) c_out @
267267+ [ `Record (Packet.HANDSHAKE, fin_raw) ;
268268+ `Change_enc server_app_ctx ] @
269269+ List.map (fun data -> `Record (Packet.HANDSHAKE, data)) st_raw)
226270227227- let log = log ^ raw ^ sh_raw in
228228- let server_hs_secret, server_ctx, client_hs_secret, client_ctx = hs_ctx hs_secret log in
271271+let handle_dhe_handshake ~hrr state config (ch : client_hello) raw cipher group keyshares groups =
272272+ Log.debug (fun m -> m "cipher %a" Ciphersuite.pp_ciphersuite cipher) ;
273273+ Log.debug (fun m -> m "group %a" pp_group group) ;
229274230230- let* sigalgs =
231231- Option.to_result
232232- ~none:(`Fatal (`Missing_extension "signature algorithms"))
233233- (Utils.map_find ~f:(function `SignatureAlgorithms sa -> Some sa | _ -> None) ch.extensions)
234234- in
235235- (* TODO respect certificate_signature_algs if present *)
275275+ if not (List.mem group groups) then
276276+ Error (`Fatal (`Handshake (`Message "keyshare group not in group list")))
277277+ else
278278+ (* we already checked above in keyshares that group is present there *)
279279+ let keyshare =
280280+ snd (List.find (fun (g, _) -> g = group) keyshares)
281281+ in
282282+ (* DHE - full handshake *)
236283237237- let f = supports_key_usage ~not_present:true `Digital_signature in
238238- let* chain, priv =
239239- let* r = agreed_cert ~f ~signature_algorithms:sigalgs config.Config.own_certificates hostname in
240240- match r with
241241- | c::cs, priv -> Ok (c::cs, priv)
242242- | _ -> Error (`Fatal (`Handshake (`Message "couldn't find certificate chain")))
243243- in
244244- let* alpn_protocol = alpn_protocol config ch in
245245- let session =
246246- let common_session_data13 = { session.common_session_data13 with
247247- own_name = hostname ; own_certificate = chain ;
248248- own_private_key = Some priv ; alpn_protocol }
284284+ let* log =
285285+ if hrr then
286286+ let* c =
287287+ Option.to_result
288288+ ~none:(`Fatal (`Missing_extension "cookie"))
289289+ (Utils.map_find ~f:(function `Cookie c -> Some c | _ -> None) ch.extensions)
249290 in
250250- { session with common_session_data13 }
251251- in
291291+ (* log is: 254 00 00 length c :: HRR *)
292292+ let hash_hdr = Writer.assemble_message_hash (String.length c) in
293293+ let hrr = { retry_version = `TLS_1_3 ; ciphersuite = cipher ; sessionid = ch.sessionid ; selected_group = group ; extensions = [ `Cookie c ]} in
294294+ let hs_buf = Writer.assemble_handshake (HelloRetryRequest hrr) in
295295+ Ok (String.concat "" [ hash_hdr ; c ; hs_buf ])
296296+ else
297297+ Ok ""
298298+ in
252299253253- let ee =
254254- let hostname_ext = Option.fold ~none:[] ~some:(fun _ -> [`Hostname]) hostname
255255- and alpn = Option.fold ~none:[] ~some:(fun proto -> [`ALPN proto]) alpn_protocol
256256- and early_data = if can_use_early_data && config.Config.zero_rtt <> 0l then [ `EarlyDataIndication ] else []
257257- in
258258- EncryptedExtensions (hostname_ext @ alpn @ early_data)
259259- in
260260- (* 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 *)
261261- let ee_raw = Writer.assemble_handshake ee in
262262- Tracing.hs ~tag:"handshake-out" ee ;
263263- let log = log ^ ee_raw in
300300+ let hostname = hostname ch in
301301+ let hlen =
302302+ let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 cipher)) in
303303+ H.digest_size
304304+ in
264305265265- let* c_out, log, session' =
266266- if session.resumed then
267267- Ok ([], log, session)
268268- else
269269- let out, log, session = match config.Config.authenticator with
270270- | None -> [], log, session
271271- | Some _ ->
272272- let certreq =
273273- let exts =
274274- `SignatureAlgorithms config.Config.signature_algorithms ::
275275- (match config.Config.acceptable_cas with
276276- | [] -> []
277277- | cas -> [ `CertificateAuthorities cas ])
278278- in
279279- CertificateRequest (Writer.assemble_certificate_request_1_3 exts)
280280- in
281281- Tracing.hs ~tag:"handshake-out" certreq ;
282282- let raw_cert_req = Writer.assemble_handshake certreq in
283283- let common_session_data13 = { session.common_session_data13 with client_auth = true } in
284284- [raw_cert_req], log ^ raw_cert_req, { session with common_session_data13 }
285285- in
306306+ let early_secret, epoch, exts, can_use_early_data =
307307+ resolve_psk config hrr cipher hostname ch raw log hlen
308308+ in
286309287287- let certs = List.map X509.Certificate.encode_der chain in
288288- let cert = Certificate (Writer.assemble_certificates_1_3 "" certs) in
289289- let cert_raw = Writer.assemble_handshake cert in
290290- Tracing.hs ~tag:"handshake-out" cert ;
291291- let log = log ^ cert_raw in
310310+ let _, early_traffic_ctx = Handshake_crypto13.early_traffic early_secret raw in
292311293293- let tbs =
294294- let module H = (val Digestif.module_of_hash' (Ciphersuite.hash13 cipher)) in
295295- H.(to_raw_string (digest_string log))
296296- in
297297- let* signed =
298298- signature `TLS_1_3
299299- ~context_string:"TLS 1.3, server CertificateVerify"
300300- tbs (Some sigalgs) config.Config.signature_algorithms priv
301301- in
302302- let cv = CertificateVerify signed in
303303- let cv_raw = Writer.assemble_handshake cv in
304304- Tracing.hs ~tag:"handshake-out" cv ;
305305- let log = log ^ cv_raw in
306306- Ok (out @ [cert_raw; cv_raw], log, session)
307307- in
312312+ let secret, public = Handshake_crypto13.dh_gen_key group in
313313+ let* es = Handshake_crypto13.dh_shared secret keyshare in
314314+ let hs_secret = Handshake_crypto13.derive early_secret es in
315315+ Tracing.cs ~tag:"hs secret" hs_secret.secret ;
308316309309- let master_secret = Handshake_crypto13.derive hs_secret (String.make hlen '\x00') in
310310- Tracing.cs ~tag:"master-secret" master_secret.secret ;
317317+ let sh, session = make_server_hello13 ch cipher epoch (`KeyShare (group, public) :: exts) in
318318+ let sh_raw = Writer.assemble_handshake (ServerHello sh) in
319319+ Tracing.hs ~tag:"handshake-out" (ServerHello sh) ;
311320312312- let f_data = finished hs_secret.hash server_hs_secret log in
313313- let fin = Finished f_data in
314314- let fin_raw = Writer.assemble_handshake fin in
321321+ let log = log ^ raw ^ sh_raw in
322322+ let server_hs_secret, server_ctx, client_hs_secret, client_ctx = hs_ctx hs_secret log in
315323316316- Tracing.hs ~tag:"handshake-out" fin ;
324324+ let* sigalgs =
325325+ Option.to_result
326326+ ~none:(`Fatal (`Missing_extension "signature algorithms"))
327327+ (Utils.map_find ~f:(function `SignatureAlgorithms sa -> Some sa | _ -> None) ch.extensions)
328328+ in
329329+ (* TODO respect certificate_signature_algs if present *)
317330318318- let log = log ^ fin_raw in
319319- let server_app_secret, server_app_ctx, client_app_secret, client_app_ctx =
320320- app_ctx master_secret log
331331+ let f = supports_key_usage ~not_present:true `Digital_signature in
332332+ let* chain, priv =
333333+ let* r = agreed_cert ~f ~signature_algorithms:sigalgs config.Config.own_certificates hostname in
334334+ match r with
335335+ | c::cs, priv -> Ok (c::cs, priv)
336336+ | _ -> Error (`Fatal (`Handshake (`Message "couldn't find certificate chain")))
337337+ in
338338+ let* alpn_protocol = alpn_protocol config ch in
339339+ let session =
340340+ let common_session_data13 = { session.common_session_data13 with
341341+ own_name = hostname ; own_certificate = chain ;
342342+ own_private_key = Some priv ; alpn_protocol }
321343 in
322322- let exporter_master_secret = Handshake_crypto13.exporter master_secret log in
323323- let session' = { session' with server_app_secret ; client_app_secret ; exporter_master_secret } in
344344+ { session with common_session_data13 }
345345+ in
324346325325- let* () =
326326- guard (String.length state.hs_fragment = 0)
327327- (`Fatal (`Handshake `Fragments))
347347+ let ee =
348348+ let hostname_ext = Option.fold ~none:[] ~some:(fun _ -> [`Hostname]) hostname
349349+ and alpn = Option.fold ~none:[] ~some:(fun proto -> [`ALPN proto]) alpn_protocol
350350+ and early_data = if can_use_early_data && config.Config.zero_rtt <> 0l then [ `EarlyDataIndication ] else []
328351 in
352352+ EncryptedExtensions (hostname_ext @ alpn @ early_data)
353353+ in
354354+ (* 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 *)
355355+ let ee_raw = Writer.assemble_handshake ee in
356356+ Tracing.hs ~tag:"handshake-out" ee ;
357357+ let log = log ^ ee_raw in
329358330330- (* send sessionticket early *)
331331- (* TODO track the nonce across handshakes / newsessionticket messages (i.e. after post-handshake auth) - needs to be unique! *)
332332- let st, st_raw =
333333- match session.resumed, config.Config.ticket_cache with
334334- | true, _ | _, None -> None, []
335335- | false, Some cache ->
336336- let age_add =
337337- let cs = Crypto_rng.generate 4 in
338338- String.get_int32_be cs 0
339339- in
340340- let psk_id = Crypto_rng.generate 32 in
341341- let nonce = Crypto_rng.generate 4 in
342342- let extensions = match config.Config.zero_rtt with
343343- | 0l -> []
344344- | x -> [ `EarlyDataIndication x ]
345345- in
346346- let st = { lifetime = cache.Config.lifetime ; age_add ; nonce ; ticket = psk_id ; extensions } in
347347- Tracing.hs ~tag:"handshake-out" (SessionTicket st) ;
348348- let st_raw = Writer.assemble_handshake (SessionTicket st) in
349349- (Some st, [st_raw])
350350- in
359359+ let* c_out, log, session' =
360360+ send_server_cert_messages config session cipher sigalgs chain priv log
361361+ in
351362352352- let session =
353353- let common_session_data13 = { session'.common_session_data13 with master_secret = master_secret.secret } in
354354- { session' with common_session_data13 ; master_secret }
355355- in
356356- let st, session =
357357- if can_use_early_data then
358358- (AwaitEndOfEarlyData13 (client_hs_secret, client_ctx, client_app_ctx, st, log),
359359- `TLS13 { session with state = `ZeroRTT } :: state.session)
360360- else if session.common_session_data13.client_auth then
361361- (AwaitClientCertificate13 (session, client_hs_secret, client_app_ctx, st, log),
362362- state.session)
363363- else
364364- (AwaitClientFinished13 (client_hs_secret, client_app_ctx, st, log),
365365- `TLS13 session :: state.session)
366366- in
367367- let early_data_left = if List.mem `EarlyDataIndication ch.extensions then config.Config.zero_rtt else 0l in
368368- Ok ({ state with machina = Server13 st ; session ; early_data_left },
369369- `Record (Packet.HANDSHAKE, sh_raw) ::
370370- (match ch.sessionid with
371371- | Some _ when not hrr -> [`Record change_cipher_spec]
372372- | _ -> []) @
373373- [ `Change_enc server_ctx ;
374374- `Change_dec (if can_use_early_data then early_traffic_ctx else client_ctx) ;
375375- `Record (Packet.HANDSHAKE, ee_raw) ] @
376376- List.map (fun data -> `Record (Packet.HANDSHAKE, data)) c_out @
377377- [ `Record (Packet.HANDSHAKE, fin_raw) ;
378378- `Change_enc server_app_ctx ] @
379379- List.map (fun data -> `Record (Packet.HANDSHAKE, data)) st_raw)
363363+ complete_server_handshake ~hrr state config ch hlen hs_secret
364364+ server_hs_secret server_ctx client_hs_secret client_ctx early_traffic_ctx
365365+ can_use_early_data sh_raw ee_raw c_out session' log
366366+367367+let answer_client_hello ~hrr state ch raw =
368368+ let* () = client_hello_valid `TLS_1_3 ch in
369369+ let* () =
370370+ guard (not (hrr && List.mem `EarlyDataIndication ch.extensions))
371371+ (`Fatal (`Handshake (`Message "has 0RTT after hello retry request")))
372372+ in
373373+ Tracing.debug (fun m -> m "version %a" pp_tls_version `TLS_1_3) ;
374374+375375+ let ciphers =
376376+ List.filter_map Ciphersuite.any_ciphersuite_to_ciphersuite13 ch.ciphersuites
377377+ in
378378+379379+ let* groups = extract_supported_groups ch.extensions in
380380+ let* keyshares = extract_keyshares ch.extensions in
381381+ let config = state.config in
382382+ match
383383+ Utils.first_match (List.map fst keyshares) config.Config.groups,
384384+ Utils.first_match ciphers (Config.ciphers13 config)
385385+ with
386386+ | _, None -> Error (`Error (`NoConfiguredCiphersuite ciphers))
387387+ | None, Some cipher ->
388388+ if hrr then
389389+ (* avoid loops CH -> HRR -> CH -> HRR -> ... *)
390390+ Error (`Fatal (`Handshake (`Message "hello retry request already sent, still no supported group")))
391391+ else
392392+ (* no keyshare, looks whether there's a supported group ++ send back HRR *)
393393+ begin match Utils.first_match groups config.Config.groups with
394394+ | None -> Error (`Fatal (`Handshake (`Message "no supported group found")))
395395+ | Some group -> send_hello_retry state config ch raw cipher group
396396+ end
397397+ | Some group, Some cipher ->
398398+ handle_dhe_handshake ~hrr state config ch raw cipher group keyshares groups
380399381400let answer_client_certificate state cert (sd : session_data13) client_fini dec_ctx st raw log =
382401 let* c = map_reader_error (Reader.parse_certificates_1_3 cert) in