open Mirage_crypto open Ciphersuite (* on-the-wire dh_params <-> (group, pub_message) *) let dh_params_pack { Mirage_crypto_pk.Dh.p; gg ; _ } message = let cs_of_z = Mirage_crypto_pk.Z_extra.to_octets_be ?size:None in { Core.dh_p = cs_of_z p ; dh_g = cs_of_z gg ; dh_Ys = message } and dh_params_unpack { Core.dh_p ; dh_g ; dh_Ys } = let z_of_cs = Mirage_crypto_pk.Z_extra.of_octets_be ?bits:None in match Mirage_crypto_pk.Dh.group ~p:(z_of_cs dh_p) ~gg:(z_of_cs dh_g) () with | Ok dh -> Ok (dh, dh_Ys) | Error _ as e -> e module Ciphers = struct (* I'm not sure how to get rid of this type, but would welcome a solution *) (* only used as result of get_block, which is called by get_cipher below *) type keyed = | K_CBC : 'k State.cbc_cipher * (string -> 'k) -> keyed let get_block = function | TRIPLE_DES_EDE_CBC -> K_CBC ( (module DES.CBC : Block.CBC with type key = DES.CBC.key), DES.CBC.of_secret ) | AES_128_CBC -> K_CBC ( (module AES.CBC : Block.CBC with type key = AES.CBC.key), AES.CBC.of_secret ) | AES_256_CBC -> K_CBC ( (module AES.CBC : Block.CBC with type key = AES.CBC.key), AES.CBC.of_secret ) type aead_keyed = | K_AEAD : 'k State.aead_cipher * (string -> 'k) * bool -> aead_keyed let get_aead = function | AES_128_CCM | AES_256_CCM -> K_AEAD ((module AES.CCM16 : AEAD with type key = AES.CCM16.key), AES.CCM16.of_secret, true) | AES_128_GCM | AES_256_GCM -> K_AEAD ((module AES.GCM : AEAD with type key = AES.GCM.key), AES.GCM.of_secret, true) | CHACHA20_POLY1305 -> K_AEAD ((module Chacha20 : AEAD with type key = Chacha20.key), Chacha20.of_secret, false) let get_aead_cipher ~secret ~nonce aead_cipher = match get_aead aead_cipher with | K_AEAD (cipher, sec, explicit_nonce) -> let cipher_secret = sec secret in State.(AEAD { cipher ; cipher_secret ; nonce ; explicit_nonce }) let get_cipher ~secret ~hmac_secret ~iv_mode ~nonce = function | `Block (cipher, hmac) -> ( match get_block cipher with | K_CBC (cipher, sec) -> let cipher_secret = sec secret in State.(CBC { cipher ; cipher_secret ; iv_mode ; hmac ; hmac_secret }) ) | `AEAD cipher -> get_aead_cipher ~secret ~nonce cipher end let sequence_buf seq = let buf = Bytes.create 8 in Bytes.set_int64_be buf 0 seq ; Bytes.unsafe_to_string buf let aead_nonce nonce seq = let s = let l = String.length nonce in let buf = Bytes.make l '\x00' in Bytes.set_int64_be buf (l - 8) seq; Bytes.unsafe_to_string buf in Uncommon.xor nonce s let adata_1_3 len = (* additional data in TLS 1.3 is using the header (RFC 8446 Section 5.2): - APPLICATION_TYPE - 0x03 0x03 (for TLS version 1.2 -- binary representation is 0x03 0x03) - *) let buf = Bytes.create 5 in Bytes.set_uint8 buf 0 (Packet.content_type_to_int Packet.APPLICATION_DATA) ; Bytes.set_uint8 buf 1 3; Bytes.set_uint8 buf 2 3; Bytes.set_uint16_be buf 3 len ; Bytes.unsafe_to_string buf let pseudo_header seq ty (v_major, v_minor) v_length = let buf = Bytes.create 13 in Bytes.set_int64_be buf 0 seq; Bytes.set_uint8 buf 8 (Packet.content_type_to_int ty); Bytes.set_uint8 buf 9 v_major; Bytes.set_uint8 buf 10 v_minor; Bytes.set_uint16_be buf 11 v_length; Bytes.unsafe_to_string buf (* MAC used in TLS *) let mac hash key pseudo_hdr data = let module H = (val Digestif.module_of_hash' hash) in H.(to_raw_string (hmacv_string ~key [ pseudo_hdr ; data ])) let cbc_block (type a) cipher = let module C = (val cipher : Block.CBC with type key = a) in C.block_size (* crazy CBC padding and unpadding for TLS *) let cbc_pad block data = (* 1 is the padding length, encoded as 8 bit at the end of the fragment *) let len = 1 + String.length data in (* we might want to add additional blocks of padding *) let padding_length = block - (len mod block) in (* 1 is again padding length field *) let cstruct_len = padding_length + 1 in String.make cstruct_len (Char.unsafe_chr padding_length) let cbc_unpad data = let len = String.length data in let padlen = String.get_uint8 data (pred len) in let rec check = function | i when i > padlen -> true | i -> (String.get_uint8 data (len - padlen - 1 + i) = padlen) && check (succ i) in try if check 0 then Some (String.sub data 0 (len - padlen - 1)) else None with Invalid_argument _ -> None let tag_len (type a) cipher = let module C = (val cipher : AEAD with type key = a) in C.tag_size let encrypt_aead (type a) ~cipher ~key ~nonce ?adata data = let module C = (val cipher : AEAD with type key = a) in C.authenticate_encrypt ~key ~nonce ?adata data let decrypt_aead (type a) ~cipher ~key ~nonce ?adata data = let module C = (val cipher : AEAD with type key = a) in C.authenticate_decrypt ~key ~nonce ?adata data let encrypt_cbc (type a) ~cipher ~key ~iv data = let module C = (val cipher : Block.CBC with type key = a) in let message = C.encrypt ~key ~iv (data ^ cbc_pad C.block_size data) in (message, C.next_iv ~iv message) let decrypt_cbc (type a) ~cipher ~key ~iv data = let module C = (val cipher : Block.CBC with type key = a) in try let message = C.decrypt ~key ~iv data in match cbc_unpad message with | Some res -> Some (res, C.next_iv ~iv data) | None -> None with (* This bails out immediately on mis-alignment, making it very timeable. * However, decryption belongs to the outermost level and this operation's * timing does not leak information ala padding oracle and friends. *) | Invalid_argument _ -> None