431 lines
16 KiB
OCaml
431 lines
16 KiB
OCaml
|
|
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
|