open Mirage_crypto.Uncommon open Common let two = Z.(~$2) and three = Z.(~$3) (* A constant-time [find_uint8] with a default value. *) let ct_find_uint8 ~default ?off ~f cs = let res = Eqaf.find_uint8 ?off ~f cs in Eqaf.select_int (res + 1) default res let (&.) f g = fun h -> f (g h) type 'a or_digest = [ `Message of 'a | `Digest of string ] module Digest_or (H : Digestif.S) = struct let digest_or = function | `Message msg -> H.(digest_string msg |> to_raw_string) | `Digest digest -> let n = String.length digest and m = H.digest_size in if n = m then digest else invalid_arg "(`Digest _): %d bytes, expecting %d" n m end exception Insufficient_key type pub = { e : Z.t ; n : Z.t } (* due to PKCS1 *) let minimum_octets = 12 let minimum_bits = 8 * minimum_octets - 7 let pub ~e ~n = (* We cannot verify a public key being good (this would require to verify "n" being the multiplication of two prime numbers - figuring out which primes were used is the security property of RSA). but we validate to ensure our usage of powm_sec does not lead to exceptions, and we avoid tiny public keys where PKCS1 / PSS would lead to infinite loops or not work due to insufficient space for the header. *) let* () = guard Z.(n > zero && is_odd n && numbits n >= minimum_bits) (`Msg "invalid modulus") in let* () = guard Z.(one < e && e < n) (`Msg "invalid exponent") in (* NOTE that we could check for e being odd, or a prime, or 2^16+1, but these are not requirements, neither for RSA nor for powm_sec *) Ok { e ; n } type priv = { e : Z.t ; d : Z.t ; n : Z.t ; p : Z.t ; q : Z.t ; dp : Z.t ; dq : Z.t ; q' : Z.t } let valid_prime name p = guard Z.(p > zero && is_odd p && Z_extra.pseudoprime p) (`Msg ("invalid prime " ^ name)) let rprime a b = Z.(gcd a b = one) let valid_e ~e ~p ~q = let* () = guard (rprime e (Z.pred p) && rprime e (Z.pred q)) (`Msg "e is not coprime of p and q") in guard (Z_extra.pseudoprime e) (`Msg "exponent e is not a pseudoprime") let priv ~e ~d ~n ~p ~q ~dp ~dq ~q' = let* _ = pub ~e ~n in let* () = valid_prime "p" p in let* () = valid_prime "q" q in let* () = guard (p <> q) (`Msg "p and q are the same number") in let* () = valid_e ~e ~p ~q in (* p and q are prime, and not equal -> multiplicative inverse exists *) let* () = guard Z.(q' = invert q p) (`Msg "q' <> q ^ -1 mod p") in let* () = guard Z.(n = p * q) (`Msg "modulus is not the product of p and q") in let* () = guard Z.(one < d && d < n) (`Msg "invalid private exponent") in let* () = guard Z.(dp = d mod (pred p)) (`Msg "dp <> d mod (p - 1)") in let* () = guard Z.(dq = d mod (pred q)) (`Msg "dq <> d mod (q - 1)") in (* e has been checked (valid_e) to be coprime to p-1 and q-1 -> muliplicative inverse exists *) let* () = guard Z.(one = d * e mod (lcm (pred p) (pred q))) (`Msg "1 <> d * e mod lcm (p - 1) (q - 1)") in Ok { e ; d ; n ; p ; q ; dp ; dq ; q' } let priv_of_primes ~e ~p ~q = let* () = valid_prime "p" p in let* () = valid_prime "q" q in let* () = guard (p <> q) (`Msg "p and q are the same prime") in let* () = valid_e ~e ~p ~q in let n = Z.(p * q) in let* _ = pub ~e ~n in (* valid_e checks e coprime to p-1 and q-1, a multiplicative inverse exists *) let d = Z.(invert e (lcm (pred p) (pred q))) in let dp = Z.(d mod (pred p)) and dq = Z.(d mod (pred q)) in (* above we checked that p and q both are primes and not equal -> there should be a multiplicate inverse *) let q' = Z.invert q p in (* does not need to check valid_priv, since it is valid by construction *) Ok { e; d; n; p; q; dp; dq; q' } (* Handbook of applied cryptography, 8.2.2 (i). *) let priv_of_exp ?g ?(attempts=100) ~e ~d ~n () = let* _ = pub ~e ~n in let* () = guard Z.(one < d && d < n) (`Msg "invalid private exponent") in let rec doit ~attempts = let factor s t = let rec go ax = function | 0 -> None | i' -> let ax2 = Z.(ax * ax mod n) in if Z.(ax <> one && ax <> pred n && ax2 = one) then Some ax else go ax2 (i' - 1) in Option.map Z.(gcd n &. pred) (go Z.(powm (Z_extra.gen ?g n) t n) s) in if attempts > 0 then let* s, t = Z_extra.strip_factor ~f:two Z.(e * d |> pred) in match s with | 0 -> Error (`Msg "invalid factor 0") | _ -> match factor s t with | None -> doit ~attempts:(attempts - 1) | Some p -> let q = Z.(div n p) in priv_of_primes ~e ~p:(max p q) ~q:(min p q) else Error (`Msg "attempts exceeded") in doit ~attempts let rec generate ?g ?(e = Z.(~$0x10001)) ~bits () = if bits < minimum_bits || e < three || (bits <= Z.numbits e || not (Z_extra.pseudoprime e)) then invalid_arg "Rsa.generate: e: %a, bits: %d" Z.pp_print e bits; let (pb, qb) = (bits / 2, bits - bits / 2) in let (p, q) = Z_extra.(prime ?g ~msb:2 pb, prime ?g ~msb:2 qb) in match priv_of_primes ~e ~p:(max p q) ~q:(min p q) with | Error _ -> generate ?g ~e ~bits () | Ok priv -> priv let pub_of_priv ({ e; n; _ } : priv) = { e ; n } let pub_bits ({ n; _ } : pub) = Z.numbits n and priv_bits ({ n; _ } : priv) = Z.numbits n type mask = [ `No | `Yes | `Yes_with of Mirage_crypto_rng.g ] let encrypt_unsafe ~key: ({ e; n } : pub) msg = Z.(powm msg e n) let decrypt_unsafe ~crt_hardening ~key:({ e; d; n; p; q; dp; dq; q'} : priv) c = let m1 = Z.(powm_sec c dp p) and m2 = Z.(powm_sec c dq q) in (* NOTE: neither erem, nor the multiplications (addition, subtraction) are guaranteed to be constant time by gmp *) let h = Z.(erem (q' * (m1 - m2)) p) in let m = Z.(h * q + m2) in (* counter Arjen Lenstra's CRT attack by verifying the signature. Since the public exponent is small, this is not very expensive. Mentioned again "Factoring RSA keys with TLS Perfect Forward Secrecy" (Weimer, 2015). *) if not crt_hardening || Z.(powm_sec m e n) = c then m else Z.(powm_sec c d n) let decrypt_blinded_unsafe ~crt_hardening ?g ~key:({ e; n; _} as key : priv) c = let r = until (rprime n) (fun _ -> Z_extra.gen_r ?g two n) in (* since r and n are coprime, there must be a multiplicative inverse *) let r' = Z.(invert r n) in let c' = Z.(powm_sec r e n * c mod n) in let x = decrypt_unsafe ~crt_hardening ~key c' in Z.(r' * x mod n) let (encrypt_z, decrypt_z) = let check_params n msg = if msg < two then invalid_arg "Rsa: message: %a" Z.pp_print msg; if n <= msg then raise Insufficient_key in (fun ~(key : pub) msg -> check_params key.n msg ; encrypt_unsafe ~key msg), (fun ~crt_hardening ~mask ~(key : priv) msg -> check_params key.n msg ; match mask with | `No -> decrypt_unsafe ~crt_hardening ~key msg | `Yes -> decrypt_blinded_unsafe ~crt_hardening ~key msg | `Yes_with g -> decrypt_blinded_unsafe ~crt_hardening ~g ~key msg ) let reformat out f msg = Z_extra.(of_octets_be msg |> f |> to_octets_be ~size:(out // 8)) let encrypt ~key = reformat (pub_bits key) (encrypt_z ~key) let decrypt ?(crt_hardening=false) ?(mask=`Yes) ~key = reformat (priv_bits key) (decrypt_z ~crt_hardening ~mask ~key) let bx00, bx01 = "\x00", "\x01" module PKCS1 = struct let min_pad = 8 (* XXX Generalize this into `Rng.samplev` or something. *) let generate_with ?g ~f n = let buf = Bytes.create n and k = let b = Mirage_crypto_rng.block g in (n // b * b) in let rec go nonce i j = if i = n then Bytes.unsafe_to_string buf else if j = k then go Mirage_crypto_rng.(generate ?g k) i 0 else match String.get_uint8 nonce j with | b when f b -> Bytes.set_uint8 buf i b ; go nonce (succ i) (succ j) | _ -> go nonce i (succ j) in go Mirage_crypto_rng.(generate ?g k) 0 0 let pad ~mark ~padding k msg = let pad = padding (k - String.length msg - 3 |> imax min_pad) in String.concat "" [ bx00 ; mark ; pad ; bx00 ; msg ] let unpad ~mark ~is_pad buf = let f = not &. is_pad in let i = ct_find_uint8 ~default:2 ~off:2 ~f buf in let c1 = String.get_uint8 buf 0 = 0x00 and c2 = String.get_uint8 buf 1 = mark and c3 = String.get_uint8 buf i = 0x00 and c4 = min_pad <= i - 2 in if c1 && c2 && c3 && c4 then Some (String.sub buf (i + 1) (String.length buf - i - 1)) else None let pad_01 = let padding size = String.make size '\xff' in pad ~mark:"\x01" ~padding let pad_02 ?g = pad ~mark:"\x02" ~padding:(generate_with ?g ~f:((<>) 0x00)) let unpad_01 = unpad ~mark:0x01 ~is_pad:((=) 0xff) let unpad_02 = unpad ~mark:0x02 ~is_pad:((<>) 0x00) let padded pad transform keybits msg = let n = keybits // 8 in let p = pad n msg in if String.length p = n then transform p else raise Insufficient_key let unpadded unpad transform keybits msg = if String.length msg = keybits // 8 then try unpad (transform msg) with Insufficient_key -> None else None let sig_encode ?(crt_hardening = true) ?mask ~key msg = padded pad_01 (decrypt ~crt_hardening ?mask ~key) (priv_bits key) msg let sig_decode ~key msg = unpadded unpad_01 (encrypt ~key) (pub_bits key) msg let encrypt ?g ~key msg = padded (pad_02 ?g) (encrypt ~key) (pub_bits key) msg let decrypt ?(crt_hardening = false) ?mask ~key msg = unpadded unpad_02 (decrypt ~crt_hardening ?mask ~key) (priv_bits key) msg let asn_of_hash, detect = let map = [ `MD5, "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10" ; `SHA1, "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14" ; `SHA224, "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c" ; `SHA256, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20" ; `SHA384, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30" ; `SHA512, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40" ] in (fun h -> List.assoc h map), (fun buf -> List.find_opt (fun (_, d) -> String.starts_with ~prefix:d buf) map) let sign ?(crt_hardening = true) ?mask ~hash ~key msg = let module H = (val Digestif.module_of_hash' (hash :> Digestif.hash')) in let module D = Digest_or(H) in let msg' = asn_of_hash hash ^ D.digest_or msg in sig_encode ~crt_hardening ?mask ~key msg' let verify ~hashp ~key ~signature msg = let (>>=) = Option.bind and (>>|) = Fun.flip Option.map in Option.value (sig_decode ~key signature >>= fun buf -> detect buf >>| fun (hash, asn) -> let module H = (val Digestif.module_of_hash' (hash :> Digestif.hash')) in let module D = Digest_or(H) in hashp hash && Eqaf.equal (asn ^ D.digest_or msg) buf) ~default:false let min_key hash = let module H = (val Digestif.module_of_hash' (hash :> Digestif.hash')) in (String.length (asn_of_hash hash) + H.digest_size + min_pad + 2) * 8 + 1 end module MGF1 (H : Digestif.S) = struct let repr n = let buf = Bytes.create 4 in Bytes.set_int32_be buf 0 n; Bytes.unsafe_to_string buf (* Assumes len < 2^32 * H.digest_size. *) let mgf ~seed len = let rec go acc c = function | 0 -> Bytes.sub (Bytes.concat Bytes.empty (List.rev acc)) 0 len | n -> let h = Bytes.create H.digest_size in H.get_into_bytes (H.feedi_string H.empty (iter2 seed (repr c))) h; go (h :: acc) Int32.(succ c) (pred n) in go [] 0l (len // H.digest_size) let mask ~seed buf = let mgf_data = mgf ~seed (String.length buf) in unsafe_xor_into buf ~src_off:0 mgf_data ~dst_off:0 (String.length buf); mgf_data end module OAEP (H : Digestif.S) = struct module MGF = MGF1 (H) let hlen = H.digest_size let max_msg_bytes k = k - 2 * hlen - 2 let eme_oaep_encode ?g ?(label = "") k msg = let seed = Mirage_crypto_rng.generate ?g hlen and pad = String.make (max_msg_bytes k - String.length msg) '\x00' in let db = String.concat "" [ H.(digest_string label |> to_raw_string) ; pad ; bx01 ; msg ] in let mdb = Bytes.unsafe_to_string (MGF.mask ~seed db) in let mseed = Bytes.unsafe_to_string (MGF.mask ~seed:mdb seed) in String.concat "" [ bx00 ; mseed ; mdb ] let eme_oaep_decode ?(label = "") msg = let b0 = String.sub msg 0 1 and ms = String.sub msg 1 hlen and mdb = String.sub msg (1 + hlen) (String.length msg - 1 - hlen) in let db = Bytes.unsafe_to_string (MGF.mask ~seed:(Bytes.unsafe_to_string (MGF.mask ~seed:mdb ms)) mdb) in let i = ct_find_uint8 ~default:0 ~off:hlen ~f:((<>) 0x00) db in let c1 = Eqaf.equal (String.sub db 0 hlen) H.(digest_string label |> to_raw_string) and c2 = String.get_uint8 b0 0 = 0x00 and c3 = String.get_uint8 db i = 0x01 in if c1 && c2 && c3 then Some (String.sub db (i + 1) (String.length db - i - 1)) else None let encrypt ?g ?label ~key msg = let k = pub_bits key // 8 in if String.length msg > max_msg_bytes k then raise Insufficient_key else encrypt ~key @@ eme_oaep_encode ?g ?label k msg let decrypt ?(crt_hardening = false) ?mask ?label ~key em = let k = priv_bits key // 8 in if String.length em <> k || max_msg_bytes k < 0 then None else try eme_oaep_decode ?label @@ decrypt ~crt_hardening ?mask ~key em with Insufficient_key -> None (* XXX Review rfc3447 7.1.2 and * http://archiv.infsec.ethz.ch/education/fs08/secsem/Manger01.pdf * again for timing properties. *) (* XXX expose seed for deterministic testing? *) end module PSS (H: Digestif.S) = struct module MGF = MGF1 (H) module H1 = Digest_or (H) let hlen = H.digest_size let bxbc = "\xbc" let b0mask embits = 0xff lsr ((8 - embits mod 8) mod 8) let zero_8 = String.make 8 '\x00' let digest ~salt msg = H.to_raw_string @@ H.digesti_string @@ iter3 zero_8 (H1.digest_or msg) salt let emsa_pss_encode ?g slen emlen msg = let n = emlen // 8 and salt = Mirage_crypto_rng.generate ?g slen in let h = digest ~salt msg in let db = String.concat "" [ String.make (n - slen - hlen - 2) '\x00' ; bx01 ; salt ] in let mdb = MGF.mask ~seed:h db in Bytes.set_uint8 mdb 0 @@ Bytes.get_uint8 mdb 0 land b0mask emlen ; String.concat "" [ Bytes.unsafe_to_string mdb ; h ; bxbc ] let emsa_pss_verify slen emlen em msg = let mdb = String.sub em 0 (String.length em - hlen - 1) and h = String.sub em (String.length em - hlen - 1) hlen and bxx = String.get_uint8 em (String.length em - 1) in let db = MGF.mask ~seed:h mdb in Bytes.set_uint8 db 0 (Bytes.get_uint8 db 0 land b0mask emlen) ; let db = Bytes.unsafe_to_string db in let salt = String.sub db (String.length db - slen) slen in let h' = digest ~salt:salt msg and i = ct_find_uint8 ~default:0 ~f:((<>) 0x00) db in let c1 = lnot (b0mask emlen) land String.get_uint8 mdb 0 = 0x00 and c2 = i = String.length em - hlen - slen - 2 and c3 = String.get_uint8 db i = 0x01 and c4 = bxx = 0xbc and c5 = Eqaf.equal h h' in c1 && c2 && c3 && c4 && c5 let sufficient_key ~slen kbits = hlen + slen + 2 <= kbits / 8 (* 8 * (hlen + slen + 1) + 2 <= kbits *) let sign ?g ?(crt_hardening = false) ?mask ?(slen = hlen) ~key msg = let b = priv_bits key in if not (sufficient_key ~slen b) then raise Insufficient_key else let msg' = emsa_pss_encode ?g (imax 0 slen) (b - 1) msg in decrypt ~crt_hardening ?mask ~key msg' let verify ?(slen = hlen) ~key ~signature msg = let b = pub_bits key and s = String.length signature in s = b // 8 && sufficient_key ~slen b && try let em = encrypt ~key signature in let to_see = s - (b - 1) // 8 in emsa_pss_verify (imax 0 slen) (b - 1) (String.sub em to_see (String.length em - to_see)) msg with Insufficient_key -> false end