(* TODO - clean up cmdliner - don't depends on wget - setup: need public key in config file - check sigs *) (* doc: https://docs.taler.net/manpages/taler-exchange-offline.1.html *) let version = "%%VERSION%%" let download ~base_url ~output = let open Bos in let uri = Uri.with_path (Uri.of_string base_url) "/management/keys/" |> Uri.to_string in let wget = Cmd.(v "wget" % "-O" % output % uri) in OS.Cmd.run wget let upload ~base_url ~input = let open Bos in let uri = Uri.with_path (Uri.of_string base_url) "/management/keys/" |> Uri.to_string in let wget = Cmd.( v "wget" % "--method=POST" % "--header='Content-Type: application/json'" % "--body-file" % input % "-O" % "/dev/null" % 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 Fmt.pr "generated master public key:@\n%s@." (Mirage_crypto_ec.Ed25519.pub_to_octets pub |> B32.encode); let priv_data = Mirage_crypto_ec.Ed25519.priv_to_octets priv in Bos.OS.File.write output priv_data let sign ~input ~output ~master_key = let open Syntax in let open Bos.OS in let open Crypto in let input = Fpath.v input in let output = Fpath.v output in let master_key = Fpath.v master_key in let* input = File.read input |> unwrap_err_msg in let* master_key = File.read master_key |> unwrap_err_msg in let master_key = EddsaPrivateKey.of_octets master_key in let* future_key_response = Api.decode Api.FutureKeysResponse.jsont input in let master_signatures = Offline_signature.master_signatures ~master_key future_key_response in let* s = Api.encode Api.MasterSignatures.jsont master_signatures in let* () = File.write output s |> unwrap_err_msg in Ok () (* CLI *) 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 ~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 = "GET /management/keys/" in let man = [ `S Manpage.s_description; `P "$(cmd) download /management/keys." ] in let output = let doc = "File to save data to be signed." in Arg.(value & opt filepath "future_keys.json" & info [ "o"; "output" ] ~doc) in let base_url = let doc = "Base url of the exchange." in Arg.(value & opt string "http://localhost:3434/" & info [ "base-url" ] ~doc) in Cmd.make (Cmd.info "download" ~version ~doc ~man) @@ let+ output = output and+ base_url = base_url in match download ~base_url ~output with | Error (`Msg err) -> Fmt.epr "Download failure: %s@." err; exit 1 | Ok () -> () let upload_cmd = let doc = "POST /management/keys/" in let man = [ `S Manpage.s_description; `P "$(cmd) upload /management/keys." ] in let input = let doc = "Input file." in Arg.( value & opt filepath "master_signatures.json" & info [ "i"; "input" ] ~doc) in let base_url = let doc = "Base url of the exchange." in Arg.(value & opt string "http://localhost:3434/" & info [ "base-url" ] ~doc) in Cmd.make (Cmd.info "upload" ~version ~doc ~man) @@ let+ input = input and+ base_url = base_url in match upload ~base_url ~input with | Error (`Msg err) -> Fmt.epr "Upload failure: %s@." err; exit 1 | Ok () -> () let sign_cmd = let doc = "Sign FutureKeysResponse." in let input = let doc = "File with FutureKeysResponse data." in Arg.(value & opt filepath "future_keys.json" & info [ "i"; "input" ] ~doc) in let output = let doc = "File to save signature to." in Arg.( value & opt filepath "master_signatures.json" & info [ "o"; "output" ] ~doc) in let master_key = let doc = "Master offline Eddsa private key file." in Arg.( value & opt filepath "data/eddsa_master_offline" & info [ "master-key" ] ~doc) in Cmd.make (Cmd.info "sign" ~version ~doc) @@ let+ input = input and+ output = output and+ master_key = master_key in match sign ~input ~output ~master_key with | Error (*`Msg*) err -> Fmt.epr "Signature failure: %s@." err; exit 1 | Ok () -> () let cli = let info = let doc = "MTE Offline CLI tool" in Cmd.info "mte-offline" ~version ~doc in Cmd.group info [ setup_cmd; download_cmd; sign_cmd; upload_cmd ] let main () = Cmd.eval cli let () = exit (main ())