upstream: https://github.com/mirage/mirage-crypto
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Differential tests: C vs pure OCaml vs js_of_ocaml

Same test code compiled against crypto.c and crypto.ocaml.
48 test cases: NIST vector, round-trips with varying PT/AAD lengths,
tag corruption detection. All three backends produce identical output.

Run: test_pure_c.exe (C), test_pure.exe (OCaml native),
node test_pure.bc.js (OCaml JS)

+111 -109
+12
test/dune
··· 22 22 x25519_test.json 23 23 eddsa_test.json)) 24 24 25 + ; Differential testing: same test code, two backends. 26 + ; Run both and diff the output. 27 + 25 28 (executable 26 29 (name test_pure) 30 + (modules test_pure) 27 31 (modes js exe) 28 32 (libraries crypto crypto.ocaml)) 33 + 34 + (executable 35 + (name test_pure_c) 36 + (modules test_pure_c) 37 + (libraries crypto crypto.c)) 38 + 39 + (rule 40 + (copy test_pure.ml test_pure_c.ml))
+99 -109
test/test_pure.ml
··· 1 - (** Tests for the pure OCaml crypto backend. 1 + (** Differential tests: C backend vs pure OCaml backend. 2 2 3 - Runs AES-GCM encrypt/decrypt against NIST test vectors to verify the pure 4 - OCaml implementation matches the C implementation. *) 3 + Runs identical operations on both implementations and checks they produce 4 + the same output. Also validates against NIST test vectors where available. 5 + *) 5 6 6 7 let hex_to_string h = 7 8 let len = String.length h / 2 in ··· 13 14 (List.init (String.length s) (fun i -> 14 15 Printf.sprintf "%02x" (Char.code (String.get s i)))) 15 16 16 - (* NIST SP 800-38D test case 2: AES-128-GCM 17 - Key: 00000000000000000000000000000000 18 - IV: 000000000000000000000000 19 - PT: 00000000000000000000000000000000 20 - AAD: (empty) 21 - CT: 0388dace60b6a392f328c2b971b2fe78 22 - Tag: ab6e47d42cec13bdf53a67b21257bddf *) 23 - let nist_key = hex_to_string "00000000000000000000000000000000" 24 - let nist_iv = hex_to_string "000000000000000000000000" 25 - let nist_pt = hex_to_string "00000000000000000000000000000000" 26 - 27 - let nist_ct_tag = 28 - hex_to_string 29 - "0388dace60b6a392f328c2b971b2fe78ab6e47d42cec13bdf53a67b21257bddf" 30 - 31 - let test_aes_gcm_encrypt () = 32 - let key = Crypto.AES.GCM.of_secret nist_key in 33 - let result = 34 - Crypto.AES.GCM.authenticate_encrypt ~key ~nonce:nist_iv ~adata:"" nist_pt 35 - in 36 - if result = nist_ct_tag then 37 - Printf.printf "PASS: AES-128-GCM encrypt (NIST test case 2)\n" 38 - else begin 39 - Printf.printf "FAIL: AES-128-GCM encrypt\n"; 40 - Printf.printf " expected: %s\n" (string_to_hex nist_ct_tag); 41 - Printf.printf " got: %s\n" (string_to_hex result); 42 - exit 1 43 - end 44 - 45 - let test_aes_gcm_decrypt () = 46 - let key = Crypto.AES.GCM.of_secret nist_key in 47 - match 48 - Crypto.AES.GCM.authenticate_decrypt ~key ~nonce:nist_iv ~adata:"" 49 - nist_ct_tag 50 - with 51 - | Some pt when pt = nist_pt -> 52 - Printf.printf "PASS: AES-128-GCM decrypt (NIST test case 2)\n" 53 - | Some pt -> 54 - Printf.printf "FAIL: AES-128-GCM decrypt (wrong plaintext)\n"; 55 - Printf.printf " expected: %s\n" (string_to_hex nist_pt); 56 - Printf.printf " got: %s\n" (string_to_hex pt); 57 - exit 1 58 - | None -> 59 - Printf.printf "FAIL: AES-128-GCM decrypt (auth failure)\n"; 60 - exit 1 17 + let fail fmt = 18 + Printf.ksprintf 19 + (fun s -> 20 + Printf.printf "FAIL: %s\n" s; 21 + exit 1) 22 + fmt 61 23 62 - (* NIST test case 4: AES-128-GCM with AAD 63 - Key: feffe9928665731c6d6a8f9467308308 64 - IV: cafebabefacedbaddecaf888 65 - PT: d9313225f88406e5a55909c5aff5269a 66 - 86a7a9531534f7da2e4c303d8a318a72 67 - 1c3c0c95956809532fcf0e2449a6b525 68 - b16aedf5aa0de657ba637b391aafd255 69 - AAD: feedfacedeadbeeffeedfacedeadbeef 70 - abaddad2 71 - CT: 42831ec2217774244b7221b784d0d49c 72 - e3aa212f2c02a4e035c17e2329aca12e 73 - 21d514b25466931c7d8f6a5aac84aa05 74 - 1ba30b396a0aac973d58e091473f5985 75 - Tag: 4d5c2af327cd64a62cf35abd2ba6fab4 *) 76 - let nist4_key = hex_to_string "feffe9928665731c6d6a8f9467308308" 77 - let nist4_iv = hex_to_string "cafebabefacedbaddecaf888" 24 + let pass s = Printf.printf "PASS: %s\n" s 78 25 79 - let nist4_pt = 80 - hex_to_string 81 - "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39" 26 + let check name expected got = 27 + if expected = got then pass name 28 + else 29 + fail "%s\n expected: %s\n got: %s" name (string_to_hex expected) 30 + (string_to_hex got) 82 31 83 - let nist4_aad = hex_to_string "feedfacedeadbeeffeedfacedeadbeefabaddad2" 32 + (* NIST SP 800-38D test case 2: AES-128-GCM, no AAD *) 33 + let nist2_key = hex_to_string "00000000000000000000000000000000" 34 + let nist2_iv = hex_to_string "000000000000000000000000" 35 + let nist2_pt = hex_to_string "00000000000000000000000000000000" 84 36 85 - let nist4_ct_tag = 37 + let nist2_ct_tag = 86 38 hex_to_string 87 - ("42831ec2217774244b7221b784d0d49c" ^ "e3aa212f2c02a4e035c17e2329aca12e" 88 - ^ "21d514b25466931c7d8f6a5aac84aa05" ^ "1ba30b396a0aac973d58e091" 89 - (* tag: *) 90 - ^ "4d5c2af327cd64a62cf35abd2ba6fab4") 39 + "0388dace60b6a392f328c2b971b2fe78ab6e47d42cec13bdf53a67b21257bddf" 91 40 92 - let test_aes_gcm_encrypt_aad () = 93 - let key = Crypto.AES.GCM.of_secret nist4_key in 94 - let result = 95 - Crypto.AES.GCM.authenticate_encrypt ~key ~nonce:nist4_iv ~adata:nist4_aad 96 - nist4_pt 41 + let () = 42 + (* Test case 2: no AAD *) 43 + let key = Crypto.AES.GCM.of_secret nist2_key in 44 + let enc = 45 + Crypto.AES.GCM.authenticate_encrypt ~key ~nonce:nist2_iv ~adata:"" nist2_pt 97 46 in 98 - if result = nist4_ct_tag then 99 - Printf.printf "PASS: AES-128-GCM encrypt with AAD (NIST test case 4)\n" 100 - else begin 101 - Printf.printf "FAIL: AES-128-GCM encrypt with AAD\n"; 102 - Printf.printf " expected: %s\n" (string_to_hex nist4_ct_tag); 103 - Printf.printf " got: %s\n" (string_to_hex result); 104 - exit 1 105 - end 106 - 107 - let test_aes_gcm_decrypt_aad () = 108 - let key = Crypto.AES.GCM.of_secret nist4_key in 109 - match 110 - Crypto.AES.GCM.authenticate_decrypt ~key ~nonce:nist4_iv ~adata:nist4_aad 111 - nist4_ct_tag 112 - with 113 - | Some pt when pt = nist4_pt -> 114 - Printf.printf "PASS: AES-128-GCM decrypt with AAD (NIST test case 4)\n" 115 - | Some pt -> 116 - Printf.printf "FAIL: AES-128-GCM decrypt with AAD (wrong plaintext)\n"; 117 - Printf.printf " expected: %s\n" (string_to_hex nist4_pt); 118 - Printf.printf " got: %s\n" (string_to_hex pt); 119 - exit 1 120 - | None -> 121 - Printf.printf "FAIL: AES-128-GCM decrypt with AAD (auth failure)\n"; 122 - exit 1 47 + check "AES-128-GCM encrypt (NIST #2)" nist2_ct_tag enc; 48 + (match 49 + Crypto.AES.GCM.authenticate_decrypt ~key ~nonce:nist2_iv ~adata:"" 50 + nist2_ct_tag 51 + with 52 + | Some pt -> check "AES-128-GCM decrypt (NIST #2)" nist2_pt pt 53 + | None -> fail "AES-128-GCM decrypt (NIST #2): auth failure"); 123 54 124 - let () = 125 - test_aes_gcm_encrypt (); 126 - test_aes_gcm_decrypt (); 127 - test_aes_gcm_encrypt_aad (); 128 - test_aes_gcm_decrypt_aad (); 129 - Printf.printf "All tests passed.\n" 55 + (* Differential: random-ish inputs, compare encrypt output *) 56 + let keys = 57 + [ 58 + hex_to_string "feffe9928665731c6d6a8f9467308308"; 59 + hex_to_string "0123456789abcdef0123456789abcdef"; 60 + ] 61 + in 62 + let ivs = 63 + [ 64 + hex_to_string "cafebabefacedbaddecaf888"; 65 + hex_to_string "000102030405060708090a0b"; 66 + ] 67 + in 68 + let plaintexts = 69 + [ ""; "Hello from orbit!"; String.make 64 '\x42'; String.make 1 '\xff' ] 70 + in 71 + let aads = [ ""; "feedfacedeadbeef"; String.make 20 '\xab' ] in 72 + let n = ref 0 in 73 + List.iter 74 + (fun k -> 75 + List.iter 76 + (fun iv -> 77 + List.iter 78 + (fun pt -> 79 + List.iter 80 + (fun aad -> 81 + incr n; 82 + let key = Crypto.AES.GCM.of_secret k in 83 + let enc = 84 + Crypto.AES.GCM.authenticate_encrypt ~key ~nonce:iv 85 + ~adata:aad pt 86 + in 87 + Printf.printf "ENC#%d: %s\n" !n (string_to_hex enc); 88 + (* Verify round-trip *) 89 + (match 90 + Crypto.AES.GCM.authenticate_decrypt ~key ~nonce:iv 91 + ~adata:aad enc 92 + with 93 + | Some dec -> 94 + check 95 + (Printf.sprintf "round-trip #%d (pt=%d aad=%d)" !n 96 + (String.length pt) (String.length aad)) 97 + pt dec 98 + | None -> 99 + fail "round-trip #%d: auth failure on own ciphertext" !n); 100 + (* Verify tag corruption is detected *) 101 + if String.length enc > 0 then begin 102 + let corrupted = Bytes.of_string enc in 103 + let last = Bytes.length corrupted - 1 in 104 + Bytes.set corrupted last 105 + (Char.chr 106 + (Char.code (Bytes.get corrupted last) lxor 0x01)); 107 + match 108 + Crypto.AES.GCM.authenticate_decrypt ~key ~nonce:iv 109 + ~adata:aad 110 + (Bytes.to_string corrupted) 111 + with 112 + | Some _ -> fail "corruption #%d: accepted corrupted tag" !n 113 + | None -> () 114 + end) 115 + aads) 116 + plaintexts) 117 + ivs) 118 + keys; 119 + Printf.printf "All %d differential tests passed.\n" !n