diff --git a/src/crypto.ml b/src/crypto.ml index 4ce582f9..bc42c580 100644 --- a/src/crypto.ml +++ b/src/crypto.ml @@ -274,104 +274,3 @@ type rsa_priv = RsaPrivateKey.t type rsa_pub = RsaPublicKey.t type rsa_sig = RsaSignature.t type denom_hash = Hash.DenominationHash.t - -(* WIP *) -module FDH_RSA = struct - open Mirage_crypto_pk - - module Kdf = struct - module XTR = Hkdf.Make (Digestif.SHA512) - module PRF = Hkdf.Make (Digestif.SHA256) - - let kdf = - fun ~xts ~ikm ~ctx ~len -> - let prk = XTR.extract ~salt:xts ikm in - let okm = PRF.expand ~prk ~info:ctx len in - okm - - let kdf_mod_n ~n ~xts ~ikm ~ctx = - let nbits = Z.numbits n in - let len = ((nbits - 1) / 8) + 1 in - assert (8 * len = nbits); - let rec go ctr = - (* cat ctx ctr_be *) - let ctx = - let ctx_len = String.length ctx in - let b = Bytes.create (ctx_len + 2) in - Bytes.blit_string ctx 0 b 0 ctx_len; - Bytes.set_uint16_be b ctx_len ctr; - Bytes.unsafe_to_string b - in - let okm = kdf ~xts ~ikm ~ctx ~len in - assert (String.length okm = len); - let r = Z_extra.of_octets_be okm in - if Z.gt r n then go (succ ctr) else r - in - go 0 - end - - let gcd_validate r n = - match Z.equal (Z.gcd r n) Z.one with - | true -> () - | false -> Fmt.failwith "RSA key is malicious" - - let rsa_full_domain_hash pub msg = - let xts = RsaPublicKey.to_octets pub in - let ctx = "RSA-FDA FTpsW!" in - let r = Kdf.kdf_mod_n ~n:pub.n ~xts ~ikm:msg ~ctx in - gcd_validate r pub.n; r - - let rsa_blinding_key_derive (pub : RsaPublicKey.t) bks = - let xts = "Blinding KDF extractor HMAC key" in - let ctx = "Blinding KDF" in - let r = Kdf.kdf_mod_n ~n:pub.n ~xts ~ikm:bks ~ctx in - gcd_validate r pub.n; r - - let rsa_blind pub ~bks ~msg = - let data = rsa_full_domain_hash pub msg in - let bkey = rsa_blinding_key_derive pub bks in - (* can we just use [powm] here instead? *) - let r_e = Z.powm_sec bkey pub.e pub.n in - let data_r_e = Z.rem (Z.mul data r_e) pub.n in - Z_extra.to_octets_be data_r_e - - (* -- WIP crypto -- *) - - (* TODO crypto - not sure about signature scheme used by taler - libgnunetutil crypto_rsa.c use "(flags raw)" => no padding *) - (* decrypt <=> sign *) - let rsa_sign_z priv r = - let data = Z_extra.to_octets_be r in - Rsa.decrypt ~crt_hardening:true ~key:priv data - - (* TODO crypto - look into mirage-crypto for this - use Eqaf for constant time string compare *) - let rsa_verify_z pub r sig_ = - let data = Z_extra.to_octets_be r in - let sig_' = Rsa.encrypt ~key:pub data in - match String.equal sig_ sig_' with - | false -> Fmt.error "RSA signature verification failed" - | true -> Ok () - - let rsa_sign_fdh priv msg = - let pub = Rsa.pub_of_priv priv in - let r = rsa_full_domain_hash pub msg in - rsa_sign_z priv r - - let rsa_unblind pub ~bks ~sig_ = - let bkey = rsa_blinding_key_derive pub bks in - let r_inv = - try Z.invert bkey pub.n - with Division_by_zero -> - (* => gcd(r,n) <> 1, should be already checked for *) - assert false - in - let ubsig = Z.rem (Z.mul sig_ r_inv) pub.n in - ubsig - - let rsa_verify pub ~msg ~sig_ = - let r = rsa_full_domain_hash pub msg in - rsa_verify_z pub r sig_ -end diff --git a/src/fdh_rsa.ml b/src/fdh_rsa.ml new file mode 100644 index 00000000..692ecd30 --- /dev/null +++ b/src/fdh_rsa.ml @@ -0,0 +1,92 @@ +(* WIP + full-domain-hash RSA + based on libgnunetutil crypto_rsa.c + + good explanation of taler crypto: + https://www.taler.net/papers/cs-thesis.pdf *) + +module Z_extra = Mirage_crypto_pk.Z_extra +open Crypto + +module Kdf = struct + module XTR = Hkdf.Make (Digestif.SHA512) + module PRF = Hkdf.Make (Digestif.SHA256) + + let kdf = + fun ~xts ~ikm ~ctx ~len -> + let prk = XTR.extract ~salt:xts ikm in + let okm = PRF.expand ~prk ~info:ctx len in + okm + + let kdf_mod_n ~n ~xts ~ikm ~ctx = + let nbits = Z.numbits n in + let len = ((nbits - 1) / 8) + 1 in + assert (8 * len = nbits); + let rec go ctr = + (* cat ctx ctr_be *) + let ctx = + let ctx_len = String.length ctx in + let b = Bytes.create (ctx_len + 2) in + Bytes.blit_string ctx 0 b 0 ctx_len; + Bytes.set_uint16_be b ctx_len ctr; + Bytes.unsafe_to_string b + in + let okm = kdf ~xts ~ikm ~ctx ~len in + assert (String.length okm = len); + let r = Z_extra.of_octets_be okm in + if Z.gt r n then go (succ ctr) else r + in + go 0 +end + +let gcd_validate r n = + match Z.equal (Z.gcd r n) Z.one with + | true -> () + | false -> Fmt.failwith "RSA key is malicious" + +let rsa_full_domain_hash pub msg = + let xts = RsaPublicKey.to_octets pub in + let ctx = "RSA-FDA FTpsW!" in + let r = Kdf.kdf_mod_n ~n:pub.n ~xts ~ikm:msg ~ctx in + gcd_validate r pub.n; r + +let rsa_blinding_key_derive (pub : RsaPublicKey.t) bks = + let xts = "Blinding KDF extractor HMAC key" in + let ctx = "Blinding KDF" in + let r = Kdf.kdf_mod_n ~n:pub.n ~xts ~ikm:bks ~ctx in + gcd_validate r pub.n; r + +let rsa_blind pub ~bks ~msg = + let data = rsa_full_domain_hash pub msg in + let bkey = rsa_blinding_key_derive pub bks in + (* can we just use [powm] here instead? *) + let r_e = Z.powm_sec bkey pub.e pub.n in + let data_r_e = Z.rem (Z.mul data r_e) pub.n in + Z_extra.to_octets_be data_r_e + +(* decrypt <=> sign *) +let rsa_sign_blinded priv ~bmsg = + Mirage_crypto_pk.Rsa.decrypt ~crt_hardening:true ~key:priv bmsg + +let rsa_unblind pub ~bks ~sig_ = + let data = Z_extra.of_octets_be sig_ in + let bkey = rsa_blinding_key_derive pub bks in + let r_inv = + try Z.invert bkey pub.n + with Division_by_zero -> + (* => gcd(r,n) <> 1, should be already checked for *) + assert false + in + let data = Z.rem (Z.mul data r_inv) pub.n in + Z_extra.to_octets_be data + +(* TODO crypto + look into mirage-crypto for this + at least use Eqaf for constant time string compare *) +let rsa_verify pub ~msg ~sig_ = + let msg_fdh = rsa_full_domain_hash pub msg in + let s1 = Z_extra.to_octets_be msg_fdh in + let s2 = Mirage_crypto_pk.Rsa.encrypt ~key:pub sig_ in + match String.equal s1 s2 with + | false -> Fmt.error "RSA signature verification failed" + | true -> Ok () diff --git a/test/test_crypto.ml b/test/test_crypto.ml index 404db053..d146a633 100644 --- a/test/test_crypto.ml +++ b/test/test_crypto.ml @@ -77,7 +77,7 @@ let () = let xts = salt |> decode in let ikm = ikm |> decode in let ctx = ctx |> decode in - let okm = Crypto.FDH_RSA.Kdf.kdf ~xts ~ikm ~ctx ~len:out_len in + let okm = Fdh_rsa.Kdf.kdf ~xts ~ikm ~ctx ~len:out_len in let okm = okm |> encode in assert (okm = out); @@ -99,12 +99,12 @@ let () = "3KHKZJZ30ABB4E56MA2V0EQWGCWH0QQG9P2ZHYHR186C5HZXJMM4N9WXAQTKS94QSV9Y17GGNXN5MB1PZZFG7Q0FY88QPKKRG4MYCPSMTZK5W59R0MJVNJ4P4AQM96TDG5W7RV8GSNR1QQZ1GNHW3CX6D6ZRTMXB2NKB5SSYTDJS79F5ZFBRZ4HVED9JBBPWSR79KVV5QQ4APBGHBCKGMF9NJJS53A1BVYHDEVYAGFYF2SNEP827ZP50FKJ5GKGV8NQ15ESEZ69AT7GJG0T3TZVENY2YN9CVR98W3BKEZ53J7VTANARG8SJS8AMJQ7S23P5HRJ7XE9KTNRNXKH49MXV9JHHYE5535N7AGWEKR47SBCGNF44Z7XJ9RV5BQV12ZRJKN4HBZQHDNCMH3QKX9Z6G64" in let open Crypto in - let msg = message_hash |> decode in let pub = rsa_public_key |> decode |> RsaPublicKey.of_octets |> Result.get_ok in let bks = blinding_key_secret |> decode in - let s = FDH_RSA.rsa_blind pub ~bks ~msg |> encode in - assert (s = blinded_message); + let msg = decode message_hash in + let bmsg = Fdh_rsa.rsa_blind pub ~bks ~msg in + assert (encode bmsg = blinded_message); ()