From 32966487daf25325312e0d57cba223f1077ee004 Mon Sep 17 00:00:00 2001 From: swrup Date: Fri, 28 Nov 2025 14:00:11 +0100 Subject: [PATCH] --- data/eddsa_master_offline | 1 + src/amount.ml | 21 ++++++++++ src/amount.mli | 6 +++ src/api.ml | 24 +++++++---- src/bin_type.ml | 68 +++++--------------------------- src/crypto.ml | 1 + tools/offline.ml | 83 ++++++++++++++++++++++++++++++++++++++- 7 files changed, 136 insertions(+), 68 deletions(-) create mode 100644 data/eddsa_master_offline diff --git a/data/eddsa_master_offline b/data/eddsa_master_offline new file mode 100644 index 00000000..3e6eca77 --- /dev/null +++ b/data/eddsa_master_offline @@ -0,0 +1 @@ +mÔ¯9Wàü]w×LèyÛ ãï[ººº¶ \㘊Ug( \ No newline at end of file diff --git a/src/amount.ml b/src/amount.ml index 563fc982..d208cfde 100644 --- a/src/amount.ml +++ b/src/amount.ml @@ -88,3 +88,24 @@ let of_string = (char '.' *> parse_int32) in fun s -> parse_string ~consume:Consume.All parse_t s |> Result.join + +let jsont = Jsont.of_of_string ~kind:"Amount" of_string ~enc:to_string +let currency_len = 12 + +let bin = + let open Bin in + record (fun _value _fraction _currency -> + (* no need to decode amount? *) + assert false) + |+ field neint64 (fun t -> t.value) + |+ field neint32 (fun t -> t.fraction) + |+ field (bytes currency_len) (fun t -> t.currency) + |> sealr + +let bin_nbo = + let open Bin in + record (fun _value _fraction _currency -> assert false) + |+ field beint64 (fun t -> t.value) + |+ field beint32 (fun t -> t.fraction) + |+ field (bytes currency_len) (fun t -> t.currency) + |> sealr diff --git a/src/amount.mli b/src/amount.mli index 180d74dd..e2572401 100644 --- a/src/amount.mli +++ b/src/amount.mli @@ -19,3 +19,9 @@ val make : val pp : Format.formatter -> t -> unit val to_string : t -> string val of_string : string -> (t, string) result +val currency_len : int +val jsont : t Jsont.t + +(* only for encoding *) +val bin : t Bin.t +val bin_nbo : t Bin.t diff --git a/src/api.ml b/src/api.ml index 988c2e4d..eec54a6f 100644 --- a/src/api.ml +++ b/src/api.ml @@ -29,23 +29,33 @@ module ErrorDetail = struct |> Jsont.Object.finish end -module HashCode = struct +module HashCode : sig + type t + + val hash : string -> t + val jsont : t Jsont.t +end = struct type t = B32.t + let hash s = + let open Digestif.SHA256 in + s |> digest_string |> to_raw_string + let jsont = Jsont.of_of_string ~kind:"HashCode" B32.decode ~enc:B32.encode end -module Amount = struct - include Amount - - let jsont = Jsont.of_of_string ~kind:"Amount" of_string ~enc:to_string -end - module Timestamp = struct type t = | Seconds of float | Never + (* TODO time + - time.ml + - check this + - what value for never? + - better way for conversion to bin_types? *) + let to_int64 t = match t with Never -> 0_L | Seconds v -> Int64.of_float v + let to_ptime t = Util.ptime_of_int64 @@ to_int64 t let of_ptime p = Seconds (Ptime.to_float_s p) let number_or_never_jsont = diff --git a/src/bin_type.ml b/src/bin_type.ml index c2a0ffc6..9660064f 100644 --- a/src/bin_type.ml +++ b/src/bin_type.ml @@ -65,13 +65,15 @@ module INT64 = struct let bin = Bin.neint64 let of_ptime v = Util.ptime_to_int64 v + let of_int64 i = i end module INT64_NBO = struct type t = int64 let bin = Bin.beint64 - let of_ptime = Util.ptime_to_int64 + let of_ptime v = Util.ptime_to_int64 v + let of_int64 i = i end (* -- Time -- *) @@ -80,6 +82,7 @@ module type Time_S = sig val bin : t Bin.t val of_ptime : Ptime.t -> t + val of_int64 : int64 -> t end module TimeAbsolute : Time_S = INT64 @@ -232,67 +235,14 @@ module AgeMask = struct record (fun mask -> { mask }) |+ field beint32 (fun t -> t.mask) |> sealr end -(* TODO - - why is the non-NBO version only used in TALER_WithdrawRequestPS? - - correctly do the padding and 0-termination - - handle "invalid" values *) -(* documentation: *) -(* Number of characters (plus 1 for 0-termination) for currency names. - typically an ISO 4217 currency code when an alphanumeric 3-digit code is used. - For regional currencies, the first character should be a "*" followed - by a region-specific name (i.e. "*BRETAGNEFR"). - Currency codes are compared case-insensitively. - - Currency string, left adjusted and padded with zeros. - All zeros for "invalid" values. - - Name of the currency, using either a three-character ISO 4217 currency - code, or a regional currency identifier between 4 and 11 characters, - consisting of ASCII alphabetic characters ("a-zA-Z"). - Should be padded to 12 bytes with 0-characters. - Currency codes are compared case-insensitively. *) -let currency_len = 12 - -(* TODO missing doc - found in src/include/taler/taler_amount_lib.h *) -module AmountP = struct - type t = { - value: int64; - fraction: int32; - currency: string; - } - - (* TODO BE here? *) - let bin = - let open Bin in - record (fun value fraction currency -> { value; fraction; currency }) - |+ field beint64 (fun t -> t.value) - |+ field beint32 (fun t -> t.fraction) - |+ field (bytes currency_len) (fun t -> t.currency) - |> sealr -end - -module AmountNBO = struct - type t = { - value: int64; - fraction: int32; - currency: string; - } - - let bin = - let open Bin in - record (fun value fraction currency -> { value; fraction; currency }) - |+ field beint64 (fun t -> t.value) - |+ field beint32 (fun t -> t.fraction) - |+ field (bytes currency_len) (fun t -> t.currency) - |> sealr -end - (* TODO keep this? some of those are actuall ecdhe, or union of eddsa|ecdhe *) module Aliases = struct - (* TODO add Amount.bin *) - module Amount = AmountP + module AmountNBO = struct + type t = Amount.t + + let bin = Amount.bin_nbo + end (* - Keys - *) module PursePublicKey = EddsaPublicKey diff --git a/src/crypto.ml b/src/crypto.ml index b6e99c94..1e2f7e2c 100644 --- a/src/crypto.ml +++ b/src/crypto.ml @@ -32,6 +32,7 @@ module EddsaPrivateKey = struct type t = priv + let pub_of_priv = pub_of_priv let to_octets t = priv_to_octets t let of_octets t = priv_of_octets t |> Result.get_ok let bin = Bin.map (Bin.bytes 32) of_octets to_octets diff --git a/tools/offline.ml b/tools/offline.ml index c127e643..c76c3651 100644 --- a/tools/offline.ml +++ b/tools/offline.ml @@ -12,6 +12,66 @@ let download ~base_url ~output = let wget = Cmd.(v "wget" % "-O" % output % uri) in OS.Cmd.run wget +let setup ~output = + let output = Fpath.v output in + let () = Mirage_crypto_rng_unix.use_default () in + let priv, _pub = Mirage_crypto_ec.Ed25519.generate () in + let priv_data = Mirage_crypto_ec.Ed25519.priv_to_octets priv in + Bos.OS.File.write output priv_data + +let _denom_signature master_priv master_pub + Api.FutureDenom. + { + section_name= _; + value; + stamp_start; + stamp_expire_withdraw; + stamp_expire_deposit; + stamp_expire_legal; + denom_pub; + fee_withdraw; + fee_deposit; + fee_refresh; + fee_refund= _; + (* TODO check sigs *) + denom_secmod_sig= _; + } = + let pub_octets = + let denom_pub = + match denom_pub with + | Rsa v -> v + | CS _ -> Fmt.failwith "CS key types are not supported." + in + let pub = denom_pub.Api.RsaDenominationKey.rsa_pub in + Crypto.RsaPublicKey.to_octets pub + in + let h_denom_pub = Api.HashCode.hash pub_octets in + let master_sig = + let open Bin_type in + let open Bin_signature.DenominationKeyValidityPS in + (* TODO time *) + let of_time ts : TimeAbsoluteNBO.t = + TimeAbsoluteNBO.of_int64 @@ Api.Timestamp.to_int64 ts + in + let denom_key_validity = + { + master= master_pub; + start= of_time stamp_start; + expire_withdraw= of_time stamp_expire_withdraw; + expire_spend= of_time stamp_expire_deposit; + expire_legal= of_time stamp_expire_legal; + value; + fee_withdraw; + fee_deposit; + fee_refresh; + denom_hash= DenominationHash.hash pub_octets; + } + in + let data = Bin.to_string bin denom_key_validity in + Crypto.EddsaSignature.sign ~key:master_priv data + in + Api.DenomSignature.{ h_denom_pub; master_sig } + let sign ~input ~output ~master_key = let open Syntax in let open Bos.OS in @@ -31,6 +91,24 @@ let sign ~input ~output ~master_key = open Cmdliner open Cmdliner.Term.Syntax +let setup_cmd = + let doc = "Generate offline master keys" in + let output = + let doc = "Output file for the generated private Eddsa key." in + Arg.( + value + & opt filepath "data/eddsa_master_offline" + & info [ "o"; "output" ] ~doc) + in + Cmd.make (Cmd.info "setup" ~version:"%%VERSION%%" ~doc) + @@ + let+ output = output in + match setup ~output with + | Error (`Msg err) -> + Fmt.epr "Setup failure: %s@." err; + exit 1 + | Ok () -> () + let download_cmd = let doc = "Downloads /management/keys/" in let man = @@ -82,6 +160,7 @@ let sign_cmd = exit 1 | Ok () -> () -let main () = Cmd.eval download_cmd -let _main () = Cmd.eval sign_cmd +let main () = Cmd.eval setup_cmd +let _main () = Cmd.eval download_cmd +let __main () = Cmd.eval sign_cmd let () = if !Sys.interactive then () else exit (main ())