(* TODO don't trust any public keys keep trusted exchange/secmod keys either from config or TOFU (Trust On First Use) not done: /management/aml-officers (for /aml) /management/partners (for /wads) *) open Cmdliner open Cmdliner.Term.Syntax open Offline_impl module Arg = struct include Arg open Time (* in seconds *) let timestamp = let parser s = match int_of_string_opt s with | None -> Error "not an int" | Some n -> Ok (Timestamp.of_s (Int64.of_int n)) in let pp fmt t = match Timestamp.to_s t with | None -> Fmt.pf fmt "never" | Some s -> Fmt.pf fmt "%Ld" s in Arg.Conv.make ~docv:"timestamp argument" ~parser ~pp () (* in seconds *) let relative_time = let parser s = match int_of_string_opt s with | None -> Error "not an int" | Some s -> Ok (TimeRelative.of_s (Int64.of_int s)) in let pp fmt d = let t = Timestamp.of_absolute TimeAbsolute.(add zero d) in match Timestamp.to_s t with | None -> Fmt.pf fmt "never" | Some s -> Fmt.pf fmt "%Ld" s in Arg.Conv.make ~docv:"relative time argument" ~parser ~pp () let b32 = let pp fmt v = Fmt.pf fmt "%s" (B32.encode v) in Arg.Conv.make ~docv:"Crockford's Base32 encoded argument" ~parser:B32.decode ~pp () let amount = Arg.Conv.make ~docv:"amount argument" ~parser:Amount.of_string ~pp:Amount.pp () let eddsa_pub = let parser s = Crypto.EddsaPublicKey.of_b32 s in let pp fmt key = let s = Crypto.EddsaPublicKey.to_b32 key in 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 = let doc = "Master offline Eddsa private key file." in Arg.(required & opt (some file) None & info [ "master_key" ] ~doc) let input = let doc = "input file" in Arg.(required & opt (some file) None & info [ "i"; "input" ] ~doc) let output = let doc = "output file" in Arg.(required & opt (some filepath) None & info [ "o"; "output" ] ~doc) let url = let doc = "url" in Arg.(required & opt (some string) None & info [ "url" ] ~doc) let setup_cmd = let doc = "Generate offline master keys, write private and public key to file" in let output = let doc = "private key output file" in Arg.(required & opt (some filepath) None & info [ "o"; "output" ] ~doc) in let output_pubkey = let doc = "public key output file, default to --output parameter with a \".pub\" \ extension" in Arg.(value & opt (some filepath) None & info [ "output-pubkey" ] ~doc) in Cmd.make (Cmd.info "setup" ~doc) @@ let+ output = output and+ output_pubkey = output_pubkey in let output_pubkey = Option.value ~default:(output ^ ".pub") output_pubkey in setup ~output ~output_pubkey let download_cmd = let doc = "use curl to send a GET request to --url, write response body to --output" in Cmd.make (Cmd.info "download" ~doc) @@ let+ output = output and+ url = url in download ~output ~url let upload_cmd = let doc = "use curl to send a POST request to --url, with request body set to \ --input file content" in Cmd.make (Cmd.info "upload" ~doc) @@ let+ input = input and+ url = url in upload ~input ~url let sign_cmd = let doc = "Sign FutureKeysResponse." in Cmd.make (Cmd.info "sign" ~doc) @@ let+ input = input and+ output = output and+ master_key = master_key in sign ~input ~output ~master_key let revoke_denom_cmd = let doc = "Revoke denomination." in let is_rsa_pub = let doc = "interpret input string as a RSA public key (in Crockford-base32) \ instead of a denomination hash" in Arg.(value & flag & info [ "rsa" ] ~doc) in let v = let doc = "hash of denomination (or RSA public key if --rsa is set)" in Arg.(required & pos 0 (some string) None & info [] ~doc) in Cmd.make (Cmd.info "revoke-denom" ~doc) @@ let+ output = output and+ master_key = master_key and+ is_rsa_pub = is_rsa_pub and+ v = v in let res = match is_rsa_pub with | false -> Ok v | true -> ( match Crypto.RsaPublicKey.of_b32 v with | Error e -> Error e | 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 | Error e -> Error e | Ok h_denom -> revoke_denom ~output ~master_key ~h_denom (* just for tests... *) 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+ 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 let signkey = let doc = "public signing key" in Arg.(required & pos 0 (some eddsa_pub) None & info [] ~doc) in Cmd.make (Cmd.info "revoke-signkey" ~doc) @@ let+ output = output and+ master_key = master_key and+ signkey = signkey in revoke_signkey ~output ~master_key ~signkey let enable_auditor_cmd = let doc = "Enable auditor." in let auditor_url = Arg.(required & opt (some string) None & info [ "auditor_url" ]) in let auditor_name = Arg.(required & opt (some string) None & info [ "auditor_name" ]) in let auditor_pub = Arg.(required & opt (some eddsa_pub) None & info [ "auditor_pub" ]) in Cmd.make (Cmd.info "enable-auditor" ~doc) @@ let+ output = output and+ master_key = master_key and+ auditor_url = auditor_url and+ auditor_name = auditor_name and+ auditor_pub = auditor_pub in enable_auditor ~output ~master_key ~auditor_url ~auditor_name ~auditor_pub let disable_auditor_cmd = let doc = "Disable auditor." in let auditor_pub = Arg.(required & opt (some eddsa_pub) None & info [ "auditor_pub" ]) in Cmd.make (Cmd.info "disable-auditor" ~doc) @@ let+ output = output and+ master_key = master_key and+ auditor_pub = auditor_pub in disable_auditor ~output ~master_key ~auditor_pub let wire_fee_cmd = let doc = "Provides wire fee configuration." in let wire_method = Arg.(required & opt (some string) None & info [ "wire_method" ]) in let fee_start = Arg.(required & opt (some timestamp) None & info [ "fee_start" ]) in let fee_end = Arg.(required & opt (some timestamp) None & info [ "fee_end" ]) in let closing_fee = Arg.(required & opt (some amount) None & info [ "closing_fee" ]) in let wire_fee = Arg.(required & opt (some amount) None & info [ "wire_fee" ]) in Cmd.make (Cmd.info "wire-fee" ~doc) @@ let+ output = output and+ master_key = master_key and+ wire_method = wire_method and+ fee_start = fee_start and+ fee_end = fee_end and+ closing_fee = closing_fee and+ wire_fee = wire_fee in Offline_impl.wire_fee ~output ~master_key ~wire_method ~fee_start ~fee_end ~closing_fee ~wire_fee let global_fees_cmd = let doc = "Provides global fee configuration." in let start_date = Arg.(required & opt (some timestamp) None & info [ "start_date" ]) in let end_date = Arg.(required & opt (some timestamp) None & info [ "end_date" ]) in let history_fee = Arg.(required & opt (some amount) None & info [ "history_fee" ]) in let account_fee = Arg.(required & opt (some amount) None & info [ "account_fee" ]) in let purse_fee = Arg.(required & opt (some amount) None & info [ "purse_fee" ]) in let history_expiration = Arg.( required & opt (some relative_time) None & info [ "history_expiration" ]) in let purse_account_limit = Arg.(required & opt (some int) None & info [ "purse_account_limit" ]) in let purse_timeout = Arg.(required & opt (some relative_time) None & info [ "purse_timeout" ]) in Cmd.make (Cmd.info "global-fees" ~doc) @@ let+ output = output and+ master_key = master_key and+ start_date = start_date and+ end_date = end_date and+ history_fee = history_fee and+ account_fee = account_fee and+ purse_fee = purse_fee and+ history_expiration = history_expiration and+ purse_account_limit = purse_account_limit and+ purse_timeout = purse_timeout in global_fees ~output ~master_key ~start_date ~end_date ~history_fee ~account_fee ~purse_fee ~history_expiration ~purse_account_limit ~purse_timeout let enable_wire_cmd = let doc = "Enable wire method." in let payto_uri = Arg.(required & opt (some string) None & info [ "payto_uri" ]) in let bank_label = Arg.(value & opt (some string) None & info [ "bank_label" ]) in let priority = Arg.(value & opt (some int) None & info [ "priority" ]) in Cmd.make (Cmd.info "enable-wire" ~doc) @@ let+ output = output and+ master_key = master_key and+ payto_uri = payto_uri and+ bank_label = bank_label and+ priority = priority in enable_wire ~output ~master_key ~payto_uri ~bank_label ~priority let disable_wire_cmd = let doc = "Disable wire method." in let payto_uri = Arg.(required & opt (some string) None & info [ "payto_uri" ]) in Cmd.make (Cmd.info "disable-wire" ~doc) @@ let+ output = output and+ master_key = master_key and+ payto_uri = payto_uri in disable_wire ~output ~master_key ~payto_uri let drain_cmd = let doc = "Drain profits from the exchange. The actual drain requires running the \ `taler-exchange-drain` tool." in let debit_account_section = Arg.(required & opt (some string) None & info [ "debit_account_section" ]) in let credit_payto_uri = Arg.(required & opt (some string) None & info [ "credit_payto_uri" ]) in let wtid = Arg.(required & opt (some b32) None & info [ "wtid" ]) in let date = Arg.(required & opt (some timestamp) None & info [ "date" ]) in let amount = Arg.(required & opt (some amount) None & info [ "amount" ]) in Cmd.make (Cmd.info "drain" ~doc) @@ let+ output = output and+ master_key = master_key and+ debit_account_section = debit_account_section and+ credit_payto_uri = credit_payto_uri and+ wtid = wtid and+ date = date and+ amount = amount in drain ~output ~master_key ~debit_account_section ~credit_payto_uri ~wtid ~date ~amount let cli = let info = let doc = "MTE Offline CLI tool" in Cmd.info "mte-offline" ~doc in Cmd.group info [ setup_cmd; download_cmd; sign_cmd; upload_cmd; revoke_denom_cmd; revoke_signkey_cmd; enable_auditor_cmd; disable_auditor_cmd; wire_fee_cmd; global_fees_cmd; enable_wire_cmd; disable_wire_cmd; drain_cmd; (* - *) test_denomination_hash_cmd; ] let main () = Cmd.eval_result cli let () = if !Sys.interactive then () else exit (main ())