From 347a552396d37144d3a641a13f94914b27b78dea Mon Sep 17 00:00:00 2001 From: Benjamin Canou Date: Wed, 6 Jun 2018 10:49:53 +0200 Subject: [PATCH] Signer: add authorized keys mechanism --- src/bin_signer/handler.ml | 26 ++- src/bin_signer/https_daemon.ml | 14 +- src/bin_signer/https_daemon.mli | 4 +- src/bin_signer/main_signer.ml | 46 ++++- src/bin_signer/socket_daemon.ml | 15 +- src/bin_signer/socket_daemon.mli | 4 +- src/lib_client_base_unix/client_main_run.ml | 56 ++++-- src/lib_signer_backends/https.ml | 101 ++++++---- src/lib_signer_backends/https.mli | 5 +- src/lib_signer_backends/remote.ml | 8 +- src/lib_signer_backends/remote.mli | 5 +- src/lib_signer_backends/socket.ml | 197 +++++++++++--------- src/lib_signer_backends/socket.mli | 8 +- src/lib_signer_services/signer_messages.ml | 52 +++++- src/lib_signer_services/signer_messages.mli | 17 ++ src/lib_signer_services/signer_services.ml | 24 ++- src/lib_signer_services/signer_services.mli | 6 +- 17 files changed, 410 insertions(+), 178 deletions(-) diff --git a/src/bin_signer/handler.ml b/src/bin_signer/handler.ml index 5d15f1574..b302bf68c 100644 --- a/src/bin_signer/handler.ml +++ b/src/bin_signer/handler.ml @@ -9,11 +9,35 @@ let log = Signer_logging.lwt_log_notice -let sign (cctxt : #Client_context.wallet) pkh data = +module Authorized_key = + Client_aliases.Alias (struct + include Signature.Public_key + let name = "authorized_key" + let to_source s = return (to_b58check s) + let of_source t = Lwt.return (of_b58check t) + end) + +let sign + (cctxt : #Client_context.wallet) + Signer_messages.Sign.Request.{ pkh ; data ; signature } ~require_auth = log "Request for signing %d bytes of data for key %a, magic byte = %02X" (MBytes.length data) Signature.Public_key_hash.pp pkh (MBytes.get_uint8 data 0) >>= fun () -> + begin match require_auth, signature with + | false, _ -> return () + | true, None -> failwith "missing authentication signature field" + | true, Some signature -> + let to_sign = Signer_messages.Sign.Request.to_sign ~pkh ~data in + Authorized_key.load cctxt >>=? fun keys -> + if List.fold_left + (fun acc (_, key) -> acc || Signature.check key signature to_sign) + false keys + then + return () + else + failwith "invalid authentication signature" + end >>=? fun () -> Client_keys.get_key cctxt pkh >>=? fun (name, _pkh, sk_uri) -> log "Signing data for key %s" name >>= fun () -> Client_keys.sign sk_uri data >>=? fun signature -> diff --git a/src/bin_signer/https_daemon.ml b/src/bin_signer/https_daemon.ml index 1892be5a9..4ac868139 100644 --- a/src/bin_signer/https_daemon.ml +++ b/src/bin_signer/https_daemon.ml @@ -9,19 +9,27 @@ let log = Signer_logging.lwt_log_notice -let run (cctxt : #Client_context.wallet) ~host ~port ~cert ~key = +let run (cctxt : #Client_context.wallet) ~host ~port ~cert ~key ~require_auth = log "Accepting HTTPS requests on port %d" port >>= fun () -> let mode : Conduit_lwt_unix.server = `TLS (`Crt_file_path cert, `Key_file_path key, `No_password, `Port port) in let dir = RPC_directory.empty in let dir = - RPC_directory.register1 dir Signer_services.sign begin fun pkh () data -> - Handler.sign cctxt pkh data + RPC_directory.register1 dir Signer_services.sign begin fun pkh signature data -> + Handler.sign cctxt { pkh ; data ; signature } ~require_auth end in let dir = RPC_directory.register1 dir Signer_services.public_key begin fun pkh () () -> Handler.public_key cctxt pkh end in + let dir = + RPC_directory.register0 dir Signer_services.authorized_keys begin fun () () -> + if require_auth then + return None + else + Handler.Authorized_key.load cctxt >>=? fun keys -> + return (Some (keys |> List.split |> snd |> List.map Signature.Public_key.hash)) + end in Lwt.catch (fun () -> RPC_server.launch ~host mode dir diff --git a/src/bin_signer/https_daemon.mli b/src/bin_signer/https_daemon.mli index cee179930..22d6eebad 100644 --- a/src/bin_signer/https_daemon.mli +++ b/src/bin_signer/https_daemon.mli @@ -9,4 +9,6 @@ val run: #Client_context.io_wallet -> - host:string -> port:int -> cert:string -> key:string -> 'a tzresult Lwt.t + host:string -> port:int -> cert:string -> key:string -> + require_auth: bool -> + 'a tzresult Lwt.t diff --git a/src/bin_signer/main_signer.ml b/src/bin_signer/main_signer.ml index 0201ce6a4..2c8d1a7a1 100644 --- a/src/bin_signer/main_signer.ml +++ b/src/bin_signer/main_signer.ml @@ -38,7 +38,7 @@ let group = { Clic.name = "signer" ; title = "Commands specific to the signing daemon" } -let commands = +let commands base_dir require_auth = Client_keys_commands.commands () @ [ command ~group ~desc: "Launch a signer daemon over a TCP socket." @@ -63,7 +63,7 @@ let commands = (prefixes [ "launch" ; "socket" ; "signer" ] @@ stop) (fun (host, port) cctxt -> Tezos_signer_backends.Encrypted.decrypt_all cctxt >>=? fun () -> - Socket_daemon.run cctxt (Tcp (host, port))) ; + Socket_daemon.run cctxt (Tcp (host, port)) ~require_auth) ; command ~group ~desc: "Launch a signer daemon over a local Unix socket." (args1 @@ -72,12 +72,12 @@ let commands = ~short: 's' ~long: "socket" ~placeholder: "path" - ~default: default_unix_path + ~default: (Filename.concat base_dir "socket") (parameter (fun _ s -> return s)))) (prefixes [ "launch" ; "local" ; "signer" ] @@ stop) (fun path cctxt -> Tezos_signer_backends.Encrypted.decrypt_all cctxt >>=? fun () -> - Socket_daemon.run cctxt (Unix path)) ; + Socket_daemon.run cctxt (Unix path) ~require_auth) ; command ~group ~desc: "Launch a signer daemon over HTTPS." (args2 @@ -109,9 +109,31 @@ let commands = (parameter (fun _ s -> return s)) @@ stop) (fun (host, port) cert key cctxt -> Tezos_signer_backends.Encrypted.decrypt_all cctxt >>=? fun () -> - Https_daemon.run cctxt ~host ~port ~cert ~key) ; + Https_daemon.run cctxt ~host ~port ~cert ~key ~require_auth) ; + command ~group + ~desc: "Authorize a given public key to perform signing requests." + (args1 + (arg + ~doc: "an optional name for the key (defaults to the hash)" + ~short: 'N' + ~long: "name" + ~placeholder: "name" + (parameter (fun _ s -> return s)))) + (prefixes [ "add" ; "authorized" ; "key" ] @@ + param + ~name:"pk" + ~desc: "full public key (Base58 encoded)" + (parameter (fun _ s -> Lwt.return (Signature.Public_key.of_b58check s))) @@ + stop) + (fun name key cctxt -> + let pkh = Signature.Public_key.hash key in + let name = match name with + | Some name -> name + | None -> Signature.Public_key_hash.to_b58check pkh in + Handler.Authorized_key.add ~force:false cctxt name key) ] + let home = try Sys.getenv "HOME" with Not_found -> "/root" let default_base_dir = @@ -132,9 +154,17 @@ let base_dir_arg () = By default: '" ^ default_base_dir ^"'.") (string_parameter ()) +let require_auth_arg () = + switch + ~long:"require-authentication" + ~short:'A' + ~doc:"Require a signature from the caller to sign." + () + let global_options () = - args1 + args2 (base_dir_arg ()) + (require_auth_arg ()) (* Main (lwt) entry *) let main () = @@ -158,7 +188,7 @@ let main () = begin begin parse_global_options - (global_options ()) () original_args >>=? fun (base_dir, remaining) -> + (global_options ()) () original_args >>=? fun ((base_dir, require_auth), remaining) -> let base_dir = Option.unopt ~default:default_base_dir base_dir in let cctxt = object inherit Client_context_unix.unix_logger ~base_dir @@ -177,7 +207,7 @@ let main () = ~global_options:(global_options ()) (if Unix.isatty Unix.stdout then Clic.Ansi else Clic.Plain) Format.std_formatter - commands in + (commands base_dir require_auth) in begin match autocomplete with | Some (prev_arg, cur_arg, script) -> Clic.autocompletion diff --git a/src/bin_signer/socket_daemon.ml b/src/bin_signer/socket_daemon.ml index d8b65c630..cb91cf20a 100644 --- a/src/bin_signer/socket_daemon.ml +++ b/src/bin_signer/socket_daemon.ml @@ -11,7 +11,7 @@ open Signer_messages let log = Signer_logging.lwt_log_notice -let run (cctxt : #Client_context.wallet) path = +let run (cctxt : #Client_context.wallet) path ~require_auth = Lwt_utils_unix.Socket.bind path >>=? fun fd -> let rec loop () = Lwt_unix.accept fd >>= fun (fd, _) -> @@ -19,7 +19,7 @@ let run (cctxt : #Client_context.wallet) path = Lwt_utils_unix.Socket.recv fd Request.encoding >>=? function | Sign req -> let encoding = result_encoding Sign.Response.encoding in - Handler.sign cctxt req.pkh req.data >>= fun res -> + Handler.sign cctxt req ~require_auth >>= fun res -> Lwt_utils_unix.Socket.send fd encoding res >>= fun _ -> Lwt_unix.close fd >>= fun () -> return () @@ -29,6 +29,17 @@ let run (cctxt : #Client_context.wallet) path = Lwt_utils_unix.Socket.send fd encoding res >>= fun _ -> Lwt_unix.close fd >>= fun () -> return () + | Authorized_keys -> + let encoding = result_encoding Authorized_keys.Response.encoding in + begin if require_auth then + Handler.Authorized_key.load cctxt >>=? fun keys -> + return (Authorized_keys.Response.Authorized_keys + (keys |> List.split |> snd |> List.map Signature.Public_key.hash)) + else return Authorized_keys.Response.No_authentication + end >>= fun res -> + Lwt_utils_unix.Socket.send fd encoding res >>= fun _ -> + Lwt_unix.close fd >>= fun () -> + return () end ; loop () in diff --git a/src/bin_signer/socket_daemon.mli b/src/bin_signer/socket_daemon.mli index f0b9fa781..a7e330fcb 100644 --- a/src/bin_signer/socket_daemon.mli +++ b/src/bin_signer/socket_daemon.mli @@ -9,4 +9,6 @@ val run: #Client_context.io_wallet -> - Lwt_utils_unix.Socket.addr -> 'a tzresult Lwt.t + Lwt_utils_unix.Socket.addr -> + require_auth: bool -> + 'a tzresult Lwt.t diff --git a/src/lib_client_base_unix/client_main_run.ml b/src/lib_client_base_unix/client_main_run.ml index 3a1edcbb9..2c8d2a2e2 100644 --- a/src/lib_client_base_unix/client_main_run.ml +++ b/src/lib_client_base_unix/client_main_run.ml @@ -74,18 +74,6 @@ let main select_commands = ignore Clic.(setup_formatter Format.err_formatter (if Unix.isatty Unix.stderr then Ansi else Plain) Short) ; init_logger () >>= fun () -> - Client_keys.register_signer - (module Tezos_signer_backends.Unencrypted) ; - Client_keys.register_signer - (module Tezos_signer_backends.Encrypted.Make(struct - let cctxt = new Client_context_unix.unix_prompter - end)) ; - Client_keys.register_signer - (module Tezos_signer_backends.Https) ; - Client_keys.register_signer - (module Tezos_signer_backends.Socket.Unix) ; - Client_keys.register_signer - (module Tezos_signer_backends.Socket.Tcp) ; Lwt.catch begin fun () -> begin Client_config.parse_config_args (new unix_full @@ -102,14 +90,6 @@ let main select_commands = tls = parsed_config_file.tls ; } in let ctxt = new RPC_client.http_ctxt rpc_config Media_type.all_media_types in - select_commands ctxt parsed_args >>=? fun commands -> - let commands = - Clic.add_manual - ~executable_name - ~global_options - (if Unix.isatty Unix.stdout then Clic.Ansi else Clic.Plain) - Format.std_formatter - (config_commands @ builtin_commands @ commands) in let rpc_config = if parsed_args.print_timings then { rpc_config with @@ -124,12 +104,48 @@ let main select_commands = ~confirmations:parsed_args.confirmations ~base_dir:parsed_config_file.base_dir ~rpc_config:rpc_config in + Client_keys.register_signer + (module Tezos_signer_backends.Unencrypted) ; + Client_keys.register_signer + (module Tezos_signer_backends.Encrypted.Make(struct + let cctxt = (client_config :> Client_context.prompter) + end)) ; + let module Remote_authenticator = struct + let authenticate pkhs payload = + Client_keys.list_keys client_config >>=? fun keys -> + match List.filter_map + (function + | (_, known_pkh, _, Some known_sk_uri) + when List.exists (fun pkh -> Signature.Public_key_hash.equal pkh known_pkh) pkhs -> + Some known_sk_uri + | _ -> None) + keys with + | sk_uri :: _ -> + Client_keys.sign sk_uri payload + | [] -> failwith + "remote signer expects authentication signature, \ + but no authorized key was found in the wallet" + end in + let module Https = Tezos_signer_backends.Https.Make(Remote_authenticator) in + let module Socket = Tezos_signer_backends.Socket.Make(Remote_authenticator) in + Client_keys.register_signer (module Https) ; + Client_keys.register_signer (module Socket.Unix) ; + Client_keys.register_signer (module Socket.Tcp) ; Option.iter parsed_config_file.remote_signer ~f: begin fun signer -> Client_keys.register_signer (module Tezos_signer_backends.Remote.Make(struct let default = signer + include Remote_authenticator end)) end ; + select_commands ctxt parsed_args >>=? fun commands -> + let commands = + Clic.add_manual + ~executable_name + ~global_options + (if Unix.isatty Unix.stdout then Clic.Ansi else Clic.Plain) + Format.std_formatter + (config_commands @ builtin_commands @ commands) in begin match autocomplete with | Some (prev_arg, cur_arg, script) -> Clic.autocompletion diff --git a/src/lib_signer_backends/https.ml b/src/lib_signer_backends/https.ml index 317335358..8ff78ea02 100644 --- a/src/lib_signer_backends/https.ml +++ b/src/lib_signer_backends/https.ml @@ -11,52 +11,73 @@ open Client_keys let scheme = "https" -let title = - "Built-in tezos-signer using remote signer through hardcoded https requests." +module Make(P : sig + val authenticate: Signature.Public_key_hash.t list -> MBytes.t -> Signature.t tzresult Lwt.t + end) = struct -let description = - "Valid locators are of this form:\n\ - \ - https://host/tz1...\n\ - \ - https://host:port/path/to/service/tz1...\n" + let scheme = scheme -let parse uri = - (* extract `tz1..` from the last component of the path *) - assert (Uri.scheme uri = Some scheme) ; - let path = Uri.path uri in - begin match String.rindex_opt path '/' with - | None -> - failwith "Invalid locator %a" Uri.pp_hum uri - | Some i -> - let pkh = String.sub path (i + 1) (String.length path - i - 1) in - let path = String.sub path 0 i in - return (Uri.with_path uri path, pkh) - end >>=? fun (base, pkh) -> - Lwt.return (Signature.Public_key_hash.of_b58check pkh) >>=? fun pkh -> - return (base, pkh) + let title = + "Built-in tezos-signer using remote signer through hardcoded https requests." -let public_key uri = - parse (uri : pk_uri :> Uri.t) >>=? fun (base, pkh) -> - RPC_client.call_service - Media_type.all_media_types - ~base Signer_services.public_key ((), pkh) () () + let description = + "Valid locators are of this form:\n\ + \ - https://host/tz1...\n\ + \ - https://host:port/path/to/service/tz1...\n" -let neuterize uri = - return (Client_keys.make_pk_uri (uri : sk_uri :> Uri.t)) + let parse uri = + (* extract `tz1..` from the last component of the path *) + assert (Uri.scheme uri = Some scheme) ; + let path = Uri.path uri in + begin match String.rindex_opt path '/' with + | None -> + failwith "Invalid locator %a" Uri.pp_hum uri + | Some i -> + let pkh = String.sub path (i + 1) (String.length path - i - 1) in + let path = String.sub path 0 i in + return (Uri.with_path uri path, pkh) + end >>=? fun (base, pkh) -> + Lwt.return (Signature.Public_key_hash.of_b58check pkh) >>=? fun pkh -> + return (base, pkh) -let public_key_hash uri = - public_key uri >>=? fun pk -> - return (Signature.Public_key.hash pk) + let public_key uri = + parse (uri : pk_uri :> Uri.t) >>=? fun (base, pkh) -> + RPC_client.call_service + Media_type.all_media_types + ~base Signer_services.public_key ((), pkh) () () -let sign ?watermark uri msg = - parse (uri : sk_uri :> Uri.t) >>=? fun (base, pkh) -> - let msg = - match watermark with - | None -> msg - | Some watermark -> - MBytes.concat "" [ Signature.bytes_of_watermark watermark ; msg ] in - RPC_client.call_service - Media_type.all_media_types - ~base Signer_services.sign ((), pkh) () msg + let neuterize uri = + return (Client_keys.make_pk_uri (uri : sk_uri :> Uri.t)) + + let public_key_hash uri = + public_key uri >>=? fun pk -> + return (Signature.Public_key.hash pk) + + let sign ?watermark uri msg = + parse (uri : sk_uri :> Uri.t) >>=? fun (base, pkh) -> + let msg = + match watermark with + | None -> msg + | Some watermark -> + MBytes.concat "" [ Signature.bytes_of_watermark watermark ; msg ] in + RPC_client.call_service + Media_type.all_media_types + ~base Signer_services.authorized_keys () () () >>=? fun authorized_keys -> + begin match authorized_keys with + | Some authorized_keys -> + P.authenticate + authorized_keys + (Signer_messages.Sign.Request.to_sign ~pkh ~data:msg) >>=? fun signature -> + return (Some signature) + | None -> return None + end >>=? fun signature -> + RPC_client.call_service + Media_type.all_media_types + ~base Signer_services.sign ((), pkh) + signature + msg + +end let make_base host port = Uri.make ~scheme ~host ~port () diff --git a/src/lib_signer_backends/https.mli b/src/lib_signer_backends/https.mli index c4da4d8f0..978378073 100644 --- a/src/lib_signer_backends/https.mli +++ b/src/lib_signer_backends/https.mli @@ -7,6 +7,9 @@ (* *) (**************************************************************************) -include Client_keys.SIGNER +module Make(P : sig + val authenticate: Signature.Public_key_hash.t list -> MBytes.t -> Signature.t tzresult Lwt.t + end) + : Client_keys.SIGNER val make_base: string -> int -> Uri.t diff --git a/src/lib_signer_backends/remote.ml b/src/lib_signer_backends/remote.ml index 2338d8314..7b95fef59 100644 --- a/src/lib_signer_backends/remote.ml +++ b/src/lib_signer_backends/remote.ml @@ -11,7 +11,10 @@ open Client_keys let scheme = "remote" -module Make(S : sig val default : Uri.t end) = struct +module Make(S : sig + val default : Uri.t + val authenticate: Signature.Public_key_hash.t list -> MBytes.t -> Signature.t tzresult Lwt.t + end) = struct let scheme = scheme @@ -27,6 +30,9 @@ module Make(S : sig val default : Uri.t end) = struct - $TEZOS_SIGNER_TCP_HOST and $TEZOS_SIGNER_TCP_PORT (default: 7732),\n\ - $TEZOS_SIGNER_HTTPS_HOST and $TEZOS_SIGNER_HTTPS_PORT (default: 443)." + module Socket = Socket.Make(S) + module Https = Https.Make(S) + let get_remote () = match Uri.scheme S.default with | Some "unix" -> (module Socket.Unix : SIGNER) diff --git a/src/lib_signer_backends/remote.mli b/src/lib_signer_backends/remote.mli index 3fff32ddc..251150ed0 100644 --- a/src/lib_signer_backends/remote.mli +++ b/src/lib_signer_backends/remote.mli @@ -7,7 +7,10 @@ (* *) (**************************************************************************) -module Make(S : sig val default : Uri.t end) : Client_keys.SIGNER +module Make(S : sig + val default : Uri.t + val authenticate: Signature.Public_key_hash.t list -> MBytes.t -> Signature.t tzresult Lwt.t + end) : Client_keys.SIGNER val make_pk: Signature.public_key -> Client_keys.pk_uri val make_sk: Signature.secret_key -> Client_keys.sk_uri diff --git a/src/lib_signer_backends/socket.ml b/src/lib_signer_backends/socket.ml index b4a096231..c51eae314 100644 --- a/src/lib_signer_backends/socket.ml +++ b/src/lib_signer_backends/socket.ml @@ -10,108 +10,129 @@ open Client_keys open Signer_messages -let sign ?watermark path pkh msg = - let msg = - match watermark with - | None -> msg - | Some watermark -> - MBytes.concat "" [ Signature.bytes_of_watermark watermark ; msg ] in - let req = { Sign.Request.pkh ; data = msg } in - Lwt_utils_unix.Socket.connect path >>=? fun conn -> - Lwt_utils_unix.Socket.send - conn Request.encoding (Request.Sign req) >>=? fun () -> - let encoding = result_encoding Sign.Response.encoding in - Lwt_utils_unix.Socket.recv conn encoding >>=? fun res -> - Lwt_unix.close conn >>= fun () -> - Lwt.return res +let tcp_scheme = "tcp" +let unix_scheme = "unix" -let public_key path pkh = - Lwt_utils_unix.Socket.connect path >>=? fun conn -> - Lwt_utils_unix.Socket.send - conn Request.encoding (Request.Public_key pkh) >>=? fun () -> - let encoding = result_encoding Public_key.Response.encoding in - Lwt_utils_unix.Socket.recv conn encoding >>=? fun res -> - Lwt_unix.close conn >>= fun () -> - Lwt.return res +module Make(P : sig + val authenticate: Signature.Public_key_hash.t list -> MBytes.t -> Signature.t tzresult Lwt.t + end) = struct -module Unix = struct + let sign ?watermark path pkh msg = + let msg = + match watermark with + | None -> msg + | Some watermark -> + MBytes.concat "" [ Signature.bytes_of_watermark watermark ; msg ] in + Lwt_utils_unix.Socket.connect path >>=? fun conn -> + Lwt_utils_unix.Socket.send + conn Request.encoding Request.Authorized_keys >>=? fun () -> + Lwt_utils_unix.Socket.recv conn + Authorized_keys.Response.encoding >>=? fun authorized_keys -> + begin match authorized_keys with + | No_authentication -> return None + | Authorized_keys authorized_keys -> + P.authenticate authorized_keys + (Sign.Request.to_sign ~pkh ~data:msg) >>=? fun signature -> + return (Some signature) + end >>=? fun signature -> + let req = { Sign.Request.pkh ; data = msg ; signature } in + Lwt_utils_unix.Socket.send + conn Request.encoding (Request.Sign req) >>=? fun () -> + Lwt_utils_unix.Socket.recv conn + (result_encoding Sign.Response.encoding) >>=? fun res -> + Lwt_unix.close conn >>= fun () -> + Lwt.return res - let scheme = "unix" + let public_key path pkh = + Lwt_utils_unix.Socket.connect path >>=? fun conn -> + Lwt_utils_unix.Socket.send + conn Request.encoding (Request.Public_key pkh) >>=? fun () -> + let encoding = result_encoding Public_key.Response.encoding in + Lwt_utils_unix.Socket.recv conn encoding >>=? fun res -> + Lwt_unix.close conn >>= fun () -> + Lwt.return res - let title = - "Built-in tezos-signer using remote signer through hardcoded unix socket." + module Unix = struct - let description = - "Valid locators are of this form: unix:///path/to/socket?pkh=tz1..." + let scheme = unix_scheme - let parse uri = - assert (Uri.scheme uri = Some scheme) ; - trace (Invalid_uri uri) @@ - match Uri.get_query_param uri "pkh" with - | None -> failwith "Missing the query parameter: 'pkh=tz1...'" - | Some key -> - Lwt.return (Signature.Public_key_hash.of_b58check key) >>=? fun key -> - return (Lwt_utils_unix.Socket.Unix (Uri.path uri), key) + let title = + "Built-in tezos-signer using remote signer through hardcoded unix socket." - let public_key uri = - parse (uri : pk_uri :> Uri.t) >>=? fun (path, pkh) -> - public_key path pkh + let description = + "Valid locators are of this form: unix:///path/to/socket?pkh=tz1..." - let neuterize uri = - return (Client_keys.make_pk_uri (uri : sk_uri :> Uri.t)) + let parse uri = + assert (Uri.scheme uri = Some scheme) ; + trace (Invalid_uri uri) @@ + match Uri.get_query_param uri "pkh" with + | None -> failwith "Missing the query parameter: 'pkh=tz1...'" + | Some key -> + Lwt.return (Signature.Public_key_hash.of_b58check key) >>=? fun key -> + return (Lwt_utils_unix.Socket.Unix (Uri.path uri), key) - let public_key_hash uri = - public_key uri >>=? fun pk -> - return (Signature.Public_key.hash pk) + let public_key uri = + parse (uri : pk_uri :> Uri.t) >>=? fun (path, pkh) -> + public_key path pkh - let sign ?watermark uri msg = - parse (uri : sk_uri :> Uri.t) >>=? fun (path, pkh) -> - sign ?watermark path pkh msg + let neuterize uri = + return (Client_keys.make_pk_uri (uri : sk_uri :> Uri.t)) + + let public_key_hash uri = + public_key uri >>=? fun pk -> + return (Signature.Public_key.hash pk) + + let sign ?watermark uri msg = + parse (uri : sk_uri :> Uri.t) >>=? fun (path, pkh) -> + sign ?watermark path pkh msg + + end + + module Tcp = struct + + let scheme = tcp_scheme + + let title = + "Built-in tezos-signer using remote signer through hardcoded tcp socket." + + let description = + "Valid locators are of this form: tcp://host:port/tz1..." + + let parse uri = + assert (Uri.scheme uri = Some scheme) ; + trace (Invalid_uri uri) @@ + match Uri.host uri, Uri.port uri with + | None, _ -> + failwith "Missing host address" + | _, None -> + failwith "Missing host port" + | Some path, Some port -> + Lwt.return + (Signature.Public_key_hash.of_b58check (Uri.path uri)) >>=? fun pkh -> + return (Lwt_utils_unix.Socket.Tcp (path, port), pkh) + + let public_key uri = + parse (uri : pk_uri :> Uri.t) >>=? fun (path, pkh) -> + public_key path pkh + + let neuterize uri = + return (Client_keys.make_pk_uri (uri : sk_uri :> Uri.t)) + + let public_key_hash uri = + public_key uri >>=? fun pk -> + return (Signature.Public_key.hash pk) + + let sign ?watermark uri msg = + parse (uri : sk_uri :> Uri.t) >>=? fun (path, pkh) -> + sign ?watermark path pkh msg + + end end -module Tcp = struct - - let scheme = "tcp" - - let title = - "Built-in tezos-signer using remote signer through hardcoded tcp socket." - - let description = - "Valid locators are of this form: tcp://host:port/tz1..." - - let parse uri = - assert (Uri.scheme uri = Some scheme) ; - trace (Invalid_uri uri) @@ - match Uri.host uri, Uri.port uri with - | None, _ -> - failwith "Missing host address" - | _, None -> - failwith "Missing host port" - | Some path, Some port -> - Lwt.return - (Signature.Public_key_hash.of_b58check (Uri.path uri)) >>=? fun pkh -> - return (Lwt_utils_unix.Socket.Tcp (path, port), pkh) - - let public_key uri = - parse (uri : pk_uri :> Uri.t) >>=? fun (path, pkh) -> - public_key path pkh - - let neuterize uri = - return (Client_keys.make_pk_uri (uri : sk_uri :> Uri.t)) - - let public_key_hash uri = - public_key uri >>=? fun pk -> - return (Signature.Public_key.hash pk) - - let sign ?watermark uri msg = - parse (uri : sk_uri :> Uri.t) >>=? fun (path, pkh) -> - sign ?watermark path pkh msg - -end let make_unix_base path = - Uri.make ~scheme:Unix.scheme ~path () + Uri.make ~scheme:unix_scheme ~path () let make_tcp_base host port = - Uri.make ~scheme:Tcp.scheme ~host ~port () + Uri.make ~scheme:tcp_scheme ~host ~port () diff --git a/src/lib_signer_backends/socket.mli b/src/lib_signer_backends/socket.mli index 86dabb587..e36e8081f 100644 --- a/src/lib_signer_backends/socket.mli +++ b/src/lib_signer_backends/socket.mli @@ -7,8 +7,12 @@ (* *) (**************************************************************************) -module Unix : Client_keys.SIGNER -module Tcp : Client_keys.SIGNER +module Make(P : sig + val authenticate: Signature.Public_key_hash.t list -> MBytes.t -> Signature.t tzresult Lwt.t + end) : sig + module Unix : Client_keys.SIGNER + module Tcp : Client_keys.SIGNER +end val make_unix_base: string -> Uri.t val make_tcp_base: string -> int -> Uri.t diff --git a/src/lib_signer_services/signer_messages.ml b/src/lib_signer_services/signer_messages.ml index 6f33958b4..0c1064f6d 100644 --- a/src/lib_signer_services/signer_messages.ml +++ b/src/lib_signer_services/signer_messages.ml @@ -14,18 +14,26 @@ module Sign = struct type t = { pkh: Signature.Public_key_hash.t ; data: MBytes.t ; + signature: Signature.t option ; } + let to_sign ~pkh ~data = + MBytes.concat "" + [ MBytes.of_hex (`Hex "04") ; + Signature.Public_key_hash.to_bytes pkh ; + data ] + let encoding = let open Data_encoding in conv - (fun { pkh ; data } -> - (pkh, data)) - (fun (pkh, data) -> - { pkh ; data }) - (obj2 + (fun { pkh ; data ; signature } -> + (pkh, data, signature)) + (fun (pkh, data, signature) -> + { pkh ; data ; signature }) + (obj3 (req "pkh" Signature.Public_key_hash.encoding) - (req "data" bytes)) + (req "data" bytes) + (opt "signature" Signature.encoding)) end @@ -62,11 +70,38 @@ module Public_key = struct end +module Authorized_keys = struct + + module Response = struct + + type t = + | No_authentication + | Authorized_keys of Signature.Public_key_hash.t list + + let encoding = + let open Data_encoding in + union + [ case (Tag 0) + ~title: "No_authentication" + (constant "no_authentication_required") + (function No_authentication -> Some () | _ -> None) + (fun () -> No_authentication) ; + case (Tag 1) + ~title: "Authorized_keys" + (list Signature.Public_key_hash.encoding) + (function Authorized_keys l -> Some l | _ -> None) + (fun l -> Authorized_keys l) ] + + end + +end + module Request = struct type t = | Sign of Sign.Request.t | Public_key of Public_key.Request.t + | Authorized_keys let encoding = let open Data_encoding in @@ -85,6 +120,11 @@ module Request = struct Public_key.Request.encoding) (function Public_key req -> Some ((), req) | _ -> None) (fun ((), req) -> Public_key req) ; + case (Tag 2) + ~title:"Authorized_keys" + (obj1 (req "kind" (constant "authorized_keys"))) + (function Authorized_keys -> Some () | _ -> None) + (fun () -> Authorized_keys) ; ] end diff --git a/src/lib_signer_services/signer_messages.mli b/src/lib_signer_services/signer_messages.mli index 529b40f62..6c2204b2c 100644 --- a/src/lib_signer_services/signer_messages.mli +++ b/src/lib_signer_services/signer_messages.mli @@ -13,7 +13,12 @@ module Sign : sig type t = { pkh: Signature.Public_key_hash.t ; data: MBytes.t ; + signature: Signature.t option ; } + val to_sign: + pkh: Signature.Public_key_hash.t -> + data: MBytes.t -> + MBytes.t val encoding : t Data_encoding.t end @@ -38,11 +43,23 @@ module Public_key : sig end +module Authorized_keys : sig + + module Response : sig + type t = + | No_authentication + | Authorized_keys of Signature.Public_key_hash.t list + val encoding : t Data_encoding.t + end + +end + module Request : sig type t = | Sign of Sign.Request.t | Public_key of Public_key.Request.t + | Authorized_keys val encoding : t Data_encoding.t end diff --git a/src/lib_signer_services/signer_services.ml b/src/lib_signer_services/signer_services.ml index 807d0a795..8050b35c5 100644 --- a/src/lib_signer_services/signer_services.ml +++ b/src/lib_signer_services/signer_services.ml @@ -8,11 +8,21 @@ (**************************************************************************) let sign = + let query = + let open RPC_query in + query (fun signature -> signature) + |+ opt_field + ~descr: "Must be provided if the signer requires \ + authentication. In this case, it must be the signature \ + of the public key hash and message concatenated, by one \ + of the keys authorized by the signer." + "authentication" Signature.rpc_arg (fun signature -> signature) + |> seal in RPC_service.post_service ~description: "Sign a piece of data with a given remote key" - ~query: RPC_query.empty + ~query ~input: Data_encoding.bytes - ~output: Data_encoding.(obj1 (req "signature" Signature.encoding)) + ~output: Data_encoding.(obj1 (req "signature" Signature.encoding)) RPC_path.(root /: Signature.Public_key_hash.rpc_arg) let public_key = @@ -21,3 +31,13 @@ let public_key = ~query: RPC_query.empty ~output: Data_encoding.(obj1 (req "public_key" Signature.Public_key.encoding)) RPC_path.(root /: Signature.Public_key_hash.rpc_arg) + +let authorized_keys = + RPC_service.get_service + ~description: "Retrieve the public keys that can be used to \ + authenticate signing commands.\n\ + If the empty object is returned, the signer has \ + been set to accept unsigned commands." + ~query: RPC_query.empty + ~output: Data_encoding.(obj1 (opt "authorized_keys" (list Signature.Public_key_hash.encoding))) + RPC_path.(root / "authorized_keys") diff --git a/src/lib_signer_services/signer_services.mli b/src/lib_signer_services/signer_services.mli index 155e76e51..fa2193186 100644 --- a/src/lib_signer_services/signer_services.mli +++ b/src/lib_signer_services/signer_services.mli @@ -9,8 +9,12 @@ val sign : ([ `POST ], unit, unit * Signature.Public_key_hash.t, - unit, MBytes.t, Signature.t) RPC_service.t + Signature.t option, MBytes.t, Signature.t) RPC_service.t val public_key : ([ `GET ], unit, unit * Signature.Public_key_hash.t, unit, unit, Signature.Public_key.t) RPC_service.t + +val authorized_keys : + ([ `GET ], unit, unit, + unit, unit, Signature.Public_key_hash.t list option) RPC_service.t