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
open Conex_utils open Conex_resource module Ui = struct let ui = let module M = struct type t = Uint.t let pp ppf v = Format.pp_print_string ppf (Uint.to_string v) let equal a b = Uint.compare a b = 0 end in (module M: Alcotest.TESTABLE with type t = M.t) let the x = match Uint.of_string x with Some x -> x | None -> Alcotest.fail "cannot parse" let max = the "FFFFFFFFFFFFFFFF" let max_int64 = the "7FFFFFFFFFFFFFFF" let min_int64 = the "8000000000000000" let compare_initial () = Alcotest.(check int "compare 0 0 is 0" 0 Uint.(compare zero zero)) ; Alcotest.(check int "compare 0 max is -1" (-1) Uint.(compare zero max)) ; Alcotest.(check int "compare max 0 is 1" 1 Uint.(compare max zero)) ; Alcotest.(check int "compare 0 max_int64 is -1" (-1) Uint.(compare zero max_int64)) ; Alcotest.(check int "compare max_int64 0 is 1" 1 Uint.(compare max_int64 zero)) ; Alcotest.(check int "compare 0 min_int64 is -1" (-1) Uint.(compare zero min_int64)) ; Alcotest.(check int "compare min_int64 0 is 1" 1 Uint.(compare min_int64 zero)) let succ_initial () = Alcotest.(check bool "succ max overflows" true (fst (Uint.succ max))) ; let one = snd Uint.(succ zero) in Alcotest.(check (pair bool ui) "succ 0 no overflow and one" (false, one) Uint.(succ zero)) ; Alcotest.(check (pair bool ui) "succ max_int64 no overflow and min_int64" (false, min_int64) Uint.(succ max_int64)) ; Alcotest.(check bool "succ min_int64 no overflow" false (fst Uint.(succ min_int64))) let r () = let buf = Bytes.create 16 in let one i = let r = Random.int 16 in let ascii = r + (if r < 10 then 0x30 else 0x37) in Bytes.set buf i (char_of_int ascii) in for i = 0 to 15 do one i done ; Bytes.to_string buf let rec trim0 s = if String.get s 0 = '0' then trim0 (String.slice ~start:1 s) else s let to_of_str_id n () = for _i = 0 to n do let r = r () in Alcotest.(check string "to_of_string is identity" (trim0 r) Uint.(to_string (the r))) done let compare_random n () = for _i = 0 to n do let r = the (r ()) in let r = if r = Uint.zero then snd (Uint.succ r) else r in Alcotest.(check int ("compare " ^ Uint.to_string r ^ " 0 is 1") 1 Uint.(compare r zero)) ; Alcotest.(check int ("compare 0 " ^ Uint.to_string r ^ " is -1") (-1) Uint.(compare zero r)) ; Alcotest.(check int ("compare " ^ Uint.to_string r ^ " with itself is 0") 0 Uint.(compare r r)) ; if r = max then begin Alcotest.(check int ("compare " ^ Uint.to_string r ^ " max is 0") 0 Uint.(compare r max)) ; Alcotest.(check int ("compare max " ^ Uint.to_string r ^ " is 0") 0 Uint.(compare max r)) end else begin Alcotest.(check int ("compare " ^ Uint.to_string r ^ " max is -1") (-1) Uint.(compare r max)) ; Alcotest.(check int ("compare max " ^ Uint.to_string r ^ " is 1") 1 Uint.(compare max r)) ; end done let tests = [ "basic compare is good", `Quick, compare_initial ; "succ is good", `Quick, succ_initial ; "to/of_string is identity", `Quick, to_of_str_id 10000 ; "compare r zero is good", `Quick, compare_random 1000 ; ] end open Common module CS = Conex_nocrypto.NC_S module BasicTests (V : Conex_crypto.VERIFY) (R : Conex_crypto.VERIFY_BACK) = struct let raw_sign p d = match Conex_nocrypto.C.sign_rsa_pss ~key:p d with | Ok s -> s | Error e -> Alcotest.fail e let sig_good () = let pid = "foobar" in let pub, p = gen_pub () in let priv = match p with `Priv (`RSA, k, _) -> k in let pu = match pub with `RSA, k, _ -> k in let d = Wire.to_string (Signature.wire pid (`RSA_PSS_SHA256, Uint.zero) pid) in let signature = raw_sign priv d in Alcotest.check (result Alcotest.unit verr) "signature is good" (Ok ()) (R.verify_rsa_pss ~key:pu ~data:d ~signature) let sign_single () = let idx = Author.t Uint.zero "a" in let pub, priv = gen_pub () in let pri = match priv with `Priv (`RSA, k, _) -> k in let key = match pub with `RSA, k, _ -> k in let raw = Wire.to_string (Author.wire idx) in let sv = raw_sign pri raw in Alcotest.check (result Alcotest.unit verr) "signature can be verified" (Ok ()) (R.verify_rsa_pss ~key ~data:raw ~signature:sv) ; Alcotest.check (result Alcotest.unit verr) "signature empty" (Error `InvalidSignature) (R.verify_rsa_pss ~key ~data:raw ~signature:"") ; Alcotest.check (result Alcotest.unit verr) "signature is bad (b64prefix)" (Error `InvalidBase64Encoding) (R.verify_rsa_pss ~key ~data:raw ~signature:("\000" ^ sv)) ; Alcotest.check (result Alcotest.unit verr) "signature is bad (prefix)" (Error `InvalidSignature) (R.verify_rsa_pss ~key ~data:raw ~signature:("abcd" ^ sv)) ; Alcotest.check (result Alcotest.unit verr) "signature is bad (postfix)" (Error `InvalidBase64Encoding) (R.verify_rsa_pss ~key ~data:raw ~signature:(sv ^ "abcd")) ; Alcotest.check (result Alcotest.unit verr) "signature is bad (raw)" (Error `InvalidSignature) (R.verify_rsa_pss ~key ~data:"" ~signature:sv) ; Alcotest.check (result Alcotest.unit verr) "public key is bad (empty)" (Error `InvalidPublicKey) (R.verify_rsa_pss ~key:"" ~data:raw ~signature:sv) ; Alcotest.check (result Alcotest.unit verr) "public key is bad (prepended)" (Error `InvalidPublicKey) (R.verify_rsa_pss ~key:("\000" ^ key) ~data:raw ~signature:sv) ; Alcotest.check (result Alcotest.unit verr) "public key is bad (prepended)" (Error `InvalidPublicKey) (R.verify_rsa_pss ~key:("abcd" ^ key) ~data:raw ~signature:sv) (* TODO: re-enable this test... openssl doesn't care about extra data, should we? *) (* Alcotest.check (result Alcotest.unit verr) "public key is bad (appended)" (Error `InvalidPublicKey) (R.verify_rsa_pss ~key:(key ^ "abcd") ~data:raw ~signature:sv) ; Alcotest.check (result Alcotest.unit verr) "public key is bad (appended)" (Error `InvalidPublicKey) (R.verify_rsa_pss ~key:(key ^ "\000") ~data:raw ~signature:sv) *) let bad_priv () = let idx = Author.t Uint.zero "a" in let pub, priv = gen_pub () in let raw = Wire.to_string (Author.wire idx) in Alcotest.check (result Alcotest.string Alcotest.string) "sign with broken key is broken" (Error "couldn't decode private key") (Conex_nocrypto.C.sign_rsa_pss ~key:"" raw) ; let get_pub p = Conex_nocrypto.C.pub_of_priv_rsa p in Alcotest.check (result Alcotest.string Alcotest.string) "pub_of_priv with broken key is broken" (Error "couldn't decode private key") (get_pub "") ; let mypriv = match priv with `Priv (`RSA, p, _) -> p and _, mypub, _ = pub in Alcotest.check (result Alcotest.string Alcotest.string) "pub_of_priv works fine" (Ok mypub) (get_pub mypriv) let verify_fail () = let idx = Author.t Uint.zero "a" in let k, p = gen_pub () in let signed = sign_idx idx p in let single_sig = match signed.Author.keys with | [(_,x)] -> x | _ -> Alcotest.fail "expected a single signature" in Alcotest.check (result Alcotest.unit verr) "signed index verifies" (Ok ()) (V.verify signed) ; let (hdr, v) = single_sig in let v' = Bytes.(of_string v) in Bytes.set v' 3 'f' ; Bytes.set v' 2 'f' ; let idx = Author.replace_sig signed (k, (hdr, (Bytes.to_string v'))) in Alcotest.check (result Alcotest.unit verr) "bad signature does not verify" (Error `InvalidSignature) (V.verify idx) ; let pub = match CS.(pub_of_priv (generate ~bits:20 Uint.zero ())) with | Ok p -> p | Error _ -> Alcotest.fail "couldn't pub_of_priv" in let idx = Author.t ~keys:[pub, single_sig] Uint.zero "a" in Alcotest.check (result Alcotest.unit verr) "too small key" (Error `InvalidPublicKey) (V.verify idx) ; let idx = Author.replace_sig signed (k, (hdr, Wire.to_string (Author.wire_raw signed))) in Alcotest.check (result Alcotest.unit verr) "invalid b64 sig" (Error `InvalidBase64Encoding) (V.verify idx) let sign_tests = [ "sign and verify is good", `Quick, sig_good ; "self-sign is good", `Quick, sign_single ; "bad priv is bad", `Quick, bad_priv ; "verify failures", `Quick, verify_fail ; ] let idx_sign () = let _, p = gen_pub () in let idx = Author.t Uint.zero "a" in let signed = sign_idx idx p in Alcotest.check (result Alcotest.unit verr) "signed index verifies" (Ok ()) (V.verify signed) ; let r = Author.r (Author.next_id idx) "foo" `Key (`SHA256, "2342") in let idx' = Author.queue signed r in Alcotest.check (result Alcotest.unit verr) "signed modified index does verify (no commit)" (Ok ()) (V.verify idx') ; let idx', _ = Author.prep_sig idx' in Alcotest.check (result Alcotest.unit verr) "signed modified index does verify (no commit)" (Error `InvalidSignature) (V.verify idx') let idx_sign_bad () = let _, p = gen_pub () in let idx = Author.t Uint.zero "b" in let signed = sign_idx idx p in let idx' = Author.t ~keys:signed.Author.keys Uint.zero "c" in Alcotest.check (result Alcotest.unit verr) "signed index does not verify (wrong id)" (Error `InvalidSignature) (V.verify idx') let idx_sign_bad2 () = let _, p = gen_pub () in let idx = Author.t Uint.zero "a" in let signed = sign_idx idx p in let idx' = Author.t ~keys:signed.Author.keys ~counter:(Uint.of_int_exn 23) Uint.zero "a" in Alcotest.check (result Alcotest.unit verr) "signed index does not verify (wrong data)" (Error `InvalidSignature) (V.verify idx') let idx_tests = [ "good index", `Quick, idx_sign ; "bad index", `Quick, idx_sign_bad ; "bad index 2", `Quick, idx_sign_bad2 ; ] let tests prefix = [ (prefix ^ "Signature", sign_tests) ; (prefix ^ "Index", idx_tests) ] end module NC = BasicTests (Conex_nocrypto.NC_V) (Conex_nocrypto.V) module OC = BasicTests (Conex_openssl.O_V) (Conex_openssl.V) let tests = ("Uint", Ui.tests) :: NC.tests "Nocrypto"