From 71b9d591746e3b1a353f2f4020fff2358e2afcad Mon Sep 17 00:00:00 2001 From: swrup Date: Fri, 27 Feb 2026 23:36:05 +0100 Subject: [PATCH] use correct denomination hash --- src/amount.ml | 2 +- src/api.ml | 3 -- src/crypto.ml | 72 +++++++++++++++++++++++++++++++++----- src/denomination.ml | 4 +-- src/hash.ml | 12 +++---- src/http_management.ml | 2 +- src/keys.ml | 8 ++--- src/secmod_rsa.ml | 5 ++- src/signatures.ml | 2 +- test/offline_management.sh | 2 +- test/test_crypto.ml | 26 ++++++++++++-- test/validate_response.ml | 14 ++++++-- tools/offline.ml | 40 +++++++++++---------- tools/offline_impl.ml | 11 +++--- 14 files changed, 142 insertions(+), 61 deletions(-) diff --git a/src/amount.ml b/src/amount.ml index 3d8f6508..9b8b34ff 100644 --- a/src/amount.ml +++ b/src/amount.ml @@ -113,6 +113,6 @@ let bin = let open Bin in record make_exn |+ field beint64 (fun t -> t.value) - |+ field beint32 (fun t -> t.fraction) + |+ field beint32 (fun t -> Int32.mul t.fraction 1_000_000_l) |+ field (bytes currency_len) (fun t -> pad_currency_string t.currency) |> sealr diff --git a/src/api.ml b/src/api.ml index 8d2211df..062ca181 100644 --- a/src/api.ml +++ b/src/api.ml @@ -10,7 +10,6 @@ open Time open Crypto open Signatures -module DenominationHash = Hash.DenominationHash let encode jsont v = Jsont_bytesrw.encode_string jsont v let decode jsont v = Jsont_bytesrw.decode_string jsont v @@ -296,8 +295,6 @@ end module DenominationKey = struct type t = Rsa of RsaDenominationKey.t - let to_octets = function Rsa denom -> RsaPublicKey.to_octets denom.rsa_pub - let of_cs _v = Jsont.Error.msg Jsont.Meta.none "CSDenominationKey are not supported" diff --git a/src/crypto.ml b/src/crypto.ml index 2f42ae72..5402e12b 100644 --- a/src/crypto.ml +++ b/src/crypto.ml @@ -209,13 +209,12 @@ module RsaPublicKey = struct let of_octets = Binary_format_rsa.pub_of_octets let to_b32 t = B32.encode (to_octets t) - let jsont = - let of_b32 s = - let* s = B32.decode s in - let+ v = of_octets s in - v - in - Jsont.of_of_string ~kind:"RsaPublicKey" of_b32 ~enc:to_b32 + let of_b32 s = + let* s = B32.decode s in + let+ v = of_octets s in + v + + let jsont = Jsont.of_of_string ~kind:"RsaPublicKey" of_b32 ~enc:to_b32 let caqti : t Caqti_type.t = Caqti_type.custom @@ -257,6 +256,63 @@ module RsaSignature = struct Jsont.of_of_string ~kind:"RsaSignature" of_b32 ~enc:to_b32 end +module DenominationHash : sig + type t + + val bin : t Bin.t + val caqti : t Caqti_type.t + val jsont : t Jsont.t + val hash_of_rsa : RsaPublicKey.t -> t + val of_octets : string -> t + val to_octets : t -> string + val of_b32 : B32.t -> (t, string) result + val to_b32 : t -> B32.t +end = struct + open Digestif + + type t = SHA512.t + + type cipher = + | RSA + | CS [@ocaml.warning "-37"] + + (* = GNUNET_CRYPTO_BSA_(RSA|CS) *) + let cipher_to_int32 = function RSA -> 1_l | CS -> 2_l + + let hash_of_rsa pub = + let age_mask = 0_l in + let cipher = cipher_to_int32 RSA in + let pub = RsaPublicKey.to_octets pub in + let len = String.length pub in + let buf = Bytes.create (4 + 4 + len) in + Bytes.set_int32_be buf 0 age_mask; + Bytes.set_int32_be buf 4 cipher; + Bytes.blit_string pub 0 buf 8 len; + SHA512.digest_bytes buf + + let of_octets s = + match SHA512.of_raw_string_opt s with + | None -> Fmt.failwith "H64.of_octets failure" + | Some t -> t + + let to_octets = SHA512.to_raw_string + let of_b32 s = Result.map of_octets (B32.decode s) + let to_b32 t = B32.encode (to_octets t) + + let bin = + let open Bin in + map (bytes 64) of_octets to_octets + + let caqti = + let open Caqti_type in + custom + ~encode:(fun v -> Ok (to_octets v)) + ~decode:(fun v -> Ok (of_octets v)) + octets + + let jsont = Jsont.of_of_string ~kind:"DenominationHash" of_b32 ~enc:to_b32 +end + (* some type aliases, just for prettier .mli *) type eddsa_priv = EddsaPrivateKey.t type eddsa_pub = EddsaPublicKey.t @@ -264,4 +320,4 @@ type eddsa_sig = EddsaSignature.t type rsa_priv = RsaPrivateKey.t type rsa_pub = RsaPublicKey.t type rsa_sig = RsaSignature.t -type denom_hash = Hash.DenominationHash.t +type denom_hash = DenominationHash.t diff --git a/src/denomination.ml b/src/denomination.ml index e1aabbc8..fc5e1545 100644 --- a/src/denomination.ml +++ b/src/denomination.ml @@ -158,9 +158,7 @@ let denoms_of_denomgroups l = lost= _; } -> - let h_pub = - DenominationHash.hash (Crypto.RsaPublicKey.to_octets rsa_pub) - in + let h_pub = DenominationHash.hash_of_rsa rsa_pub in { pub= rsa_pub; value; diff --git a/src/hash.ml b/src/hash.ml index ef1f4e10..2e13ef35 100644 --- a/src/hash.ml +++ b/src/hash.ml @@ -10,6 +10,7 @@ module type S = sig val of_octets : string -> t val to_octets : t -> string val of_b32 : B32.t -> (t, string) result + val to_b32 : t -> B32.t end module H32 = struct @@ -24,6 +25,7 @@ module H32 = struct let to_octets = SHA256.to_raw_string let of_b32 s = Result.map of_octets (B32.decode s) + let to_b32 t = B32.encode (to_octets t) let bin = let open Bin in @@ -37,9 +39,7 @@ module H32 = struct ~decode:(fun v -> Ok (of_octets v)) octets - let jsont = - let enc v = B32.encode (to_octets v) in - Jsont.of_of_string ~kind:"Hash 32" of_b32 ~enc + let jsont = Jsont.of_of_string ~kind:"Hash 32" of_b32 ~enc:to_b32 end module H64 = struct @@ -54,6 +54,7 @@ module H64 = struct let to_octets = SHA512.to_raw_string let of_b32 s = Result.map of_octets (B32.decode s) + let to_b32 t = B32.encode (to_octets t) let bin = let open Bin in @@ -67,9 +68,7 @@ module H64 = struct ~decode:(fun v -> Ok (of_octets v)) octets - let jsont = - let enc v = B32.encode (to_octets v) in - Jsont.of_of_string ~kind:"Hash 64" of_b32 ~enc + let jsont = Jsont.of_of_string ~kind:"Hash 64" of_b32 ~enc:to_b32 end (* C-terminated strings @@ -92,7 +91,6 @@ end check which hash algorithm to use for each hash type *) module FullPaytoHash : S = H32 module NormalizedPaytoHash : S = H32 -module DenominationHash : S = H64 module PrivateContractHash : S = H64 module ExtensionsPolicyHash : S = H64 module MerchantWireHash : S = H64 diff --git a/src/http_management.ml b/src/http_management.ml index 04fc9a51..b62191a9 100644 --- a/src/http_management.ml +++ b/src/http_management.ml @@ -58,7 +58,7 @@ module Denom_revoke = struct Logs.info (fun m -> m "POST /management/denominations/$H_DENOM_PUB/revoke/"); let keys = Vif.Server.device Devices.keys server in let res = - let* h_denom_pub = DenominationHash.of_b32 h_denom_pub in + let* h_denom_pub = Crypto.DenominationHash.of_b32 h_denom_pub in let* v = Vif.Request.of_json req |> unwrap_err_msg in let* () = verify keys h_denom_pub v in let* () = do_ keys h_denom_pub v in diff --git a/src/keys.ml b/src/keys.ml index 09d6da82..45433c32 100644 --- a/src/keys.ml +++ b/src/keys.ml @@ -119,8 +119,7 @@ module Make (Conn : Pg.CONN) : S = struct let make_future_dn (h_pub, (section_name, pub, start)) = Logs.debug (fun m -> - m "make_future_dn: `%s`" - (B32.encode @@ Hash.DenominationHash.to_octets h_pub)); + m "make_future_dn: `%s`" (DenominationHash.to_b32 h_pub)); let open Time in let Config.Coin. { @@ -320,7 +319,7 @@ module Make (Conn : Pg.CONN) : S = struct let+ () = Pg.insert_denom conn dn |> unwrap_err_caqti in Logs.info (fun m -> m "certified denomination `%s`" - (B32.encode @@ Hash.DenominationHash.to_octets dn.h_pub)); + (DenominationHash.to_b32 dn.h_pub)); ()) let revoke_signkey pub revoked_sig = @@ -342,7 +341,6 @@ module Make (Conn : Pg.CONN) : S = struct |> unwrap_err_caqti in Logs.info (fun m -> - m "revoked denomination `%s`" - (B32.encode @@ Hash.DenominationHash.to_octets h_pub)); + m "revoked denomination `%s`" (DenominationHash.to_b32 h_pub)); () end diff --git a/src/secmod_rsa.ml b/src/secmod_rsa.ml index 226afccb..ef149b3e 100644 --- a/src/secmod_rsa.ml +++ b/src/secmod_rsa.ml @@ -7,7 +7,6 @@ module Log = (val Logs.src_log src : Logs.LOG) open Syntax open Crypto open Time -module DenominationHash = Hash.DenominationHash module Cfg = struct open Config @@ -125,7 +124,7 @@ let get_key_dir_contents dir_fpath = let gen_key ~section_name t1 t2 = let bits = Cfg.rsa_keysize ~section_name in let priv, pub = RsaPrivateKey.generate ~bits () in - let h_pub = DenominationHash.hash (RsaPublicKey.to_octets pub) in + let h_pub = DenominationHash.hash_of_rsa pub in Log.debug (fun m -> m "generated key (%a):@,`%s`" pp_filename (t1, t2) (DenominationHash.to_octets h_pub |> B32.encode)); @@ -179,7 +178,7 @@ let load_key ~section_name fpath = | Some (t1, t2) -> let+ priv = read_rsa fpath in let pub = RsaPrivateKey.pub_of_priv priv in - let h_pub = DenominationHash.hash (RsaPublicKey.to_octets pub) in + let h_pub = DenominationHash.hash_of_rsa pub in { section_name; priv; pub; h_pub; t1; t2 } let load_section section_name = diff --git a/src/signatures.ml b/src/signatures.ml index af9c7b6a..f727b5c2 100644 --- a/src/signatures.ml +++ b/src/signatures.ml @@ -2,7 +2,7 @@ open Time open Hash module Aliases = struct - (* - Keys - *) + module DenominationHash = Crypto.DenominationHash (* some of those are actuall ecdhe, or union of eddsa|ecdhe *) open Crypto diff --git a/test/offline_management.sh b/test/offline_management.sh index 606e6028..1bc6a542 100755 --- a/test/offline_management.sh +++ b/test/offline_management.sh @@ -29,7 +29,7 @@ offline_tool revoke-denom \ --output $b \ --rsa \ $rsa_pub -h_denom=$(dune exec offline -- hash64 $rsa_pub) +h_denom=$(dune exec offline -- denomination-hash $rsa_pub) offline_tool upload --input $b --url $url"/management/denominations/"$h_denom"/revoke" echo "[OK] /management/denominations/\$H_DENOM/revoke" diff --git a/test/test_crypto.ml b/test/test_crypto.ml index 13cac4df..7b4e7417 100644 --- a/test/test_crypto.ml +++ b/test/test_crypto.ml @@ -99,9 +99,8 @@ let () = "3KHKZJZ30ABB4E56MA2V0EQWGCWH0QQG9P2ZHYHR186C5HZXJMM4N9WXAQTKS94QSV9Y17GGNXN5MB1PZZFG7Q0FY88QPKKRG4MYCPSMTZK5W59R0MJVNJ4P4AQM96TDG5W7RV8GSNR1QQZ1GNHW3CX6D6ZRTMXB2NKB5SSYTDJS79F5ZFBRZ4HVED9JBBPWSR79KVV5QQ4APBGHBCKGMF9NJJS53A1BVYHDEVYAGFYF2SNEP827ZP50FKJ5GKGV8NQ15ESEZ69AT7GJG0T3TZVENY2YN9CVR98W3BKEZ53J7VTANARG8SJS8AMJQ7S23P5HRJ7XE9KTNRNXKH49MXV9JHHYE5535N7AGWEKR47SBCGNF44Z7XJ9RV5BQV12ZRJKN4HBZQHDNCMH3QKX9Z6G64" in let open Crypto in - let pub = - rsa_public_key |> decode |> RsaPublicKey.of_octets |> Result.get_ok - in + let pub = rsa_public_key |> RsaPublicKey.of_b32 |> Result.get_ok in + assert (RsaPublicKey.to_b32 pub = rsa_public_key); let bks = blinding_key_secret |> decode in let msg = decode message_hash in let bmsg = Fdh_rsa.blind_msg pub ~bks msg in @@ -123,3 +122,24 @@ let () = assert (Result.is_ok res); () + +let () = + (* denomination-hash *) + (* test value generated by using taler-typescript-core (taler-util/src/taler-crypto.ts) + at SUPPORTED_EXCHANGE_PROTOCOL_VERSION = "31:0:6" *) + let rsa_pub = + "040000Y0EAK8KAVYJ3GBT5EJHEE6PQ843PDRPRNBFTPEPQEYCXZ30M0S7B80303C75D1CMK74ZJT7WA7DGWTEK7C26J6GVPR93FX99BWX48HGSN8R9C2V104X6WECY6TYPDCAM3FXEVFVVE610P1C8DPV2GKGWYJ5K91A470CBZ00EASZ6A013B7CZ6SAF7MJS20TBYTVWHN7CPJFV5Q7Q0AXGED999E1G3JC3PY5W4MVC16J5SG2SPRJMW5YGM0N1C64NCZBDBTA9HW009TEV5NBCNRQ9GK54AEDY84SVEF4V43ZCPMQ6M3ZWZGQNBX8EF19NCKVQN7MMRCDCY7A9MVJSDEBRZDQ4ZKRVT2C35225KM660ZQ731B9ZYH9JQ35WQ93MKH4PH4AWCJ8EH38JJA93R73M704002" + in + let h_pub = + "X1WDZ01CAP3V8ZRQMXX1CCWNKYDMYYKA6RG3X61N7RBARX85QCDJCDEAEDZNFYC2J22P2C930A13BAGQNVZMFG4M1YM28TCS2WK0T6R" + in + let open Crypto in + let h_pub' = + RsaPublicKey.of_b32 rsa_pub + |> Result.get_ok + |> DenominationHash.hash_of_rsa + |> DenominationHash.to_b32 + in + assert (h_pub' = h_pub); + + () diff --git a/test/validate_response.ml b/test/validate_response.ml index aebda500..970a803f 100644 --- a/test/validate_response.ml +++ b/test/validate_response.ml @@ -74,8 +74,18 @@ let keys content = let denom_l = v.denominations |> Denomination.denoms_of_denomgroups in let* () = denom_l - |> list_iter - (Denomination.verify_denomination_key_validity ~key:v.master_public_key) + |> list_iter (fun dn -> + let open Denomination in + (*let to_int64 ts = ts |> Timestamp.to_s |> Option.get in*) + Fmt.pr "rsa_pub: %s@." (Crypto.RsaPublicKey.to_b32 dn.pub); + Fmt.pr "master_sig: %s@." (B32.encode @@ Obj.magic dn.master_sig); + (* amount bin fraction bad? *) + let* () = + Denomination.verify_denomination_key_validity ~key:v.master_public_key + dn + in + Fmt.pr "validated a denom@."; + Ok ()) in let* () = let last_issue_date = diff --git a/tools/offline.ml b/tools/offline.ml index 4aa05bd1..0a13ea34 100644 --- a/tools/offline.ml +++ b/tools/offline.ml @@ -59,6 +59,14 @@ module Arg = struct Fmt.pf fmt "%s" s in Arg.Conv.make ~docv:"eddsa public key argument" ~parser ~pp () + + let rsa_pub = + let parser s = Crypto.RsaPublicKey.of_b32 s in + let pp fmt key = + let s = Crypto.RsaPublicKey.to_b32 key in + Fmt.pf fmt "%s" s + in + Arg.Conv.make ~docv:"rsa public key argument" ~parser ~pp () end let master_key = @@ -147,11 +155,11 @@ let revoke_denom_cmd = match is_rsa_pub with | false -> Ok v | true -> ( - match B32.decode v with + match Crypto.RsaPublicKey.of_b32 v with | Error e -> Error e - | Ok s -> - let h = Hash.DenominationHash.hash s in - let s = B32.encode (Hash.DenominationHash.to_octets h) in + | Ok rsa_pub -> + let h = Crypto.DenominationHash.hash_of_rsa rsa_pub in + let s = Crypto.DenominationHash.to_b32 h in Ok s) in match res with @@ -159,21 +167,15 @@ let revoke_denom_cmd = | Ok h_denom -> revoke_denom ~output ~master_key ~h_denom (* just for tests... *) -let test_hash64_cmd = - let doc = - "Compute SHA-512, print output to stdout, input and output are \ - Crockford-base32 encoded" - in - let s = Arg.(required & pos 0 (some string) None & info []) in - Cmd.make (Cmd.info "hash64" ~doc) +let test_denomination_hash_cmd = + let doc = "compute denomination hash (of rsa)" in + let rsa_pub = Arg.(required & pos 0 (some rsa_pub) None & info []) in + Cmd.make (Cmd.info "denomination-hash" ~doc) @@ - let+ s = s in - match B32.decode s with - | Error e -> Error e - | Ok s -> - let h = Hash.DenominationHash.hash s in - let s = B32.encode (Hash.DenominationHash.to_octets h) in - Fmt.pr "%s@." s; Ok () + let+ rsa_pub = rsa_pub in + let h = Crypto.DenominationHash.hash_of_rsa rsa_pub in + let s = Crypto.DenominationHash.to_b32 h in + Fmt.pr "%s@." s; Ok () let revoke_signkey_cmd = let doc = "Revoke signkey." in @@ -367,7 +369,7 @@ let cli = disable_wire_cmd; drain_cmd; (* - *) - test_hash64_cmd; + test_denomination_hash_cmd; ] let main () = Cmd.eval_result cli diff --git a/tools/offline_impl.ml b/tools/offline_impl.ml index 6f0623c9..c2c890db 100644 --- a/tools/offline_impl.ml +++ b/tools/offline_impl.ml @@ -30,9 +30,10 @@ module Future_keys = struct fee_refund= _; denom_secmod_sig; } = - let h_denom_pub = - DenominationHash.hash (DenominationKey.to_octets denom_pub) + let rsa_pub = + match denom_pub with DenominationKey.Rsa denom -> denom.rsa_pub in + let h_denom_pub = DenominationHash.hash_of_rsa rsa_pub in let h_section_name = Hash.Cstring.H64.hash section_name in let anchor_time = stamp_start in let duration_withdraw = @@ -96,8 +97,10 @@ module Future_keys = struct fee_refund; denom_secmod_sig= _; } = - let octets = DenominationKey.to_octets denom_pub in - let h_denom_pub = DenominationHash.hash octets in + let rsa_pub = + match denom_pub with DenominationKey.Rsa denom -> denom.rsa_pub + in + let h_denom_pub = DenominationHash.hash_of_rsa rsa_pub in let master_sig = let open Signatures.DenominationKeyValidity in let master = EddsaPrivateKey.(pub_of_priv master_key) in