Signer: add authorized keys mechanism
This commit is contained in:
parent
a8b2ab325e
commit
347a552396
@ -9,11 +9,35 @@
|
|||||||
|
|
||||||
let log = Signer_logging.lwt_log_notice
|
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"
|
log "Request for signing %d bytes of data for key %a, magic byte = %02X"
|
||||||
(MBytes.length data)
|
(MBytes.length data)
|
||||||
Signature.Public_key_hash.pp pkh
|
Signature.Public_key_hash.pp pkh
|
||||||
(MBytes.get_uint8 data 0) >>= fun () ->
|
(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) ->
|
Client_keys.get_key cctxt pkh >>=? fun (name, _pkh, sk_uri) ->
|
||||||
log "Signing data for key %s" name >>= fun () ->
|
log "Signing data for key %s" name >>= fun () ->
|
||||||
Client_keys.sign sk_uri data >>=? fun signature ->
|
Client_keys.sign sk_uri data >>=? fun signature ->
|
||||||
|
@ -9,19 +9,27 @@
|
|||||||
|
|
||||||
let log = Signer_logging.lwt_log_notice
|
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 () ->
|
log "Accepting HTTPS requests on port %d" port >>= fun () ->
|
||||||
let mode : Conduit_lwt_unix.server =
|
let mode : Conduit_lwt_unix.server =
|
||||||
`TLS (`Crt_file_path cert, `Key_file_path key, `No_password, `Port port) in
|
`TLS (`Crt_file_path cert, `Key_file_path key, `No_password, `Port port) in
|
||||||
let dir = RPC_directory.empty in
|
let dir = RPC_directory.empty in
|
||||||
let dir =
|
let dir =
|
||||||
RPC_directory.register1 dir Signer_services.sign begin fun pkh () data ->
|
RPC_directory.register1 dir Signer_services.sign begin fun pkh signature data ->
|
||||||
Handler.sign cctxt pkh data
|
Handler.sign cctxt { pkh ; data ; signature } ~require_auth
|
||||||
end in
|
end in
|
||||||
let dir =
|
let dir =
|
||||||
RPC_directory.register1 dir Signer_services.public_key begin fun pkh () () ->
|
RPC_directory.register1 dir Signer_services.public_key begin fun pkh () () ->
|
||||||
Handler.public_key cctxt pkh
|
Handler.public_key cctxt pkh
|
||||||
end in
|
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
|
Lwt.catch
|
||||||
(fun () ->
|
(fun () ->
|
||||||
RPC_server.launch ~host mode dir
|
RPC_server.launch ~host mode dir
|
||||||
|
@ -9,4 +9,6 @@
|
|||||||
|
|
||||||
val run:
|
val run:
|
||||||
#Client_context.io_wallet ->
|
#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
|
||||||
|
@ -38,7 +38,7 @@ let group =
|
|||||||
{ Clic.name = "signer" ;
|
{ Clic.name = "signer" ;
|
||||||
title = "Commands specific to the signing daemon" }
|
title = "Commands specific to the signing daemon" }
|
||||||
|
|
||||||
let commands =
|
let commands base_dir require_auth =
|
||||||
Client_keys_commands.commands () @
|
Client_keys_commands.commands () @
|
||||||
[ command ~group
|
[ command ~group
|
||||||
~desc: "Launch a signer daemon over a TCP socket."
|
~desc: "Launch a signer daemon over a TCP socket."
|
||||||
@ -63,7 +63,7 @@ let commands =
|
|||||||
(prefixes [ "launch" ; "socket" ; "signer" ] @@ stop)
|
(prefixes [ "launch" ; "socket" ; "signer" ] @@ stop)
|
||||||
(fun (host, port) cctxt ->
|
(fun (host, port) cctxt ->
|
||||||
Tezos_signer_backends.Encrypted.decrypt_all cctxt >>=? fun () ->
|
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
|
command ~group
|
||||||
~desc: "Launch a signer daemon over a local Unix socket."
|
~desc: "Launch a signer daemon over a local Unix socket."
|
||||||
(args1
|
(args1
|
||||||
@ -72,12 +72,12 @@ let commands =
|
|||||||
~short: 's'
|
~short: 's'
|
||||||
~long: "socket"
|
~long: "socket"
|
||||||
~placeholder: "path"
|
~placeholder: "path"
|
||||||
~default: default_unix_path
|
~default: (Filename.concat base_dir "socket")
|
||||||
(parameter (fun _ s -> return s))))
|
(parameter (fun _ s -> return s))))
|
||||||
(prefixes [ "launch" ; "local" ; "signer" ] @@ stop)
|
(prefixes [ "launch" ; "local" ; "signer" ] @@ stop)
|
||||||
(fun path cctxt ->
|
(fun path cctxt ->
|
||||||
Tezos_signer_backends.Encrypted.decrypt_all cctxt >>=? fun () ->
|
Tezos_signer_backends.Encrypted.decrypt_all cctxt >>=? fun () ->
|
||||||
Socket_daemon.run cctxt (Unix path)) ;
|
Socket_daemon.run cctxt (Unix path) ~require_auth) ;
|
||||||
command ~group
|
command ~group
|
||||||
~desc: "Launch a signer daemon over HTTPS."
|
~desc: "Launch a signer daemon over HTTPS."
|
||||||
(args2
|
(args2
|
||||||
@ -109,9 +109,31 @@ let commands =
|
|||||||
(parameter (fun _ s -> return s)) @@ stop)
|
(parameter (fun _ s -> return s)) @@ stop)
|
||||||
(fun (host, port) cert key cctxt ->
|
(fun (host, port) cert key cctxt ->
|
||||||
Tezos_signer_backends.Encrypted.decrypt_all cctxt >>=? fun () ->
|
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 home = try Sys.getenv "HOME" with Not_found -> "/root"
|
||||||
|
|
||||||
let default_base_dir =
|
let default_base_dir =
|
||||||
@ -132,9 +154,17 @@ let base_dir_arg () =
|
|||||||
By default: '" ^ default_base_dir ^"'.")
|
By default: '" ^ default_base_dir ^"'.")
|
||||||
(string_parameter ())
|
(string_parameter ())
|
||||||
|
|
||||||
|
let require_auth_arg () =
|
||||||
|
switch
|
||||||
|
~long:"require-authentication"
|
||||||
|
~short:'A'
|
||||||
|
~doc:"Require a signature from the caller to sign."
|
||||||
|
()
|
||||||
|
|
||||||
let global_options () =
|
let global_options () =
|
||||||
args1
|
args2
|
||||||
(base_dir_arg ())
|
(base_dir_arg ())
|
||||||
|
(require_auth_arg ())
|
||||||
|
|
||||||
(* Main (lwt) entry *)
|
(* Main (lwt) entry *)
|
||||||
let main () =
|
let main () =
|
||||||
@ -158,7 +188,7 @@ let main () =
|
|||||||
begin
|
begin
|
||||||
begin
|
begin
|
||||||
parse_global_options
|
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 base_dir = Option.unopt ~default:default_base_dir base_dir in
|
||||||
let cctxt = object
|
let cctxt = object
|
||||||
inherit Client_context_unix.unix_logger ~base_dir
|
inherit Client_context_unix.unix_logger ~base_dir
|
||||||
@ -177,7 +207,7 @@ let main () =
|
|||||||
~global_options:(global_options ())
|
~global_options:(global_options ())
|
||||||
(if Unix.isatty Unix.stdout then Clic.Ansi else Clic.Plain)
|
(if Unix.isatty Unix.stdout then Clic.Ansi else Clic.Plain)
|
||||||
Format.std_formatter
|
Format.std_formatter
|
||||||
commands in
|
(commands base_dir require_auth) in
|
||||||
begin match autocomplete with
|
begin match autocomplete with
|
||||||
| Some (prev_arg, cur_arg, script) ->
|
| Some (prev_arg, cur_arg, script) ->
|
||||||
Clic.autocompletion
|
Clic.autocompletion
|
||||||
|
@ -11,7 +11,7 @@ open Signer_messages
|
|||||||
|
|
||||||
let log = Signer_logging.lwt_log_notice
|
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 ->
|
Lwt_utils_unix.Socket.bind path >>=? fun fd ->
|
||||||
let rec loop () =
|
let rec loop () =
|
||||||
Lwt_unix.accept fd >>= fun (fd, _) ->
|
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
|
Lwt_utils_unix.Socket.recv fd Request.encoding >>=? function
|
||||||
| Sign req ->
|
| Sign req ->
|
||||||
let encoding = result_encoding Sign.Response.encoding in
|
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_utils_unix.Socket.send fd encoding res >>= fun _ ->
|
||||||
Lwt_unix.close fd >>= fun () ->
|
Lwt_unix.close fd >>= fun () ->
|
||||||
return ()
|
return ()
|
||||||
@ -29,6 +29,17 @@ let run (cctxt : #Client_context.wallet) path =
|
|||||||
Lwt_utils_unix.Socket.send fd encoding res >>= fun _ ->
|
Lwt_utils_unix.Socket.send fd encoding res >>= fun _ ->
|
||||||
Lwt_unix.close fd >>= fun () ->
|
Lwt_unix.close fd >>= fun () ->
|
||||||
return ()
|
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 ;
|
end ;
|
||||||
loop ()
|
loop ()
|
||||||
in
|
in
|
||||||
|
@ -9,4 +9,6 @@
|
|||||||
|
|
||||||
val run:
|
val run:
|
||||||
#Client_context.io_wallet ->
|
#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
|
||||||
|
@ -74,18 +74,6 @@ let main select_commands =
|
|||||||
ignore Clic.(setup_formatter Format.err_formatter
|
ignore Clic.(setup_formatter Format.err_formatter
|
||||||
(if Unix.isatty Unix.stderr then Ansi else Plain) Short) ;
|
(if Unix.isatty Unix.stderr then Ansi else Plain) Short) ;
|
||||||
init_logger () >>= fun () ->
|
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
|
Lwt.catch begin fun () -> begin
|
||||||
Client_config.parse_config_args
|
Client_config.parse_config_args
|
||||||
(new unix_full
|
(new unix_full
|
||||||
@ -102,14 +90,6 @@ let main select_commands =
|
|||||||
tls = parsed_config_file.tls ;
|
tls = parsed_config_file.tls ;
|
||||||
} in
|
} in
|
||||||
let ctxt = new RPC_client.http_ctxt rpc_config Media_type.all_media_types 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 =
|
let rpc_config =
|
||||||
if parsed_args.print_timings then
|
if parsed_args.print_timings then
|
||||||
{ rpc_config with
|
{ rpc_config with
|
||||||
@ -124,12 +104,48 @@ let main select_commands =
|
|||||||
~confirmations:parsed_args.confirmations
|
~confirmations:parsed_args.confirmations
|
||||||
~base_dir:parsed_config_file.base_dir
|
~base_dir:parsed_config_file.base_dir
|
||||||
~rpc_config:rpc_config in
|
~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 ->
|
Option.iter parsed_config_file.remote_signer ~f: begin fun signer ->
|
||||||
Client_keys.register_signer
|
Client_keys.register_signer
|
||||||
(module Tezos_signer_backends.Remote.Make(struct
|
(module Tezos_signer_backends.Remote.Make(struct
|
||||||
let default = signer
|
let default = signer
|
||||||
|
include Remote_authenticator
|
||||||
end))
|
end))
|
||||||
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
|
begin match autocomplete with
|
||||||
| Some (prev_arg, cur_arg, script) ->
|
| Some (prev_arg, cur_arg, script) ->
|
||||||
Clic.autocompletion
|
Clic.autocompletion
|
||||||
|
@ -11,6 +11,12 @@ open Client_keys
|
|||||||
|
|
||||||
let scheme = "https"
|
let scheme = "https"
|
||||||
|
|
||||||
|
module Make(P : sig
|
||||||
|
val authenticate: Signature.Public_key_hash.t list -> MBytes.t -> Signature.t tzresult Lwt.t
|
||||||
|
end) = struct
|
||||||
|
|
||||||
|
let scheme = scheme
|
||||||
|
|
||||||
let title =
|
let title =
|
||||||
"Built-in tezos-signer using remote signer through hardcoded https requests."
|
"Built-in tezos-signer using remote signer through hardcoded https requests."
|
||||||
|
|
||||||
@ -56,7 +62,22 @@ let sign ?watermark uri msg =
|
|||||||
MBytes.concat "" [ Signature.bytes_of_watermark watermark ; msg ] in
|
MBytes.concat "" [ Signature.bytes_of_watermark watermark ; msg ] in
|
||||||
RPC_client.call_service
|
RPC_client.call_service
|
||||||
Media_type.all_media_types
|
Media_type.all_media_types
|
||||||
~base Signer_services.sign ((), pkh) () msg
|
~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 =
|
let make_base host port =
|
||||||
Uri.make ~scheme ~host ~port ()
|
Uri.make ~scheme ~host ~port ()
|
||||||
|
@ -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
|
val make_base: string -> int -> Uri.t
|
||||||
|
@ -11,7 +11,10 @@ open Client_keys
|
|||||||
|
|
||||||
let scheme = "remote"
|
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
|
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_TCP_HOST and $TEZOS_SIGNER_TCP_PORT (default: 7732),\n\
|
||||||
- $TEZOS_SIGNER_HTTPS_HOST and $TEZOS_SIGNER_HTTPS_PORT (default: 443)."
|
- $TEZOS_SIGNER_HTTPS_HOST and $TEZOS_SIGNER_HTTPS_PORT (default: 443)."
|
||||||
|
|
||||||
|
module Socket = Socket.Make(S)
|
||||||
|
module Https = Https.Make(S)
|
||||||
|
|
||||||
let get_remote () =
|
let get_remote () =
|
||||||
match Uri.scheme S.default with
|
match Uri.scheme S.default with
|
||||||
| Some "unix" -> (module Socket.Unix : SIGNER)
|
| Some "unix" -> (module Socket.Unix : SIGNER)
|
||||||
|
@ -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_pk: Signature.public_key -> Client_keys.pk_uri
|
||||||
val make_sk: Signature.secret_key -> Client_keys.sk_uri
|
val make_sk: Signature.secret_key -> Client_keys.sk_uri
|
||||||
|
@ -10,18 +10,36 @@
|
|||||||
open Client_keys
|
open Client_keys
|
||||||
open Signer_messages
|
open Signer_messages
|
||||||
|
|
||||||
|
let tcp_scheme = "tcp"
|
||||||
|
let unix_scheme = "unix"
|
||||||
|
|
||||||
|
module Make(P : sig
|
||||||
|
val authenticate: Signature.Public_key_hash.t list -> MBytes.t -> Signature.t tzresult Lwt.t
|
||||||
|
end) = struct
|
||||||
|
|
||||||
let sign ?watermark path pkh msg =
|
let sign ?watermark path pkh msg =
|
||||||
let msg =
|
let msg =
|
||||||
match watermark with
|
match watermark with
|
||||||
| None -> msg
|
| None -> msg
|
||||||
| Some watermark ->
|
| Some watermark ->
|
||||||
MBytes.concat "" [ Signature.bytes_of_watermark watermark ; msg ] in
|
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.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
|
Lwt_utils_unix.Socket.send
|
||||||
conn Request.encoding (Request.Sign req) >>=? fun () ->
|
conn Request.encoding (Request.Sign req) >>=? fun () ->
|
||||||
let encoding = result_encoding Sign.Response.encoding in
|
Lwt_utils_unix.Socket.recv conn
|
||||||
Lwt_utils_unix.Socket.recv conn encoding >>=? fun res ->
|
(result_encoding Sign.Response.encoding) >>=? fun res ->
|
||||||
Lwt_unix.close conn >>= fun () ->
|
Lwt_unix.close conn >>= fun () ->
|
||||||
Lwt.return res
|
Lwt.return res
|
||||||
|
|
||||||
@ -36,7 +54,7 @@ let public_key path pkh =
|
|||||||
|
|
||||||
module Unix = struct
|
module Unix = struct
|
||||||
|
|
||||||
let scheme = "unix"
|
let scheme = unix_scheme
|
||||||
|
|
||||||
let title =
|
let title =
|
||||||
"Built-in tezos-signer using remote signer through hardcoded unix socket."
|
"Built-in tezos-signer using remote signer through hardcoded unix socket."
|
||||||
@ -72,7 +90,7 @@ end
|
|||||||
|
|
||||||
module Tcp = struct
|
module Tcp = struct
|
||||||
|
|
||||||
let scheme = "tcp"
|
let scheme = tcp_scheme
|
||||||
|
|
||||||
let title =
|
let title =
|
||||||
"Built-in tezos-signer using remote signer through hardcoded tcp socket."
|
"Built-in tezos-signer using remote signer through hardcoded tcp socket."
|
||||||
@ -110,8 +128,11 @@ module Tcp = struct
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
let make_unix_base path =
|
let make_unix_base path =
|
||||||
Uri.make ~scheme:Unix.scheme ~path ()
|
Uri.make ~scheme:unix_scheme ~path ()
|
||||||
|
|
||||||
let make_tcp_base host port =
|
let make_tcp_base host port =
|
||||||
Uri.make ~scheme:Tcp.scheme ~host ~port ()
|
Uri.make ~scheme:tcp_scheme ~host ~port ()
|
||||||
|
@ -7,8 +7,12 @@
|
|||||||
(* *)
|
(* *)
|
||||||
(**************************************************************************)
|
(**************************************************************************)
|
||||||
|
|
||||||
|
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 Unix : Client_keys.SIGNER
|
||||||
module Tcp : Client_keys.SIGNER
|
module Tcp : Client_keys.SIGNER
|
||||||
|
end
|
||||||
|
|
||||||
val make_unix_base: string -> Uri.t
|
val make_unix_base: string -> Uri.t
|
||||||
val make_tcp_base: string -> int -> Uri.t
|
val make_tcp_base: string -> int -> Uri.t
|
||||||
|
@ -14,18 +14,26 @@ module Sign = struct
|
|||||||
type t = {
|
type t = {
|
||||||
pkh: Signature.Public_key_hash.t ;
|
pkh: Signature.Public_key_hash.t ;
|
||||||
data: MBytes.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 encoding =
|
||||||
let open Data_encoding in
|
let open Data_encoding in
|
||||||
conv
|
conv
|
||||||
(fun { pkh ; data } ->
|
(fun { pkh ; data ; signature } ->
|
||||||
(pkh, data))
|
(pkh, data, signature))
|
||||||
(fun (pkh, data) ->
|
(fun (pkh, data, signature) ->
|
||||||
{ pkh ; data })
|
{ pkh ; data ; signature })
|
||||||
(obj2
|
(obj3
|
||||||
(req "pkh" Signature.Public_key_hash.encoding)
|
(req "pkh" Signature.Public_key_hash.encoding)
|
||||||
(req "data" bytes))
|
(req "data" bytes)
|
||||||
|
(opt "signature" Signature.encoding))
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -62,11 +70,38 @@ module Public_key = struct
|
|||||||
|
|
||||||
end
|
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
|
module Request = struct
|
||||||
|
|
||||||
type t =
|
type t =
|
||||||
| Sign of Sign.Request.t
|
| Sign of Sign.Request.t
|
||||||
| Public_key of Public_key.Request.t
|
| Public_key of Public_key.Request.t
|
||||||
|
| Authorized_keys
|
||||||
|
|
||||||
let encoding =
|
let encoding =
|
||||||
let open Data_encoding in
|
let open Data_encoding in
|
||||||
@ -85,6 +120,11 @@ module Request = struct
|
|||||||
Public_key.Request.encoding)
|
Public_key.Request.encoding)
|
||||||
(function Public_key req -> Some ((), req) | _ -> None)
|
(function Public_key req -> Some ((), req) | _ -> None)
|
||||||
(fun ((), req) -> Public_key req) ;
|
(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
|
end
|
||||||
|
@ -13,7 +13,12 @@ module Sign : sig
|
|||||||
type t = {
|
type t = {
|
||||||
pkh: Signature.Public_key_hash.t ;
|
pkh: Signature.Public_key_hash.t ;
|
||||||
data: MBytes.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
|
val encoding : t Data_encoding.t
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -38,11 +43,23 @@ module Public_key : sig
|
|||||||
|
|
||||||
end
|
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
|
module Request : sig
|
||||||
|
|
||||||
type t =
|
type t =
|
||||||
| Sign of Sign.Request.t
|
| Sign of Sign.Request.t
|
||||||
| Public_key of Public_key.Request.t
|
| Public_key of Public_key.Request.t
|
||||||
|
| Authorized_keys
|
||||||
val encoding : t Data_encoding.t
|
val encoding : t Data_encoding.t
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -8,9 +8,19 @@
|
|||||||
(**************************************************************************)
|
(**************************************************************************)
|
||||||
|
|
||||||
let sign =
|
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
|
RPC_service.post_service
|
||||||
~description: "Sign a piece of data with a given remote key"
|
~description: "Sign a piece of data with a given remote key"
|
||||||
~query: RPC_query.empty
|
~query
|
||||||
~input: Data_encoding.bytes
|
~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)
|
RPC_path.(root /: Signature.Public_key_hash.rpc_arg)
|
||||||
@ -21,3 +31,13 @@ let public_key =
|
|||||||
~query: RPC_query.empty
|
~query: RPC_query.empty
|
||||||
~output: Data_encoding.(obj1 (req "public_key" Signature.Public_key.encoding))
|
~output: Data_encoding.(obj1 (req "public_key" Signature.Public_key.encoding))
|
||||||
RPC_path.(root /: Signature.Public_key_hash.rpc_arg)
|
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")
|
||||||
|
@ -9,8 +9,12 @@
|
|||||||
|
|
||||||
val sign :
|
val sign :
|
||||||
([ `POST ], unit, unit * Signature.Public_key_hash.t,
|
([ `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 :
|
val public_key :
|
||||||
([ `GET ], unit, unit * Signature.Public_key_hash.t,
|
([ `GET ], unit, unit * Signature.Public_key_hash.t,
|
||||||
unit, unit, Signature.Public_key.t) RPC_service.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
|
||||||
|
Loading…
Reference in New Issue
Block a user