open Syntax open Crypto open Hash module Future_keys = struct open Api let verify offline_master_public_key FutureKeysResponse. { future_denoms; future_signkeys; master_pub; denom_secmod_public_key; signkey_secmod_public_key; } = let verify_future_denom ~sm_denom_pub 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= _; denom_secmod_sig; } = let h_denom_pub = DenominationHash.hash (DenominationKey.to_octets denom_pub) in let h_section_name = Hash.Cstring.H64.hash section_name in let anchor_time = stamp_start in let duration_withdraw = Timestamp.diff stamp_start stamp_expire_withdraw in let open Signatures.DenominationKeyAnnouncement in verify sm_denom_pub denom_secmod_sig { h_denom_pub; h_section_name; anchor_time; duration_withdraw } in let verify_future_signkey ~sm_signkey_pub FutureSignKey. { key; stamp_start; stamp_expire; stamp_end= _; signkey_secmod_sig } = let exchange_pub = key in let anchor_time = stamp_start in let duration = Timestamp.diff stamp_start stamp_expire in let open Signatures.SigningKeyAnnouncement in verify sm_signkey_pub signkey_secmod_sig { exchange_pub; anchor_time; duration } in let* () = match master_pub = offline_master_public_key with | false -> Fmt.error "master public key of the future key response does not match ours" | true -> Ok () in let* () = list_iter (verify_future_denom ~sm_denom_pub:denom_secmod_public_key) future_denoms in let* () = list_iter (verify_future_signkey ~sm_signkey_pub:signkey_secmod_public_key) future_signkeys in Ok () let make ~master_key FutureKeysResponse. { future_denoms; future_signkeys; master_pub= _; denom_secmod_public_key= _; signkey_secmod_public_key= _; } = let denom_signature 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; denom_secmod_sig= _; } = let octets = DenominationKey.to_octets denom_pub in let h_denom_pub = DenominationHash.hash octets in let master_sig = let open Signatures.DenominationKeyValidity in let master = EddsaPrivateKey.(pub_of_priv master_key) in signf (EddsaSignature.sign ~key:master_key) { master; start= stamp_start; expire_withdraw= stamp_expire_withdraw; expire_spend= stamp_expire_deposit; expire_legal= stamp_expire_legal; value; fee_withdraw; fee_deposit; fee_refresh; fee_refund; denom_hash= h_denom_pub; } in DenomSignature.{ h_denom_pub; master_sig } in let signkey_signature FutureSignKey. { key; stamp_start; stamp_expire; stamp_end; signkey_secmod_sig= _ } = let master_sig = let open Signatures.ExchangeSigningKeyValidity in signf (EddsaSignature.sign ~key:master_key) { start= stamp_start; expire= stamp_expire; end_= stamp_end; signkey_pub= key; } in SignKeySignature.{ key; master_sig } in let denom_sigs = List.map denom_signature future_denoms in let signkey_sigs = List.map signkey_signature future_signkeys in MasterSignatures.{ denom_sigs; signkey_sigs } end (* -- *) let read_file fname = Bos.OS.File.read (Fpath.v fname) |> unwrap_err_msg let write_file fname content = Bos.OS.File.write (Fpath.v fname) content |> unwrap_err_msg let read_master_key_file filename = let* master_key = read_file filename in EddsaPrivateKey.of_octets master_key let download ~output ~url = let open Bos in OS.Cmd.run Cmd.( v "curl" % "--silent" % "--show-error" % "--fail" % "-o" % output % "-X" % "GET" % url) |> unwrap_err_msg let upload ~input ~url = let open Bos in OS.Cmd.run Cmd.( v "curl" % "--silent" % "--show-error" % "--fail" % "-i" % "-X" % "POST" % "-H" % "Content-Type: application/json" % "--data" % ("@" ^ input) % url) |> unwrap_err_msg let setup ~output ~output_pubkey = Mirage_crypto_rng_unix.use_default (); let priv, pub = Mirage_crypto_ec.Ed25519.generate () in let priv_data = Mirage_crypto_ec.Ed25519.priv_to_octets priv in let* () = write_file output priv_data in let pub_data = Mirage_crypto_ec.Ed25519.pub_to_octets pub |> B32.encode in let* () = write_file output_pubkey pub_data in Ok () let sign ~master_key ~input ~output = let* master_key = read_master_key_file master_key in let* input = read_file input in let master_pub = EddsaPrivateKey.pub_of_priv master_key in let* future_keys_response = Api.decode Api.FutureKeysResponse.jsont input in let* () = Future_keys.verify master_pub future_keys_response in let master_signatures = Future_keys.make ~master_key future_keys_response in let* s = Api.encode Api.MasterSignatures.jsont master_signatures in let* () = write_file output s in Ok () let revoke_denom ~output ~master_key ~h_denom = let* key = read_master_key_file master_key in let* h_denom_pub = DenominationHash.of_b32 h_denom in let denom_revoke = let master_sig = let open Signatures.MasterDenominationKeyRevocation in signf (EddsaSignature.sign ~key) { h_denom_pub } in Api.DenomRevocationSignature.{ master_sig } in let* s = Api.encode Api.DenomRevocationSignature.jsont denom_revoke in let* () = write_file output s in Ok () let revoke_signkey ~output ~master_key ~signkey = let* key = read_master_key_file master_key in let signkey_revoke = let master_sig = let open Signatures.MasterSigningKeyRevocation in signf (EddsaSignature.sign ~key) { exchange_pub= signkey } in Api.SignkeyRevocationSignature.{ master_sig } in let* s = Api.encode Api.SignkeyRevocationSignature.jsont signkey_revoke in let* () = write_file output s in Ok () let global_fees ~output ~master_key ~start_date ~end_date ~history_fee ~account_fee ~purse_fee ~history_expiration ~purse_account_limit ~purse_timeout = let* key = read_master_key_file master_key in let* purse_account_limit = match purse_account_limit >= 0 && purse_account_limit <= Int32.to_int Int32.max_int with | false -> Error "invalid purse_account_limit value" | true -> Ok (Int32.of_int purse_account_limit) in let master_sig = let open Signatures.GlobalFees in signf (EddsaSignature.sign ~key) { start_date; end_date; purse_timeout; history_expiration; history_fee; account_fee; purse_fee; purse_account_limit; } in let global_fees = Api.GlobalFees. { start_date; end_date; purse_timeout; history_expiration; history_fee; account_fee; purse_fee; purse_account_limit; master_sig; } in let* s = Api.encode Api.GlobalFees.jsont global_fees in let* () = write_file output s in Ok () let enable_auditor ~output ~master_key ~auditor_url ~auditor_name ~auditor_pub ~validity_start = let* key = read_master_key_file master_key in let master_sig = let open Signatures.MasterAddAuditor in signf (EddsaSignature.sign ~key) { start_date= validity_start; auditor_pub; h_auditor_url= Hash.Cstring.H64.hash auditor_url; } in let v = Api.AuditorSetupMessage. { auditor_url; auditor_name; auditor_pub; master_sig; validity_start } in let* s = Api.encode Api.AuditorSetupMessage.jsont v in let* () = write_file output s in Ok () let disable_auditor ~output ~master_key ~auditor_pub ~validity_end = let* key = read_master_key_file master_key in let master_sig = let open Signatures.MasterDelAuditor in signf (EddsaSignature.sign ~key) { end_date= validity_end; auditor_pub } in let v = Api.AuditorTeardownMessage.{ master_sig; validity_end } in let* s = Api.encode Api.AuditorTeardownMessage.jsont v in let* () = write_file output s in Ok () let wire_fee ~output ~master_key ~wire_method ~fee_start ~fee_end ~closing_fee ~wire_fee = let* key = read_master_key_file master_key in let master_sig_wire = let open Signatures.MasterWireFee in signf (EddsaSignature.sign ~key) { h_wire_method= Hash.Cstring.H64.hash wire_method; start_date= fee_start; end_date= fee_end; closing_fee; wire_fee; } in let v = Api.WireFeeSetupMessage. { wire_method; fee_start; fee_end; closing_fee; wire_fee; master_sig_wire; } in let* s = Api.encode Api.WireFeeSetupMessage.jsont v in let* () = write_file output s in Ok () let drain ~output ~master_key ~debit_account_section ~credit_payto_uri ~wtid ~date ~amount = let* key = read_master_key_file master_key in let master_sig = let open Signatures.MasterDrainProfit in signf (EddsaSignature.sign ~key) { wtid; date; amount; h_section= Hash.Cstring.H64.hash debit_account_section; h_payto= FullPaytoHash.hash credit_payto_uri; } in let v = Api.DrainProfitsMessage. { debit_account_section; credit_payto_uri; wtid; master_sig; date; amount; } in let* s = Api.encode Api.DrainProfitsMessage.jsont v in let* () = write_file output s in Ok ()