package tls

  1. Overview
  2. Docs

Source file crypto.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
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)
     - <length in 16 bit>
  *)
  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
OCaml

Innovation. Community. Security.