open OUnit2

open Mirage_crypto

open Test_common

let des_ecb_cases =
  let case ~data ~key ~out = vx data, DES.ECB.of_secret (vx key), vx out

  and check (data, key, out) _ =
    let enc = DES.ECB.encrypt ~key data in
    let dec = DES.ECB.decrypt ~key enc in
    assert_oct_equal ~msg:"ciphertext" out enc ;
    assert_oct_equal ~msg:"plaintext" data dec in

  cases_of check [
    case
      ~data:"3f87 9123 0058 8d88  e784 d52a 5d0f 2038
             f523 6889 bbce ce1f  a7bf 7aa8 6fcc 8245
             0576 2144 8f11 94d7  07bc 1bba 9b92 5e45
             3190 c42b 758f 3d91  f68e ebbb ce62 b8e7"
      ~key: "3f47 f79c c120 7188  4700 217e fd88 bbe4 6f51 27fb 7340 81e5"
      ~out: "b43b 3ae3 d765 b299  06ea 7c35 ceeb 9e52
             946c 06e7 0d50 193e  5a22 1ff0 afe9 abe0
             3b82 ce7d c42a 465d  0f19 45f0 5382 7006
             b4cd 21f0 5b0f 6843  de2a 67b6 9fb4 6a8f"
]

let des_cbc_cases =
  let case ~data ~key ~iv ~out = vx data, DES.CBC.of_secret (vx key), vx iv, vx out

  and check (data, key, iv, out) _ =
    let enc = DES.CBC.encrypt ~key ~iv data in
    let dec = DES.CBC.decrypt ~key ~iv enc in
    assert_oct_equal ~msg:"ciphertext" out enc ;
    assert_oct_equal ~msg:"plaintext" data dec in

  cases_of check [
    case
      ~data:
"8f8c 1e0a c8fb 1614  3cec ed1c 28ac fd6f
ae6d 3686 5365 511d  6707 68d9 7928 0479
cacd 6808 1540 d5fc  2971 2a8a c2b1 17c2
f0e6 a329 e190 44ff  54e7 5eec 8296 6a58"
      ~iv:"b219 ef93 4c37 aadf"
      ~key:"7ecd 2240 a2ac a10a  e713 f467 7ea5 d327  e04c cfe0 5cb4 bb09"
~out:
"3110 3904 faa1 4ef4  e404 d3d0 f2ee ae58
5fe9 e6b7 9552 b4f4  3608 03ca 395a f6e9
2330 69d6 2c6f a52a  d083 faab 3306 b794
89f6 6671 e3dd 3368  0b13 f8d9 7136 9674"
  ]

let des_ctr_cases =
  let case ~data ~key ~ctr ~out = test_case @@ fun _ ->
    let open DES.CTR in
    let key  = vx key |> of_secret
    and ctr  = vx ctr |> ctr_of_octets
    and out  = vx out
    and data = vx data in
    let enc = encrypt ~key ~ctr data in
    let dec = decrypt ~key ~ctr enc in
    assert_oct_equal ~msg:"cipher" out enc;
    assert_oct_equal ~msg:"plain" data dec
  in
  [ case
      ~data:
"e9ee ce61 7b75 4c70  79f3 3e5b 036a 7d5b
4bee f693 0eb3 fa50  9fe3 61d8 713a a487
a692 21b0 8627 5e6f  d021 4030 7c58 507a
5fea ca64 d17d a493  7337 8c17 ae05 f3c4
c6dc 15cc 49c4 3ab0  dab3 9c9b e964 a3c8
5865 7bb8 6e4d 8507  3866 b805 02c2 4970
dbbd 3554 20b1 76b2  ee6c 98b3 f7ce 9035
1e5f 880e"
~key:"76b9 d4ff d52f 5024  6d24 a3e1 4ebd e605  b82c d81f 0c07 2da1"
~ctr:"6318 a132 cafd aac0"
~out:
"b8d8 aeec d583 009c  f042 ec4d 7ddf c5e5
386f 89e6 d975 02bc  7583 e113 4899 dabc
bd93 871b 774b e5ce  4e12 6778 f208 0c53
52cb a3ac 7567 cdb9  ae81 fc46 25d4 7f9d
6f3f fbec 4512 8845  3739 1014 2b39 d293
845a 8505 91a6 f644  5168 bf00 ca4d 4603
6e5f 418f c43f fabd  272e 1009 c69b 2a6b
7d2c edb2"

  ]


(* NIST SP 800-38A test vectors for block cipher modes of operation *)

let nist_sp_800_38a = vx
  "6b c1 be e2 2e 40 9f 96 e9 3d 7e 11 73 93 17 2a
   ae 2d 8a 57 1e 03 ac 9c 9e b7 6f ac 45 af 8e 51
   30 c8 1c 46 a3 5c e4 11 e5 fb c1 19 1a 0a 52 ef
   f6 9f 24 45 df 4f 9b 17 ad 2b 41 7b e6 6c 37 10"

let aes_ecb_cases =
  let case ~key ~out = (AES.ECB.of_secret (vx key), vx out)

  and check (key, out) _ =
    let enc = AES.ECB.encrypt ~key nist_sp_800_38a in
    let dec = AES.ECB.decrypt ~key enc in
    assert_oct_equal ~msg:"ciphertext" out enc ;
    assert_oct_equal ~msg:"plaintext" nist_sp_800_38a dec in

  cases_of check [
    case ~key: "2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c"
         ~out: "3a d7 7b b4 0d 7a 36 60 a8 9e ca f3 24 66 ef 97
                f5 d3 d5 85 03 b9 69 9d e7 85 89 5a 96 fd ba af
                43 b1 cd 7f 59 8e ce 23 88 1b 00 e3 ed 03 06 88
                7b 0c 78 5e 27 e8 ad 3f 82 23 20 71 04 72 5d d4"

  ; case ~key: "8e 73 b0 f7 da 0e 64 52 c8 10 f3 2b 80 90 79 e5
                62 f8 ea d2 52 2c 6b 7b"
         ~out: "bd 33 4f 1d 6e 45 f2 5f f7 12 a2 14 57 1f a5 cc
                97 41 04 84 6d 0a d3 ad 77 34 ec b3 ec ee 4e ef
                ef 7a fd 22 70 e2 e6 0a dc e0 ba 2f ac e6 44 4e
                9a 4b 41 ba 73 8d 6c 72 fb 16 69 16 03 c1 8e 0e"

  ; case ~key: "60 3d eb 10 15 ca 71 be 2b 73 ae f0 85 7d 77 81
                1f 35 2c 07 3b 61 08 d7 2d 98 10 a3 09 14 df f4"
         ~out: "f3 ee d1 bd b5 d2 a0 3c 06 4b 5a 7e 3d b1 81 f8
                59 1c cb 10 d4 10 ed 26 dc 5b a7 4a 31 36 28 70
                b6 ed 21 b9 9c a6 f4 f9 f1 53 e7 b1 be af ed 1d
                23 30 4b 7a 39 f9 f3 ff 06 7d 8d 8f 9e 24 ec c7"
  ]

let aes_cbc_cases =
  let case ~key ~iv ~out = (AES.CBC.of_secret (vx key), vx iv, vx out)

  and check (key, iv, out) _ =
    let enc = AES.CBC.encrypt ~key ~iv nist_sp_800_38a in
    let dec = AES.CBC.decrypt ~key ~iv enc in
    assert_oct_equal ~msg:"ciphertext" out enc ;
    assert_oct_equal ~msg:"plaintext" nist_sp_800_38a dec in

  cases_of check [
    case ~key: "2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c"
         ~iv:  "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"
         ~out: "76 49 ab ac 81 19 b2 46 ce e9 8e 9b 12 e9 19 7d
                50 86 cb 9b 50 72 19 ee 95 db 11 3a 91 76 78 b2
                73 be d6 b8 e3 c1 74 3b 71 16 e6 9e 22 22 95 16
                3f f1 ca a1 68 1f ac 09 12 0e ca 30 75 86 e1 a7"

  ; case ~key: "8e 73 b0 f7 da 0e 64 52 c8 10 f3 2b 80 90 79 e5
                62 f8 ea d2 52 2c 6b 7b"
         ~iv:  "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"
         ~out: "4f 02 1d b2 43 bc 63 3d 71 78 18 3a 9f a0 71 e8
                b4 d9 ad a9 ad 7d ed f4 e5 e7 38 76 3f 69 14 5a
                57 1b 24 20 12 fb 7a e0 7f a9 ba ac 3d f1 02 e0
                08 b0 e2 79 88 59 88 81 d9 20 a9 e6 4f 56 15 cd"

  ; case ~key: "60 3d eb 10 15 ca 71 be 2b 73 ae f0 85 7d 77 81
                1f 35 2c 07 3b 61 08 d7 2d 98 10 a3 09 14 df f4"
         ~iv:  "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"
         ~out: "f5 8c 4c 04 d6 e5 f1 ba 77 9e ab fb 5f 7b fb d6
                9c fc 4e 96 7e db 80 8d 67 9f 77 7b c6 70 2c 7d
                39 f2 33 69 a9 d9 ba cf a5 30 e2 63 04 23 14 61
                b2 eb 05 e2 c3 9b e9 fc da 6c 19 07 8c 6a 9d 1b"
  ]

let aes_ctr_cases =
  let case ~key ~ctr ~out ~ctr1 = test_case @@ fun _ ->
    let open AES.CTR in
    let key  = vx key |> of_secret
    and ctr  = vx ctr |> ctr_of_octets
    and ctr1 = vx ctr1 |> ctr_of_octets
    and out  = vx out in
    let enc = encrypt ~key ~ctr nist_sp_800_38a in
    let dec = decrypt ~key ~ctr enc in
    assert_oct_equal ~msg:"cipher" out enc;
    assert_oct_equal ~msg:"plain" nist_sp_800_38a dec;
    let blocks = String.length nist_sp_800_38a / block_size in
    assert_equal ~msg:"counters" ctr1 (add_ctr ctr (Int64.of_int blocks))
  in
  [ case ~key:  "2b7e1516 28aed2a6 abf71588 09cf4f3c"
         ~ctr:  "f0f1f2f3 f4f5f6f7 f8f9fafb fcfdfeff"
         ~out:  "874d6191 b620e326 1bef6864 990db6ce
                 9806f66b 7970fdff 8617187b b9fffdff
                 5ae4df3e dbd5d35e 5b4f0902 0db03eab
                 1e031dda 2fbe03d1 792170a0 f3009cee"
         ~ctr1: "f0f1f2f3 f4f5f6f7 f8f9fafb fcfdff03"

  ; case ~key:  "8e73b0f7 da0e6452 c810f32b 809079e5
                 62f8ead2 522c6b7b"
         ~ctr:  "f0f1f2f3 f4f5f6f7 f8f9fafb fcfdfeff"
         ~out:  "1abc9324 17521ca2 4f2b0459 fe7e6e0b
                 090339ec 0aa6faef d5ccc2c6 f4ce8e94
                 1e36b26b d1ebc670 d1bd1d66 5620abf7
                 4f78a7f6 d2980958 5a97daec 58c6b050"
         ~ctr1: "f0f1f2f3 f4f5f6f7 f8f9fafb fcfdff03"

  ; case ~key:  "603deb10 15ca71be 2b73aef0 857d7781
                 1f352c07 3b6108d7 2d9810a3 0914dff4"
         ~ctr:  "f0f1f2f3 f4f5f6f7 f8f9fafb fcfdfeff"
         ~out:  "601ec313 775789a5 b7a7f504 bbf3d228
                 f443e3ca 4d62b59a ca84e990 cacaf5c5
                 2b0930da a23de94c e87017ba 2d84988d
                 dfc9c58d b67aada6 13c2dd08 457941a6"
         ~ctr1: "f0f1f2f3 f4f5f6f7 f8f9fafb fcfdff03"

  ; case ~key:  "00010203 04050607 08090a0b 0c0d0e0f" (* ctr rollover *)
         ~ctr:  "00000000 00000000 ffffffff fffffffe"
         ~out:  "5d0a5645 378f579a 988ff186 d42eaa2f
                 978a655d 145bfe34 21656c8f 01101a43
                 23d0862c 47f7e3bf 95586ba4 2ab4cb31
                 790b0d01 93c0d022 3469534e 537ce82d"
         ~ctr1: "00000000 00000001 00000000 00000002"
  ]

(* aes gcm *)

let gcm_cases =
  let case ~key ~p ~a ~nonce ~c ~t =
    (AES.GCM.of_secret (vx key), vx p, vx a, vx nonce, vx c, vx t) in

  let check (key, p, adata, nonce, c, t) _ =
    let cipher = AES.GCM.authenticate_encrypt ~key ~nonce ~adata p in
    let pdata =
      match AES.GCM.authenticate_decrypt ~key ~nonce ~adata cipher with
      | None -> assert_failure "GCM decryption broken"
      | Some data -> data
    in
    assert_oct_equal ~msg:"ciphertext" (c ^ t) cipher ;
    assert_oct_equal ~msg:"decrypted plaintext" p pdata
  in

  cases_of check [

    case ~key: "00000000000000000000000000000000"
         ~p:   ""
         ~a:   ""
         ~nonce: "000000000000000000000000"
         ~c:   ""
         ~t:   "58e2fccefa7e3061367f1d57a4e7455a" ;
    case ~key: "00000000000000000000000000000000"
         ~p:   "00000000000000000000000000000000"
         ~a:   ""
         ~nonce: "000000000000000000000000"
         ~c:   "0388dace60b6a392f328c2b971b2fe78"
         ~t:   "ab6e47d42cec13bdf53a67b21257bddf" ;
    case ~key: "feffe9928665731c6d6a8f9467308308"
         ~p:   "d9313225f88406e5a55909c5aff5269a
                86a7a9531534f7da2e4c303d8a318a72
                1c3c0c95956809532fcf0e2449a6b525
                b16aedf5aa0de657ba637b391aafd255"
         ~a:   ""
         ~nonce: "cafebabefacedbaddecaf888"
         ~c:   "42831ec2217774244b7221b784d0d49c
                e3aa212f2c02a4e035c17e2329aca12e
                21d514b25466931c7d8f6a5aac84aa05
                1ba30b396a0aac973d58e091473f5985"
         ~t:   "4d5c2af327cd64a62cf35abd2ba6fab4" ;
    case ~key: "feffe9928665731c6d6a8f9467308308"
         ~p:   "d9313225f88406e5a55909c5aff5269a
                86a7a9531534f7da2e4c303d8a318a72
                1c3c0c95956809532fcf0e2449a6b525
                b16aedf5aa0de657ba637b39"
         ~a:   "feedfacedeadbeeffeedfacedeadbeef
                abaddad2"
         ~nonce: "cafebabefacedbaddecaf888"
         ~c:   "42831ec2217774244b7221b784d0d49c
                e3aa212f2c02a4e035c17e2329aca12e
                21d514b25466931c7d8f6a5aac84aa05
                1ba30b396a0aac973d58e091"
         ~t:   "5bc94fbc3221a5db94fae95ae7121a47" ;
    case ~key: "feffe9928665731c6d6a8f9467308308"
         ~p:   "d9313225f88406e5a55909c5aff5269a
                86a7a9531534f7da2e4c303d8a318a72
                1c3c0c95956809532fcf0e2449a6b525
                b16aedf5aa0de657ba637b39"
         ~a:   "feedfacedeadbeeffeedfacedeadbeef
                abaddad2"
         ~nonce: "cafebabefacedbad"
         ~c:   "61353b4c2806934a777ff51fa22a4755
                699b2a714fcdc6f83766e5f97b6c7423
                73806900e49f24b22b097544d4896b42
                4989b5e1ebac0f07c23f4598"
         ~t:   "3612d2e79e3b0785561be14aaca2fccb" ;
    case ~key: "feffe9928665731c6d6a8f9467308308"
         ~p:   "d9313225f88406e5a55909c5aff5269a
                86a7a9531534f7da2e4c303d8a318a72
                1c3c0c95956809532fcf0e2449a6b525
                b16aedf5aa0de657ba637b39"
         ~a:   "feedfacedeadbeeffeedfacedeadbeef
                abaddad2"
         ~nonce: "9313225df88406e555909c5aff5269aa
                6a7a9538534f7da1e4c303d2a318a728
                c3c0c95156809539fcf0e2429a6b5254
                16aedbf5a0de6a57a637b39b"
         ~c:   "8ce24998625615b603a033aca13fb894
                be9112a5c3a211a8ba262a3cca7e2ca7
                01e4a9a4fba43c90ccdcb281d48c7c6f
                d62875d2aca417034c34aee5"
         ~t:   "619cc5aefffe0bfa462af43c1699d050" ;
    case ~key: "feffe9928665731c6d6a8f9467308308
                feffe9928665731c"
         ~p:   "d9313225f88406e5a55909c5aff5269a
                86a7a9531534f7da2e4c303d8a318a72
                1c3c0c95956809532fcf0e2449a6b525
                b16aedf5aa0de657ba637b39"
         ~a:   "feedfacedeadbeeffeedfacedeadbeef
                abaddad2"
         ~nonce: "cafebabefacedbaddecaf888"
         ~c:   "3980ca0b3c00e841eb06fac4872a2757
                859e1ceaa6efd984628593b40ca1e19c
                7d773d00c144c525ac619d18c84a3f47
                18e2448b2fe324d9ccda2710"
         ~t:   "2519498e80f1478f37ba55bd6d27618c" ;
    case ~key: "feffe9928665731c6d6a8f9467308308
                feffe9928665731c6d6a8f9467308308"
         ~p:   "d9313225f88406e5a55909c5aff5269a
                86a7a9531534f7da2e4c303d8a318a72
                1c3c0c95956809532fcf0e2449a6b525
                b16aedf5aa0de657ba637b39"
         ~a:   "feedfacedeadbeeffeedfacedeadbeef
                abaddad2"
         ~nonce: "9313225df88406e555909c5aff5269aa
                6a7a9538534f7da1e4c303d2a318a728
                c3c0c95156809539fcf0e2429a6b5254
                16aedbf5a0de6a57a637b39b"
         ~c:   "5a8def2f0c9e53f1f75d7853659e2a20
                eeb2b22aafde6419a058ab4f6f746bf4
                0fc0c3b780f244452da3ebf1c5d82cde
                a2418997200ef82e44ae7e3f"
         ~t:   "a44a8266ee1c8eb0c8b5d4cf5ae9f19a";
    case ~key: "00000000000000000000000000000000"  (* large GHASH batch *)
         ~p:   ""
         ~a:   "f0f0f0f0f0f0f0f00f0f0f0f0f0f0f0f
                e0e0e0e0e0e0e0e00e0e0e0e0e0e0e0e
                d0d0d0d0d0d0d0d00d0d0d0d0d0d0d0d
                c0c0c0c0c0c0c0c00c0c0c0c0c0c0c0c
                b0b0b0b0b0b0b0b00b0b0b0b0b0b0b0b
                a0a0a0a0a0a0a0a00a0a0a0a0a0a0a0a
                90909090909090900909090909090909
                80808080808080800808080808080808
                70707070707070700707070707070707
                60606060606060600606060606060606
                50505050505050500505050505050505
                40404040404040400404040404040404
                30303030303030300303030303030303
                20202020202020200202020202020202
                10101010101010100101010101010101
                00000000000000000000000000000000
                ff"
         ~nonce: "000000000000000000000000"
         ~c:   ""
         ~t:   "9bfdb8fdac1be65739780c41703c0fb6";
    case ~key: "00000000000000000000000000000002"  (* ctr rollover *)
         ~nonce: "3222415d"
         ~p:   "deadbeefdeadbeefdeadbeefdeadbeef
                deadbeefdeadbeefdeadbeefdeadbeef
                deadbeef"
         ~a:   ""
         ~c:   "42627ce3de61b5c105c7f01629c031c1
                b890bb273b6b6bc26b56c801f87fa95c
                a8b37503"
         ~t:   "3631cbe44782713b93b1c7d93c3c8638"
]


(*
(* from SP800-38C_updated-July20_2007.pdf appendix C *)
let ccm_cases =
  let open Cipher_block.AES.CCM in
  let case ~key ~p ~a ~nonce ~c ~maclen =
    (of_secret ~maclen (vx key), vx p, vx a, vx nonce, vx c) in

  let check (key, p, adata, nonce, c) _ =
    let cip = authenticate_encrypt ~key ~nonce ~adata p in
    assert_oct_equal ~msg:"encrypt" c cip ;
    match authenticate_decrypt ~key ~nonce ~adata c with
      | Some x -> assert_oct_equal ~msg:"decrypt" p x
      | None -> assert_failure "CCM decryption broken"
  in

  cases_of check [

    case ~key:    "404142434445464748494a4b4c4d4e4f"
         ~p:      "20212223"
         ~a:      "0001020304050607"
         ~nonce:  "10111213141516"
         ~c:      "7162015b4dac255d"
         ~maclen: 4
    ;
    case ~key:    "40414243 44454647 48494a4b 4c4d4e4f"
         ~p:      "20212223 24252627 28292a2b 2c2d2e2f"
         ~a:      "00010203 04050607 08090a0b 0c0d0e0f"
         ~nonce:  "10111213 14151617"
         ~c:      "d2a1f0e0 51ea5f62 081a7792 073d593d 1fc64fbf accd"
         ~maclen: 6
    ;
    case ~key:    "404142434445464748494a4b4c4d4e4f"
         ~p:      "202122232425262728292a2b2c2d2e2f3031323334353637"
         ~a:      "000102030405060708090a0b0c0d0e0f10111213"
         ~nonce:  "101112131415161718191a1b"
         ~c:      "e3b201a9f5b71a7a9b1ceaeccd97e70b6176aad9a4428aa5484392fbc1b09951"
         ~maclen: 8
  ]
*)

let ccm_regressions =
  let open AES.CCM16 in
  let no_vs_empty_ad _ =
    (* as reported in https://github.com/mirleft/ocaml-nocrypto/issues/166 *)
    (* see RFC 3610 Section 2.1, AD of length 0 should be same as no AD *)
    let key = of_secret (vx "000102030405060708090a0b0c0d0e0f")
    and nonce = vx "0001020304050607"
    and plaintext = "hello"
    in
    assert_oct_equal ~msg:"CCM no vs empty ad"
      (authenticate_encrypt ~key ~nonce plaintext)
      (authenticate_encrypt ~adata:"" ~key ~nonce plaintext)
  and short_nonce_enc _ =
    (* as reported in https://github.com/mirleft/ocaml-nocrypto/issues/167 *)
    (* valid nonce sizes for CCM are 7..13 (L can be 2..8, nonce is 15 - L)*)
    (* see RFC3610 Section 2.1 *)
    let key = of_secret (vx "000102030405060708090a0b0c0d0e0f")
    and nonce = ""
    and plaintext = "hello"
    in
    assert_raises ~msg:"CCM with short nonce raises"
      (Invalid_argument "Mirage_crypto: CCM: nonce length not between 7 and 13: 0")
      (fun () -> authenticate_encrypt ~key ~nonce plaintext)
  and short_nonce_enc2 _ =
    let key = of_secret (vx "000102030405060708090a0b0c0d0e0f")
    and nonce = vx "00"
    and plaintext = "hello"
    in
    assert_raises ~msg:"CCM with short nonce raises"
      (Invalid_argument "Mirage_crypto: CCM: nonce length not between 7 and 13: 1")
      (fun () -> authenticate_encrypt ~key ~nonce plaintext)
  and short_nonce_enc3 _ =
    let key = of_secret (vx "000102030405060708090a0b0c0d0e0f")
    and nonce = vx "000102030405"
    and plaintext = "hello"
    in
    assert_raises ~msg:"CCM with short nonce raises"
      (Invalid_argument "Mirage_crypto: CCM: nonce length not between 7 and 13: 6")
      (fun () -> authenticate_encrypt ~key ~nonce plaintext)
  and long_nonce_enc _ =
    let key = of_secret (vx "000102030405060708090a0b0c0d0e0f")
    and nonce = vx "000102030405060708090a0b0c0d"
    and plaintext = "hello"
    in
    assert_raises ~msg:"CCM with short nonce raises"
      (Invalid_argument "Mirage_crypto: CCM: nonce length not between 7 and 13: 14")
      (fun () -> authenticate_encrypt ~key ~nonce plaintext)
  and enc_dec_empty_message _ =
    (* as reported in https://github.com/mirleft/ocaml-nocrypto/issues/168 *)
    let key = of_secret (vx "000102030405060708090a0b0c0d0e0f")
    and nonce = vx "0001020304050607"
    and adata = "hello"
    and p = ""
    in
    let cipher = authenticate_encrypt ~adata ~key ~nonce p in
    match authenticate_decrypt ~key ~nonce ~adata cipher with
    | Some x -> assert_oct_equal ~msg:"CCM decrypt of empty message" p x
    | None -> assert_failure "decryption broken"
  and long_adata _ =
    let key = of_secret (vx "000102030405060708090a0b0c0d0e0f")
    and nonce = vx "0001020304050607"
    and plaintext = "hello"
    (* [adata] is greater than [1 lsl 16 - 1 lsl 8] *)
    and adata = String.make 65280 '\x00'
    and expected = vx "6592169e946f98973bc06d080f7c9dbb493a536f8a"
    in
    let cipher = authenticate_encrypt ~adata ~key ~nonce plaintext in
    assert_oct_equal ~msg:"CCM encrypt of >=65280 adata" expected cipher
  in
  let regr_tls =
    let key = of_secret (vx "063a 96fd 15f9 82d5  5aad 5bf9 d098 7546") in
    (* discovered while moving ocaml-tls to string *)
    let nonce = vx "81cd 4758 1880 9de0  c655 7c31"
    and adata = vx "1703 0300 17"
    and data = vx "0800 0002 0000 16"
    and expected = vx "94ca 065a c948 c5d6  92fd 5fab c850 0611  a07c 4f6e 0710 90"
    in
    let a _ =
      let cipher = authenticate_encrypt ~adata ~key ~nonce data in
      assert_oct_equal ~msg:"TLS regression 0" expected cipher
    and b _ =
      match authenticate_decrypt ~key ~nonce ~adata expected with
      | None -> assert_failure "TLS regression 0, decrypt broken"
      | Some x -> assert_oct_equal ~msg:"TLS regression 0 decrypt" x data
    in
    let nonce = vx "81cd 4758 1880 9de0  c655 7c30"
    and adata = vx "1703 0302 85"
    and data = vx {|
0b00 0270 0000 026c  0002 6730 8202 6330
8201 cc02 0900 cb6c  4e84 4b58 a1d4 300d
|}
    and expected = vx {|
1e59 904e e6d5 c2ac  e538 78d7 e24f 6e46
6169 f8e2 d3dd 8b5d  788c 78ff ea9f e1d0
9885 ac1a c6d9 fb88  b66a 3a11 5ba5 6e7c
        |}
    in
    let c _ =
      let cipher = authenticate_encrypt ~adata ~key ~nonce data in
      assert_oct_equal ~msg:"TLS regression 1" expected cipher
    and d _ =
      match authenticate_decrypt ~key ~nonce ~adata expected with
      | None -> assert_failure "TLS regression 1, decrypt broken"
      | Some x -> assert_oct_equal ~msg:"TLS regression 1 decrypt" x data
    in
    let data = vx {|
0b00 0270 0000 026c  0002 6730 8202 6330
8201 cc02 0900 cb6c  4e84 4b58 a1d4 300d
8201 cc02 0900
|}
    and expected = vx {|
1e59 904e e6d5 c2ac  e538 78d7 e24f 6e46
6169 f8e2 d3dd 8b5d  788c 78ff ea9f e1d0
7c8d 9993 6bfd cf76  9799 473b 58f4 ed69
d7a4 df7a 2d6b
      |}
    in
    let e _ =
      let cipher = authenticate_encrypt ~adata ~key ~nonce data in
      assert_oct_equal ~msg:"TLS regression 2" expected cipher
    and f _ =
      match authenticate_decrypt ~key ~nonce ~adata expected with
      | None -> assert_failure "TLS regression 2, decrypt broken"
      | Some x -> assert_oct_equal ~msg:"TLS regression 2 decrypt" x data
    in
    let data = vx {|
0b00 0270 0000 026c  0002 6730 8202 6330
8201 cc02 0900 cb6c  4e84 4b58 a1d4 300d
0609 2a86 4886 f70d  0101 0505 0030 7631
0b30 0906 0355 0406  1302 4155 3113 3011
0603 5504 080c 0a53  6f6d 652d 5374 6174
6531 2130 1f06 0355  040a 0c18 496e 7465
726e 6574 2057 6964  6769 7473 2050 7479
204c 7464 3115 3013  0603 5504 030c 0c59
4f55 5220 4e41 4d45  2121 2131 1830 1606
092a 8648 86f7 0d01  0901 1609 6d65 4062
6172 2e64 6530 1e17  0d31 3430 3231 3732
3230 3834 355a 170d  3135 3032 3137 3232
3038 3435 5a30 7631  0b30 0906 0355 0406
1302 4155 3113 3011  0603 5504 080c 0a53
6f6d 652d 5374 6174  6531 2130 1f06 0355
040a 0c18 496e 7465  726e 6574 2057 6964
6769 7473 2050 7479  204c 7464 3115 3013
0603 5504 030c 0c59  4f55 5220 4e41 4d45
2121 2131 1830 1606  092a 8648 86f7 0d01
0901 1609 6d65 4062  6172 2e64 6530 819f
300d 0609 2a86 4886  f70d 0101 0105 0003
818d 0030 8189 0281  8100 b640 48de e6bc
2194 3da2 ab5e b6f8  d837 007f 417c 0fe3
3492 c3aa 2f55 3e4d  5e31 4346 89c2 6f2b
e68e 00d2 88b0 e3ab  f6fe 1188 45d9 4989
8512 f192 cbe4 9fd5  b083 1f01 cb2d 274d
b3a6 38f5 befb 3ce8  1ab6 b559 3934 4404
4fed d6ca 154f 76bf  bd52 5608 bb55 0a39
bbd2 ed12 e6d7 1f9f  84ba 21aa 5e21 8015
0267 1aab 049a f864  0da1 0203 0100 0130
0d06 092a 8648 86f7  0d01 0105 0500 0381
8100 8a38 669a 4896  9dc9 4729 6d44 2d7f
0320 82d2 db21 e537  4cdd 6ef6 e7cc 1da0
fde5 11ed 3c52 52f0  a673 dc68 9fdc 5fca
cc1b 85df e22b 7bef  2adb 56b5 3732 e981
1063 794d 6e23 9f8f  a267 215b a7a4 d3dc
e505 e799 ec5c 38cd  1c16 ee75 e0d5 a46b
8f4c 8e82 6505 6153  9a84 305d f19a 5a24
1be5 55f8 7083 4e09  4d41 cf9f 74b3 342e
8345 0000 16
        |}
    and expected = vx {|
1e59 904e e6d5 c2ac  e538 78d7 e24f 6e46
6169 f8e2 d3dd 8b5d  788c 78ff ea9f e1d0
f885 7f17 2a7b f163  d29e 0a8e 8636 418f
a9da 651b f2ba 36aa  a1a4 14d0 6a9a f991
0836 eb93 80b9 bbe2  1f20 98d9 be0b c16f
d58c c98d 4082 dadd  f575 57a4 43f7 af31
c1b7 1eeb 2590 a887  e31c 590a 7e56 798c
69aa 4576 fde6 63d2  1b62 d00d 98f6 4015
dae7 8454 b96a f7f9  774f f539 24bf efe6
4629 ee35 4c81 32d4  43df ffa9 17a2 6306
fd07 f9ab b462 2bcd  bb0a 3750 af1a 3525
66ad 6c67 b647 2ca7  d6b5 b13e ea34 d90d
5731 a599 e608 d037  bc77 40aa b305 84ad
8d78 43fc 7f55 70a2  fbbb 1b30 a14a 2f5f
b3c3 2584 1f9e 7f3f  3dfa 19e2 9539 a1be
ead8 e051 d847 915b  ed23 87ab 7082 7df4
71a0 e0a6 46db a780  1e7b fb98 dac4 0af1
c3eb 42d4 3a6c 3c71  f55a b377 e4de ff20
14d7 b47c 8743 f291  56f3 6d8c 45d1 7cb3
0321 e2cf 8ffd babf  a129 ea0d cc1b 7a0d
b1ec 448d 0e3b 4386  9cc2 2b5a 5569 2930
ea33 080e 9168 3696  b224 6238 34fc 3e25
7895 6af3 cd60 f3c8  6643 3d6f 5736 4e78
6aca 8b2d 1575 2d34  4533 79bd e27e 9c46
f9f4 be4a 2fe3 f377  3acf 7b6e e4f0 3eb0
ec85 95a6 ed04 2316  fe4e 2a54 25aa c40a
c464 4128 0e35 1003  9f5d abfa e8e9 dc73
f709 f29b f930 0bdc  d941 981b c5b3 8295
97a5 c7e9 481d ce99  c6b6 5dfb 672d 3fdb
38bb a6be d7f8 9863  345d c3a8 77f3 6b77
f309 5c3b b9df fa40  8d42 ff79 6724 23da
8f24 c9b0 e02d 4794  581f e185 32e6 94bb
5b6a 6d5c 3b80 4c83  a0d8 0b42 d575 4fc3
4353 a78d fdb5 003c  4f0b 437d 75fb 5886
a76a 35f5 892d a10b  ce33 3ce6 ffd9 f09c
7264 5b09 c50a 7013  344c 11a1 ab92 5728
43e1 bc8c 8c1b 3fad  4a02 25a9 cb96 5fd2
1962 4b0c b46b 9f8f  1225 b18c 2572 6297
c890 238f 22d6 2bb0  7678 568a 3c9b 75e5
b8fc 10f3 13c7 aa16  8165 a29c 67f1 46f4
6e44 8e84 f5
|}
    in
    let g _ =
      let cipher = authenticate_encrypt ~adata ~key ~nonce data in
      assert_oct_equal ~msg:"TLS regression 3" expected cipher
    and h _ =
      match authenticate_decrypt ~key ~nonce ~adata expected with
      | None -> assert_failure "TLS regression 3, decrypt broken"
      | Some x -> assert_oct_equal ~msg:"TLS regression 3 decrypt" x data
    in
    [ a ; b ; c ; d ; e ; f ; g ; h ]
  in
  [
    test_case no_vs_empty_ad ;
    test_case short_nonce_enc ;
    test_case short_nonce_enc2 ;
    test_case short_nonce_enc3 ;
    test_case long_nonce_enc ;
    test_case enc_dec_empty_message ;
    test_case long_adata ;
  ] @ List.map test_case regr_tls

let gcm_regressions =
  let open AES.GCM in
  let msg = vx "000102030405060708090a0b0c0d0e0f" in
  let key = of_secret msg
  and nonce = ""
  in
  let nonce_zero_length_enc _ =
    (* reported in https://github.com/mirleft/ocaml-nocrypto/issues/169 *)
    assert_raises ~msg:"GCM with nonce of length 0"
      (Invalid_argument "Mirage_crypto: GCM: invalid nonce of length 0")
      (fun () -> authenticate_encrypt ~key ~nonce msg)
  and nonce_zero_length_dec _ =
    assert_raises ~msg:"GCM with nonce of 0"
      (Invalid_argument "Mirage_crypto: GCM: invalid nonce of length 0")
      (fun () -> authenticate_decrypt ~key ~nonce msg)
  in
  [
    test_case nonce_zero_length_enc ;
    test_case nonce_zero_length_dec ;
  ]


let chacha20_cases =
  let case msg ?ctr ~key ~nonce ?(input = String.make 128 '\000') output =
    let key = Chacha20.of_secret (vx key)
    and nonce = vx nonce
    and output = vx output
    in
    assert_oct_equal ~msg (Chacha20.crypt ~key ~nonce ?ctr input) output
  in
  let rfc8439_input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it." in
  let rfc8439_test_2_4_2 _ =
    let key = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
    and nonce = "000000000000004a00000000"
    and output =
{|6e 2e 35 9a 25 68 f9 80 41 ba 07 28 dd 0d 69 81
  e9 7e 7a ec 1d 43 60 c2 0a 27 af cc fd 9f ae 0b
  f9 1b 65 c5 52 47 33 ab 8f 59 3d ab cd 62 b3 57
  16 39 d6 24 e6 51 52 ab 8f 53 0c 35 9f 08 61 d8
  07 ca 0d bf 50 0d 6a 61 56 a3 8e 08 8a 22 b6 5e
  52 bc 51 4d 16 cc f8 06 81 8c e9 1a b7 79 37 36
  5a f9 0b bf 74 a3 5b e6 b4 0b 8e ed f2 78 5e 42
  87 4d|}
    in
    case "Chacha20 RFC 8439 2.4.2" ~ctr:1L ~key ~nonce ~input:rfc8439_input output
  and rfc8439_test_2_8_2 _ =
    let key = Chacha20.of_secret (vx "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f")
    and adata = vx "50515253c0c1c2c3c4c5c6c7"
    and nonce = vx "0700000040 41424344454647"
    and output = vx {|
  d3 1a 8d 34 64 8e 60 db 7b 86 af bc 53 ef 7e c2
  a4 ad ed 51 29 6e 08 fe a9 e2 b5 a7 36 ee 62 d6
  3d be a4 5e 8c a9 67 12 82 fa fb 69 da 92 72 8b
  1a 71 de 0a 9e 06 0b 29 05 d6 a5 b6 7e cd 3b 36
  92 dd bd 7f 2d 77 8b 8c 98 03 ae e3 28 09 1b 58
  fa b3 24 e4 fa d6 75 94 55 85 80 8b 48 31 d7 bc
  3f f4 de f0 8e 4b 7a 9d e5 76 d2 65 86 ce c6 4b
  61 16
  1a e1 0b 59 4f 09 e2 6a 7e 90 2e cb d0 60 06 91|}
    in
    assert_oct_equal ~msg:"Chacha20/Poly1305 RFC 8439 2.8.2 encrypt"
      (Chacha20.authenticate_encrypt ~key ~nonce ~adata rfc8439_input)
      output;
    assert_oct_equal ~msg:"Chacha20/Poly1305 RFC 8439 2.8.2 decrypt"
      (match Chacha20.authenticate_decrypt ~key ~nonce ~adata output with
       | Some cs -> cs | None -> assert_failure "Chacha20/poly1305 decryption broken")
      rfc8439_input;
  in
  (* from https://tools.ietf.org/html/draft-strombergson-chacha-test-vectors-01 *)
  let case ~key ~nonce ~output0 ~output1 _ =
    case "chacha20 crypt" ~key ~nonce (output0 ^ output1)
  in
  List.map test_case
    [
      rfc8439_test_2_4_2 ;

      rfc8439_test_2_8_2 ;

      case
        ~key:(String.make 64 '0')
        ~nonce:(String.make 16 '0')
        ~output0:("76b8e0ada0f13d90405d6ae55386bd28" ^
                  "bdd219b8a08ded1aa836efcc8b770dc7" ^
                  "da41597c5157488d7724e03fb8d84a37" ^
                  "6a43b8f41518a11cc387b669b2ee6586")
        ~output1:("9f07e7be5551387a98ba977c732d080d" ^
                  "cb0f29a048e3656912c6533e32ee7aed" ^
                  "29b721769ce64e43d57133b074d839d5" ^
                  "31ed1f28510afb45ace10a1f4b794d6f") ;

      case
        ~key:("01" ^ String.make 62 '0')
        ~nonce:(String.make 16 '0')
        ~output0:("c5d30a7ce1ec119378c84f487d775a85" ^
                  "42f13ece238a9455e8229e888de85bbd" ^
                  "29eb63d0a17a5b999b52da22be4023eb" ^
                  "07620a54f6fa6ad8737b71eb0464dac0")
        ~output1:("10f656e6d1fd55053e50c4875c9930a3" ^
                  "3f6d0263bd14dfd6ab8c70521c19338b" ^
                  "2308b95cf8d0bb7d202d2102780ea352" ^
                  "8f1cb48560f76b20f382b942500fceac") ;

      case
        ~key:(String.make 64 '0')
        ~nonce:("01" ^ String.make 14 '0')
        ~output0:("ef3fdfd6c61578fbf5cf35bd3dd33b80" ^
                  "09631634d21e42ac33960bd138e50d32" ^
                  "111e4caf237ee53ca8ad6426194a8854" ^
                  "5ddc497a0b466e7d6bbdb0041b2f586b")
        ~output1:("5305e5e44aff19b235936144675efbe4" ^
                  "409eb7e8e5f1430f5f5836aeb49bb532" ^
                  "8b017c4b9dc11f8a03863fa803dc71d5" ^
                  "726b2b6b31aa32708afe5af1d6b69058") ;

      case
        ~key:(String.make 64 'f')
        ~nonce:(String.make 16 'f')
        ~output0:("d9bf3f6bce6ed0b54254557767fb5744" ^
                  "3dd4778911b606055c39cc25e674b836" ^
                  "3feabc57fde54f790c52c8ae43240b79" ^
                  "d49042b777bfd6cb80e931270b7f50eb")
        ~output1:("5bac2acd86a836c5dc98c116c1217ec3" ^
                  "1d3a63a9451319f097f3b4d6dab07787" ^
                  "19477d24d24b403a12241d7cca064f79" ^
                  "0f1d51ccaff6b1667d4bbca1958c4306") ;

      case
        ~key:(String.make 64 '5')
        ~nonce:(String.make 16 '5')
        ~output0:("bea9411aa453c5434a5ae8c92862f564" ^
                  "396855a9ea6e22d6d3b50ae1b3663311" ^
                  "a4a3606c671d605ce16c3aece8e61ea1" ^
                  "45c59775017bee2fa6f88afc758069f7")
        ~output1:("e0b8f676e644216f4d2a3422d7fa36c6" ^
                  "c4931aca950e9da42788e6d0b6d1cd83" ^
                  "8ef652e97b145b14871eae6c6804c700" ^
                  "4db5ac2fce4c68c726d004b10fcaba86") ;

      case
        ~key:(String.make 64 'a')
        ~nonce:(String.make 16 'a')
        ~output0:("9aa2a9f656efde5aa7591c5fed4b35ae" ^
                  "a2895dec7cb4543b9e9f21f5e7bcbcf3" ^
                  "c43c748a970888f8248393a09d43e0b7" ^
                  "e164bc4d0b0fb240a2d72115c4808906")
        ~output1:("72184489440545d021d97ef6b693dfe5" ^
                  "b2c132d47e6f041c9063651f96b623e6" ^
                  "2a11999a23b6f7c461b2153026ad5e86" ^
                  "6a2e597ed07b8401dec63a0934c6b2a9") ;

      case
        ~key:"00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100"
        ~nonce:"0f1e2d3c4b5a6978"
        ~output0:("9fadf409c00811d00431d67efbd88fba" ^
                  "59218d5d6708b1d685863fabbb0e961e" ^
                  "ea480fd6fb532bfd494b215101505742" ^
                  "3ab60a63fe4f55f7a212e2167ccab931")
        ~output1:("fbfd29cf7bc1d279eddf25dd316bb884" ^
                  "3d6edee0bd1ef121d12fa17cbc2c574c" ^
                  "ccab5e275167b08bd686f8a09df87ec3" ^
                  "ffb35361b94ebfa13fec0e4889d18da5") ;

      case
        ~key:"c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d"
        ~nonce:"1ada31d5cf688221"
        ~output0:("f63a89b75c2271f9368816542ba52f06" ^
                  "ed49241792302b00b5e8f80ae9a473af" ^
                  "c25b218f519af0fdd406362e8d69de7f" ^
                  "54c604a6e00f353f110f771bdca8ab92")
        ~output1:("e5fbc34e60a1d9a9db17345b0a402736" ^
                  "853bf910b060bdf1f897b6290f01d138" ^
                  "ae2c4c90225ba9ea14d518f55929dea0" ^
                  "98ca7a6ccfe61227053c84e49a4a3332") ;

      case
        ~key:(String.make 32 '0')
        ~nonce:(String.make 16 '0')
        ~output0:("89670952608364fd00b2f90936f031c8" ^
                  "e756e15dba04b8493d00429259b20f46" ^
                  "cc04f111246b6c2ce066be3bfb32d9aa" ^
                  "0fddfbc12123d4b9e44f34dca05a103f")
        ~output1:("6cd135c2878c832b5896b134f6142a9d" ^
                  "4d8d0d8f1026d20a0a81512cbce6e975" ^
                  "8a7143d021978022a384141a80cea306" ^
                  "2f41f67a752e66ad3411984c787e30ad") ;

      case
        ~key:("01" ^ String.make 30 '0')
        ~nonce:(String.make 16 '0')
        ~output0:("ae56060d04f5b597897ff2af1388dbce" ^
                  "ff5a2a4920335dc17a3cb1b1b10fbe70" ^
                  "ece8f4864d8c7cdf0076453a8291c7db" ^
                  "eb3aa9c9d10e8ca36be4449376ed7c42")
        ~output1:("fc3d471c34a36fbbf616bc0a0e7c5230" ^
                  "30d944f43ec3e78dd6a12466547cb4f7" ^
                  "b3cebd0a5005e762e562d1375b7ac445" ^
                  "93a991b85d1a60fba2035dfaa2a642d5") ;

      case
        ~key:(String.make 32 '0')
        ~nonce:("01" ^ String.make 14 '0')
        ~output0:("1663879eb3f2c9949e2388caa343d361" ^
                  "bb132771245ae6d027ca9cb010dc1fa7" ^
                  "178dc41f8278bc1f64b3f12769a24097" ^
                  "f40d63a86366bdb36ac08abe60c07fe8")
        ~output1:("b057375c89144408cc744624f69f7f4c" ^
                  "cbd93366c92fc4dfcada65f1b959d8c6" ^
                  "4dfc50de711fb46416c2553cc60f21bb" ^
                  "fd006491cb17888b4fb3521c4fdd8745") ;

      case
        ~key:(String.make 32 'f')
        ~nonce:(String.make 16 'f')
        ~output0:("992947c3966126a0e660a3e95db048de" ^
                  "091fb9e0185b1e41e41015bb7ee50150" ^
                  "399e4760b262f9d53f26d8dd19e56f5c" ^
                  "506ae0c3619fa67fb0c408106d0203ee")
        ~output1:("40ea3cfa61fa32a2fda8d1238a2135d9" ^
                  "d4178775240f99007064a6a7f0c731b6" ^
                  "7c227c52ef796b6bed9f9059ba0614bc" ^
                  "f6dd6e38917f3b150e576375be50ed67") ;

      case
        ~key:(String.make 32 '5')
        ~nonce:(String.make 16 '5')
        ~output0:("357d7d94f966778f5815a2051dcb0413" ^
                  "3b26b0ead9f57dd09927837bc3067e4b" ^
                  "6bf299ad81f7f50c8da83c7810bfc17b" ^
                  "b6f4813ab6c326957045fd3fd5e19915")
        ~output1:("ec744a6b9bf8cbdcb36d8b6a5499c68a" ^
                  "08ef7be6cc1e93f2f5bcd2cad4e47c18" ^
                  "a3e5d94b5666382c6d130d822dd56aac" ^
                  "b0f8195278e7b292495f09868ddf12cc") ;

      case
        ~key:(String.make 32 'a')
        ~nonce:(String.make 16 'a')
        ~output0:("fc79acbd58526103862776aab20f3b7d" ^
                  "8d3149b2fab65766299316b6e5b16684" ^
                  "de5de548c1b7d083efd9e3052319e0c6" ^
                  "254141da04a6586df800f64d46b01c87")
        ~output1:("1f05bc67e07628ebe6f6865a2177e0b6" ^
                  "6a558aa7cc1e8ff1a98d27f7071f8335" ^
                  "efce4537bb0ef7b573b32f32765f2900" ^
                  "7da53bba62e7a44d006f41eb28fe15d6") ;

      case
        ~key:"00112233445566778899aabbccddeeff"
        ~nonce:"0f1e2d3c4b5a6978"
        ~output0:("d1abf630467eb4f67f1cfb47cd626aae" ^
                  "8afedbbe4ff8fc5fe9cfae307e74ed45" ^
                  "1f1404425ad2b54569d5f18148939971" ^
                  "abb8fafc88ce4ac7fe1c3d1f7a1eb7ca")
        ~output1:("e76ca87b61a9713541497760dd9ae059" ^
                  "350cad0dcedfaa80a883119a1a6f987f" ^
                  "d1ce91fd8ee0828034b411200a9745a2" ^
                  "85554475d12afc04887fef3516d12a2c") ;

      case
        ~key:"c46ec1b18ce8a878725a37e780dfb735"
        ~nonce:"1ada31d5cf688221"
        ~output0:("826abdd84460e2e9349f0ef4af5b179b" ^
                  "426e4b2d109a9c5bb44000ae51bea90a" ^
                  "496beeef62a76850ff3f0402c4ddc99f" ^
                  "6db07f151c1c0dfac2e56565d6289625")
        ~output1:("5b23132e7b469c7bfb88fa95d44ca5ae" ^
                  "3e45e848a4108e98bad7a9eb15512784" ^
                  "a6a9e6e591dce674120acaf9040ff50f" ^
                  "f3ac30ccfb5e14204f5e4268b90a8804")
    ]

let poly1305_rfc8439_2_5_2 _ =
  let key = vx "85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b"
  and data = "Cryptographic Forum Research Group"
  and output = vx "a8061dc1305136c6c22b8baf0c0127a9"
  in
  assert_oct_equal ~msg:"poly 1305 RFC8439 Section 2.5.2"
    (Poly1305.mac ~key data) output

let empty_cases _ =
  let plain = ""
  and cipher = ""
  in
  (* 3DES ECB CBC CTR *)
  Array.iter (fun key_size ->
      let key = DES.ECB.of_secret (String.make key_size '\x00') in
      assert_oct_equal ~msg:"DES ECB encrypt" cipher (DES.ECB.encrypt ~key plain) ;
      assert_oct_equal ~msg:"DES ECB decrypt" plain (DES.ECB.decrypt ~key cipher))
    DES.ECB.key_sizes ;
  Array.iter (fun key_size ->
      let key = DES.CBC.of_secret (String.make key_size '\x00')
      and iv = String.make DES.CBC.block_size '\x00'
      in
      assert_oct_equal ~msg:"DES CBC encrypt" cipher (DES.CBC.encrypt ~key ~iv plain) ;
      assert_oct_equal ~msg:"DES CBC decrypt" plain (DES.CBC.decrypt ~key ~iv cipher))
    DES.CBC.key_sizes ;
  Array.iter (fun key_size ->
      let key = DES.CTR.of_secret (String.make key_size '\x00')
      and ctr = DES.CTR.ctr_of_octets (String.make DES.CTR.block_size '\x00')
      in
      assert_oct_equal ~msg:"DES CTR encrypt" cipher (DES.CTR.encrypt ~key ~ctr plain) ;
      assert_oct_equal ~msg:"DES CTR decrypt" plain (DES.CTR.decrypt ~key ~ctr cipher))
    DES.CTR.key_sizes ;

  (* AES ECB CBC CTR GCM CCM16 *)
  Array.iter (fun key_size ->
      let key = AES.ECB.of_secret (String.make key_size '\x00') in
      assert_oct_equal ~msg:"AES ECB encrypt" cipher (AES.ECB.encrypt ~key plain) ;
      assert_oct_equal ~msg:"AES ECB decrypt" plain (AES.ECB.decrypt ~key cipher))
    AES.ECB.key_sizes ;
  Array.iter (fun key_size ->
      let key = AES.CBC.of_secret (String.make key_size '\x00')
      and iv = String.make AES.CBC.block_size '\x00'
      in
      assert_oct_equal ~msg:"AES CBC encrypt" cipher (AES.CBC.encrypt ~key ~iv plain) ;
      assert_oct_equal ~msg:"AES CBC decrypt" plain (AES.CBC.decrypt ~key ~iv cipher))
    AES.CBC.key_sizes ;
  Array.iter (fun key_size ->
      let key = AES.CTR.of_secret (String.make key_size '\x00')
      and ctr = AES.CTR.ctr_of_octets (String.make AES.CTR.block_size '\x00')
      in
      assert_oct_equal ~msg:"AES CTR encrypt" cipher (AES.CTR.encrypt ~key ~ctr plain) ;
      assert_oct_equal ~msg:"AES CTR decrypt" plain (AES.CTR.decrypt ~key ~ctr cipher))
    AES.CTR.key_sizes ;
  Array.iter (fun key_size ->
      let key = AES.CCM16.of_secret (String.make key_size '\x00') in
      let test_one nonce =
        let c, tag = AES.CCM16.authenticate_encrypt_tag ~key ~nonce plain in
        assert_oct_equal ~msg:"AES CCM16 encrypt" cipher c ;
        match AES.CCM16.authenticate_decrypt_tag ~key ~nonce ~tag cipher with
        | None -> assert false
        | Some p -> assert_oct_equal ~msg:"AES CCM16 decrypt" plain p
      in
      test_one (String.make 7 '\x00');
      test_one (String.make 8 '\x00');
      test_one (String.make 13 '\x00'))
    AES.CCM16.key_sizes ;

  (* ChaCha20 *)
  Array.iter (fun key_size ->
      let key = Chacha20.of_secret (String.make key_size '\x00') in
      let test_one nonce =
        let c, tag = Chacha20.authenticate_encrypt_tag ~key ~nonce plain in
        assert_oct_equal ~msg:"Chacha20 encrypt" cipher c ;
        match Chacha20.authenticate_decrypt_tag ~key ~nonce ~tag cipher with
        | None -> assert false
        | Some p -> assert_oct_equal ~msg:"Chacha20 decrypt" plain p
      in
      test_one (String.make 8 '\x00');
      if key_size = 32 then
        test_one (String.make 12 '\x00'))
    [| 16 ; 32 |] ;

  (* ARC4 *)
  let key = ARC4.of_secret (String.make 16 '\x00') in
  assert_oct_equal ~msg:"ARC4 encrypt" cipher (ARC4.(encrypt ~key plain).message) ;
  assert_oct_equal ~msg:"ARC4 decrypt" plain (ARC4.(decrypt ~key cipher).message)

let aead_forged_tag =
  let nonce = String.make 12 '\x00'
  and secret = "let password = 42" in
  let len = String.length secret in
  let sentinel = String.make len '\x2a' in
  let forge blob =
    let b = Bytes.of_string blob in
    Bytes.set b len (Char.chr (Char.code (Bytes.get b len) lxor 1)) ;
    Bytes.unsafe_to_string b
  in
  let gcm _ =
    let key = AES.GCM.of_secret (String.make 32 '\x00') in
    let forged = forge (AES.GCM.authenticate_encrypt ~key ~nonce secret) in
    let dst = Bytes.of_string sentinel in
    let ok = AES.GCM.authenticate_decrypt_into ~key ~nonce forged ~src_off:0
        ~tag_off:len dst ~dst_off:0 len in
    assert_bool "GCM forged tag rejected" (not ok) ;
    assert_equal ~printer:(fun s -> s) ~msg:"GCM dst untouched on forgery"
      sentinel (Bytes.to_string dst)
  and chacha _ =
    let key = Chacha20.of_secret (String.make 32 '\x00') in
    let forged = forge (Chacha20.authenticate_encrypt ~key ~nonce secret) in
    let dst = Bytes.of_string sentinel in
    let ok = Chacha20.authenticate_decrypt_into ~key ~nonce forged ~src_off:0
        ~tag_off:len dst ~dst_off:0 len in
    assert_bool "Chacha20 forged tag rejected" (not ok) ;
    assert_equal ~printer:(fun s -> s) ~msg:"Chacha20 dst untouched on forgery"
      sentinel (Bytes.to_string dst)
  and ccm _ =
    let key = AES.CCM16.of_secret (vx "000102030405060708090a0b0c0d0e0f") in
    let forged = forge (AES.CCM16.authenticate_encrypt ~key ~nonce secret) in
    let dst = Bytes.of_string sentinel in
    let ok = AES.CCM16.authenticate_decrypt_into ~key ~nonce forged ~src_off:0
        ~tag_off:len dst ~dst_off:0 len in
    assert_bool "AES CCM forged tag rejected" (not ok) ;
    assert_equal ~printer:(fun s -> s) ~msg:"AES CCM dst zeroed on forgery"
      (String.make len '\000') (Bytes.to_string dst)
  in
  [ test_case gcm ; test_case chacha ; test_case ccm ]

let suite = [
  "3DES-ECB" >::: des_ecb_cases ;
  "3DES-CBC" >::: des_cbc_cases ;
  "3DES-CTR" >::: des_ctr_cases ;
  "AES-ECB" >::: [ "SP 300-38A" >::: aes_ecb_cases ] ;
  "AES-CBC" >::: [ "SP 300-38A" >::: aes_cbc_cases ] ;
  "AES-CTR" >::: [ "SP 300-38A" >::: aes_ctr_cases; ] ;
  "AES-GCM" >::: gcm_cases ;
  (* "AES-CCM" >::: ccm_cases ; *)
  "AES-CCM-REGRESSION" >::: ccm_regressions ;
  "AES-GCM-REGRESSION" >::: gcm_regressions ;
  "Chacha20" >::: chacha20_cases ;
  "poly1305" >:: poly1305_rfc8439_2_5_2 ;
  "empty" >:: empty_cases ;
  "AEAD-forged-tag" >::: aead_forged_tag ;
]
