Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
writer.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
open Packet open Core let assemble_protocol_version_int buf off version = let major, minor = pair_of_tls_version version in Bytes.set_uint8 buf off major; Bytes.set_uint8 buf (off + 1) minor let assemble_protocol_version ?(buf= Bytes.create 2) version = assemble_protocol_version_int buf 0 version; Bytes.unsafe_to_string buf let assemble_any_protocol_version_into buf off version = let major, minor = pair_of_tls_any_version version in Bytes.set_uint8 buf off major; Bytes.set_uint8 buf (off + 1) minor let assemble_any_protocol_version version = let buf = Bytes.create 2 in assemble_any_protocol_version_into buf 0 version; Bytes.unsafe_to_string buf let assemble_hdr version (content_type, payload) = let buf = Bytes.create 5 in Bytes.set_uint8 buf 0 (content_type_to_int content_type); assemble_protocol_version_int buf 1 version; Bytes.set_uint16_be buf 3 (String.length payload); Bytes.unsafe_to_string buf ^ payload type len = One | Two | Three let assemble_list ?none_if_empty lenb f elements = let length body = match lenb with | One -> let l = Bytes.create 1 in Bytes.set_uint8 l 0 (String.length body) ; Bytes.unsafe_to_string l | Two -> let l = Bytes.create 2 in Bytes.set_uint16_be l 0 (String.length body) ; Bytes.unsafe_to_string l | Three -> let l = Bytes.create 3 in set_uint24_len ~off:0 l (String.length body) ; Bytes.unsafe_to_string l in let b es = String.concat "" (List.map f es) in let full es = let body = b es in length body ^ body in match none_if_empty with | Some _ -> (match elements with | [] -> "" | eles -> full eles) | None -> full elements let assemble_certificate c = let length = String.length c in let buf = Bytes.create 3 in set_uint24_len ~off:0 buf length; Bytes.unsafe_to_string buf ^ c let assemble_certificates cs = assemble_list Three assemble_certificate cs let assemble_compression_method m = String.make 1 (Char.unsafe_chr (compression_method_to_int m)) let assemble_compression_methods ms = assemble_list One assemble_compression_method ms let assemble_any_ciphersuite c = let buf = Bytes.create 2 in Bytes.set_uint16_be buf 0 (any_ciphersuite_to_int c); Bytes.unsafe_to_string buf let assemble_any_ciphersuites cs = assemble_list Two assemble_any_ciphersuite cs let assemble_ciphersuite c = let acs = Ciphersuite.ciphersuite_to_any_ciphersuite c in assemble_any_ciphersuite acs let assemble_hostname host = let host = Domain_name.to_string host in (* 8 bit hostname type; 16 bit length; value *) let vallength = String.length host in let buf = Bytes.create 3 in Bytes.set_uint8 buf 0 0; (* type, only 0 registered *) Bytes.set_uint16_be buf 1 vallength; Bytes.unsafe_to_string buf ^ host let assemble_hostnames hosts = assemble_list Two assemble_hostname hosts let assemble_hash_signature sigalg = let buf = Bytes.create 2 in Bytes.set_uint16_be buf 0 (signature_alg_to_int (to_signature_alg sigalg)) ; Bytes.unsafe_to_string buf let assemble_signature_algorithms s = assemble_list Two assemble_hash_signature s let assemble_certificate_types ts = let ass x = String.make 1 (Char.unsafe_chr (client_certificate_type_to_int x)) in assemble_list One ass ts let assemble_cas cas = let ass x = let buf = Bytes.create 2 in Bytes.set_uint16_be buf 0 (String.length x) ; Bytes.unsafe_to_string buf ^ x in assemble_list Two ass cas let assemble_certificate_request ts cas = assemble_certificate_types ts ^ assemble_cas cas let assemble_certificate_request_1_2 ts sigalgs cas = String.concat "" [ assemble_certificate_types ts; assemble_signature_algorithms sigalgs; assemble_cas cas ] let assemble_named_group g = let buf = Bytes.create 2 in Bytes.set_uint16_be buf 0 (named_group_to_int g); Bytes.unsafe_to_string buf let assemble_group g = assemble_named_group (group_to_named_group g) let assemble_supported_groups groups = assemble_list Two assemble_named_group groups let (ng, ks) = let g = assemble_named_group ng in let l = Bytes.create 2 in Bytes.set_uint16_be l 0 (String.length ks) ; String.concat "" [ g ; Bytes.unsafe_to_string l ; ks ] let assemble_psk_id (id, age) = let id_len = Bytes.create 2 in Bytes.set_uint16_be id_len 0 (String.length id) ; let age_buf = Bytes.create 4 in Bytes.set_int32_be age_buf 0 age ; String.concat "" [ Bytes.unsafe_to_string id_len ; id ; Bytes.unsafe_to_string age_buf ] let assemble_binder b = let b_len = String.make 1 (Char.unsafe_chr (String.length b)) in b_len ^ b let assemble_client_psks psks = let ids, binders = List.split psks in let ids_buf = assemble_list Two assemble_psk_id ids in let binders_buf = assemble_list Two assemble_binder binders in ids_buf ^ binders_buf let assemble_alpn_protocol p = let buf = String.make 1 (Char.unsafe_chr (String.length p)) in buf ^ p let assemble_alpn_protocols protocols = assemble_list Two assemble_alpn_protocol protocols let assemble_supported_versions vs = assemble_list One assemble_any_protocol_version vs let assemble_extension = function | `SecureRenegotiation x -> let buf = String.make 1 (Char.unsafe_chr (String.length x)) in (buf ^ x, RENEGOTIATION_INFO) | `ExtendedMasterSecret -> ("", EXTENDED_MASTER_SECRET) | `ECPointFormats -> (* a list of point formats, we support type 0 = uncompressed unconditionally *) let data = Bytes.make 2 '\x00' in Bytes.set_uint8 data 0 1; (Bytes.unsafe_to_string data, EC_POINT_FORMATS) | _ -> invalid_arg "unknown extension" let c = let l = Bytes.create 2 in Bytes.set_uint16_be l 0 (String.length c) ; Bytes.unsafe_to_string l ^ c let assemble_psk_key_exchange_mode mode = String.make 1 (Char.unsafe_chr (psk_key_exchange_mode_to_int mode)) let assemble_psk_key_exchange_modes modes = assemble_list One assemble_psk_key_exchange_mode modes let assemble_ext (pay, typ) = let buf = Bytes.create 4 in Bytes.set_uint16_be buf 0 (extension_type_to_int typ); Bytes.set_uint16_be buf 2 (String.length pay); Bytes.unsafe_to_string buf ^ pay let assemble_extensions ?none_if_empty assemble_e es = assemble_list ?none_if_empty Two assemble_e es let assemble_ca ca = let lenbuf = Bytes.create 2 in let data = X509.Distinguished_name.encode_der ca in Bytes.set_uint16_be lenbuf 0 (String.length data) ; Bytes.unsafe_to_string lenbuf ^ data let cas = assemble_list Two assemble_ca cas let assemble_certificate_request_extension e = assemble_ext @@ match e with | `SignatureAlgorithms s -> (assemble_signature_algorithms s, SIGNATURE_ALGORITHMS) | `CertificateAuthorities cas -> (assemble_certificate_authorities cas, CERTIFICATE_AUTHORITIES) | _ -> invalid_arg "unknown extension" let assemble_certificate_request_1_3 ?(context = "") exts = let clen = String.make 1 (Char.unsafe_chr (String.length context)) in let exts = assemble_extensions assemble_certificate_request_extension exts in String.concat "" [ clen ; context ; exts ] let assemble_client_extension e = assemble_ext @@ match e with | `SupportedGroups groups -> (assemble_supported_groups groups, SUPPORTED_GROUPS) | `Hostname name -> (assemble_hostnames [name], SERVER_NAME) | `Padding x -> (String.make x '\x00', PADDING) | `SignatureAlgorithms s -> (assemble_signature_algorithms s, SIGNATURE_ALGORITHMS) | `ALPN protocols -> (assemble_alpn_protocols protocols, APPLICATION_LAYER_PROTOCOL_NEGOTIATION) | `KeyShare ks -> (assemble_list Two assemble_keyshare_entry ks, KEY_SHARE) | `PreSharedKeys ids -> (assemble_client_psks ids, PRE_SHARED_KEY) | `EarlyDataIndication -> ("", EARLY_DATA) | `SupportedVersions vs -> (assemble_supported_versions vs, SUPPORTED_VERSIONS) | `PostHandshakeAuthentication -> ("", POST_HANDSHAKE_AUTH) | `Cookie c -> (assemble_cookie c, COOKIE) | `PskKeyExchangeModes modes -> (assemble_psk_key_exchange_modes modes, PSK_KEY_EXCHANGE_MODES) | x -> assemble_extension x let assemble_server_extension e = assemble_ext @@ match e with | `Hostname -> ("", SERVER_NAME) | `ALPN protocol -> (assemble_alpn_protocols [protocol], APPLICATION_LAYER_PROTOCOL_NEGOTIATION) | `KeyShare (g, ks) -> let ng = group_to_named_group g in (assemble_keyshare_entry (ng, ks), KEY_SHARE) | `PreSharedKey id -> let data = Bytes.create 2 in Bytes.set_uint16_be data 0 id ; (Bytes.unsafe_to_string data, PRE_SHARED_KEY) | `SelectedVersion v -> (assemble_protocol_version v, SUPPORTED_VERSIONS) | x -> assemble_extension x let assemble_encrypted_extension e = assemble_ext @@ match e with | `Hostname -> ("", SERVER_NAME) | `ALPN protocol -> (assemble_alpn_protocols [protocol], APPLICATION_LAYER_PROTOCOL_NEGOTIATION) | `SupportedGroups groups -> (assemble_supported_groups (List.map group_to_named_group groups), SUPPORTED_GROUPS) | `EarlyDataIndication -> ("", EARLY_DATA) | _ -> invalid_arg "unknown extension" let assemble_retry_extension e = assemble_ext @@ match e with | `SelectedGroup g -> (assemble_group g, KEY_SHARE) | `Cookie c -> (assemble_cookie c, COOKIE) | `SelectedVersion v -> (assemble_protocol_version v, SUPPORTED_VERSIONS) | `UnknownExtension _ -> invalid_arg "unknown retry extension" let assemble_cert_ext (certificate, extensions) = let cert = assemble_certificate certificate and exts = assemble_list Two assemble_server_extension extensions in cert ^ exts let assemble_certs_exts cs = assemble_list Three assemble_cert_ext cs let assemble_certificates_1_3 context certs = let l = String.make 1 (Char.unsafe_chr (String.length context)) in String.concat "" [ l ; context ; assemble_certs_exts (List.map (fun c -> c, []) certs) ] let assemble_sid sid = match sid with | None -> String.make 1 '\x00' | Some s -> String.make 1 (Char.unsafe_chr (String.length s)) ^ s let assemble_client_hello (cl : client_hello) : string = let version = match cl.client_version with | `TLS_1_3 -> `TLS_1_2 (* keep 0x03 0x03 on wire *) | x -> x in let v = assemble_any_protocol_version version in let sid = assemble_sid cl.sessionid in let css = assemble_any_ciphersuites cl.ciphersuites in (* compression methods, completely useless *) let cms = assemble_compression_methods [NULL] in let bbuf = String.concat "" [ v ; cl.client_random ; sid ; css ; cms ] in let extensions = assemble_extensions ~none_if_empty:true assemble_client_extension cl.extensions in (* some widely deployed firewalls drop ClientHello messages which are > 256 and < 511 byte, insert PADDING extension for these *) (* from draft-ietf-tls-padding-00: As an example, consider a client that wishes to avoid sending a ClientHello with a record size between 256 and 511 bytes (inclusive). This case is considered because at least one TLS implementation is known to hang the connection when such a ClientHello record is received. After building a ClientHello as normal, the client can add four to the length (to account for the "msg_type" and "length" fields of the handshake protocol) and test whether the resulting length falls into that range. If it does, a padding extension can be added in order to push the length to (at least) 512 bytes. *) let extrapadding = (* since PreSharedKeys _must_ be the last extension, don't bother padding when it is present. rationale from ietf-tls WG "Padding extension and 0-RTT" thread (2016-10-30) *) if List.exists (function `PreSharedKeys _ -> true | _ -> false) cl.extensions then "" else let buflen = String.length bbuf + String.length extensions + 4 (* see above, header *) in if buflen >= 256 && buflen <= 511 then match String.length extensions with | 0 -> (* need to construct a 2 byte extension length as well *) let l = 512 (* desired length *) - 2 (* extension length *) - 4 (* padding extension header *) - buflen in let l = max l 0 in (* negative size is not good *) let padding = assemble_client_extension (`Padding l) in let extension_length = Bytes.create 2 in Bytes.set_uint16_be extension_length 0 (String.length padding); Bytes.unsafe_to_string extension_length ^ padding | _ -> let l = 512 - 4 (* padding extension header *) - buflen in let l = max l 0 in let padding = assemble_client_extension (`Padding l) in (* extensions include the 16 bit extension length field *) let elen = String.length extensions + String.length padding - 2 (* the 16 bit length field *) in Bytes.set_uint16_be (Bytes.unsafe_of_string extensions) 0 elen; padding else "" in String.concat "" [ bbuf ; extensions ; extrapadding ] let assemble_server_hello (sh : server_hello) : string = let version, exts = match sh.server_version with | `TLS_1_3 -> `TLS_1_2, `SelectedVersion `TLS_1_3 :: sh.extensions | x -> x, sh.extensions in let v = assemble_protocol_version version in let sid = assemble_sid sh.sessionid in let cs = assemble_ciphersuite sh.ciphersuite in (* useless compression method *) let cm = assemble_compression_method NULL in let extensions = assemble_extensions ~none_if_empty:true assemble_server_extension exts in String.concat "" [ v ; sh.server_random ; sid ; cs ; cm ; extensions ] let assemble_dh_parameters p = let plen, glen, yslen = (String.length p.dh_p, String.length p.dh_g, String.length p.dh_Ys) in let buf = Bytes.create (2 + 2 + 2 + plen + glen + yslen) in Bytes.set_uint16_be buf 0 plen; Bytes.blit_string p.dh_p 0 buf 2 plen; Bytes.set_uint16_be buf (2 + plen) glen; Bytes.blit_string p.dh_g 0 buf (4 + plen) glen; Bytes.set_uint16_be buf (4 + plen + glen) yslen; Bytes.blit_string p.dh_Ys 0 buf (6 + plen + glen) yslen; Bytes.unsafe_to_string buf let assemble_ec_parameters named_curve point = let hdr = Bytes.create 4 in Bytes.set_uint8 hdr 0 (ec_curve_type_to_int NAMED_CURVE); Bytes.set_uint16_be hdr 1 (named_group_to_int (group_to_named_group named_curve)); Bytes.set_uint8 hdr 3 (String.length point); Bytes.unsafe_to_string hdr ^ point let assemble_digitally_signed signature = let lenbuf = Bytes.create 2 in Bytes.set_uint16_be lenbuf 0 (String.length signature); Bytes.unsafe_to_string lenbuf ^ signature let assemble_digitally_signed_1_2 sigalg signature = (assemble_hash_signature sigalg) ^ (assemble_digitally_signed signature) let assemble_session_ticket_extension e = assemble_ext @@ match e with | `EarlyDataIndication max -> let buf = Bytes.create 4 in Bytes.set_int32_be buf 0 max ; (Bytes.unsafe_to_string buf, EARLY_DATA) | _ -> invalid_arg "unknown extension" let assemble_session_ticket (se : session_ticket) = let buf = Bytes.create 9 in Bytes.set_int32_be buf 0 se.lifetime ; Bytes.set_int32_be buf 4 se.age_add ; Bytes.set_uint8 buf 8 (String.length se.nonce) ; let ticketlen = Bytes.create 2 in Bytes.set_uint16_be ticketlen 0 (String.length se.ticket) ; let exts = assemble_extensions assemble_session_ticket_extension se.extensions in String.concat "" [ Bytes.unsafe_to_string buf ; se.nonce ; Bytes.unsafe_to_string ticketlen ; se.ticket ; exts ] let assemble_client_dh_key_exchange kex = let len = String.length kex in let buf = Bytes.create (len + 2) in Bytes.set_uint16_be buf 0 len; Bytes.blit_string kex 0 buf 2 len; Bytes.unsafe_to_string buf let assemble_client_ec_key_exchange kex = let len = String.length kex in let buf = Bytes.create (len + 1) in Bytes.set_uint8 buf 0 len; Bytes.blit_string kex 0 buf 1 len; Bytes.unsafe_to_string buf let assemble_hello_retry_request hrr = let exts = `SelectedGroup hrr.selected_group :: hrr.extensions in let version, exts = match hrr.retry_version with | `TLS_1_3 -> `TLS_1_2, `SelectedVersion `TLS_1_3 :: exts | x -> x, exts in let v = assemble_protocol_version version in let sid = assemble_sid hrr.sessionid in let cs = assemble_ciphersuite (hrr.ciphersuite :> Ciphersuite.ciphersuite) in (* useless compression method *) let cm = String.make 1 '\x00' in let extensions = assemble_extensions ~none_if_empty:true assemble_retry_extension exts in String.concat "" [ v ; helloretryrequest ; sid ; cs ; cm ; extensions ] let assemble_hs typ len = let buf = Bytes.create 4 in Bytes.set_uint8 buf 0 (handshake_type_to_int typ); set_uint24_len ~off:1 buf len; Bytes.unsafe_to_string buf let assemble_message_hash len = assemble_hs MESSAGE_HASH len let assemble_key_update req = String.make 1 (Char.unsafe_chr (key_update_request_type_to_int req)) let assemble_handshake hs = let (payload, payload_type) = match hs with | ClientHello ch -> (assemble_client_hello ch, CLIENT_HELLO) | ServerHello sh -> (assemble_server_hello sh, SERVER_HELLO) | HelloRetryRequest hr -> (assemble_hello_retry_request hr, SERVER_HELLO) | Certificate cs -> (cs, CERTIFICATE) | CertificateRequest cr -> (cr, CERTIFICATE_REQUEST) | CertificateVerify c -> (c, CERTIFICATE_VERIFY) | ServerKeyExchange kex -> (kex, SERVER_KEY_EXCHANGE) | ClientKeyExchange kex -> (kex, CLIENT_KEY_EXCHANGE) | ServerHelloDone -> ("", SERVER_HELLO_DONE) | HelloRequest -> ("", HELLO_REQUEST) | Finished fs -> (fs, FINISHED) | SessionTicket st -> (assemble_session_ticket st, SESSION_TICKET) | EncryptedExtensions ee -> let cs = assemble_extensions assemble_encrypted_extension ee in (cs, ENCRYPTED_EXTENSIONS) | KeyUpdate req -> let cs = assemble_key_update req in (cs, KEY_UPDATE) | EndOfEarlyData -> ("", END_OF_EARLY_DATA) in let pay_len = String.length payload in let buf = assemble_hs payload_type pay_len in buf ^ payload let assemble_alert ?(level = Packet.FATAL) typ = let buf = Bytes.create 2 in Bytes.set_uint8 buf 1 (alert_type_to_int typ); Bytes.set_uint8 buf 0 (alert_level_to_int level) ; Bytes.unsafe_to_string buf let assemble_change_cipher_spec = String.make 1 '\x01'