From 5909750f874e4c84f9ad281784459fd2c1c7e561 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Tue, 25 Feb 2025 19:13:45 +0100 Subject: [PATCH 1/5] Signer/client: introducing versionning of BLS keys --- src/bin_signer/handler.ml | 17 ++- src/bin_signer/http_daemon.ml | 9 +- src/lib_client_base/client_keys.ml | 11 +- src/lib_client_base/client_keys.mli | 1 + src/lib_client_base_unix/client_main_run.ml | 2 +- src/lib_crypto/signature.ml | 39 ++++++- src/lib_crypto/signature.mli | 20 +++- src/lib_signer_backends/encrypted.ml | 29 ++++- src/lib_signer_backends/http_gen.ml | 4 +- src/lib_signer_backends/unencrypted.ml | 29 ++++- .../unix/ledger.available.ml | 4 +- src/lib_signer_backends/unix/ledger.none.ml | 3 +- src/lib_signer_backends/unix/remote.ml | 4 +- src/lib_signer_backends/unix/socket.ml | 35 ++++-- src/lib_signer_services/signer_messages.ml | 103 +++++++++++++++++- src/lib_signer_services/signer_messages.mli | 24 +++- src/lib_signer_services/signer_services.ml | 28 ++++- src/lib_signer_services/signer_services.mli | 2 +- 18 files changed, 320 insertions(+), 44 deletions(-) diff --git a/src/bin_signer/handler.ml b/src/bin_signer/handler.ml index b5e261b670ee..68d88c5e8d36 100644 --- a/src/bin_signer/handler.ml +++ b/src/bin_signer/handler.ml @@ -268,6 +268,11 @@ let sign ?magic_bytes ~check_high_watermark ~require_auth (cctxt : #Client_context.wallet) Signer_messages.Sign.Request.{pkh; data; signature} = let open Lwt_result_syntax in + let pkh, version = + match pkh with + | Pkh pkh -> (pkh, None) + | Pkh_with_version (pkh, version) -> (pkh, Some version) + in let*! () = Events.(emit request_for_signing) (Bytes.length data, pkh, TzEndian.get_uint8 data 0) @@ -276,7 +281,17 @@ let sign ?magic_bytes ~check_high_watermark ~require_auth let* () = check_magic_byte name magic_bytes data in let* () = check_authorization cctxt pkh data require_auth signature in let*! () = Events.(emit signing_data) name in - let sign = Client_keys.sign cctxt sk_uri in + let sign bytes = + match version with + | Some Version_0 -> + let* s = Client_keys.V0.sign cctxt sk_uri bytes in + return (Signature.V_latest.Of_V0.signature s) + | Some Version_1 -> + let* s = Client_keys.V1.sign cctxt sk_uri bytes in + return (Signature.V_latest.Of_V1.signature s) + | Some Version_2 -> Client_keys.V2.sign cctxt sk_uri bytes + | None -> Client_keys.V_latest.sign cctxt sk_uri bytes + in if check_high_watermark then High_watermark.mark_if_block_or_endorsement cctxt pkh data sign else sign data diff --git a/src/bin_signer/http_daemon.ml b/src/bin_signer/http_daemon.ml index beb84db698b5..15a16591d376 100644 --- a/src/bin_signer/http_daemon.ml +++ b/src/bin_signer/http_daemon.ml @@ -33,7 +33,14 @@ let run (cctxt : #Client_context.wallet) ~hosts ?magic_bytes Tezos_rpc.Directory.register1 dir Signer_services.sign - (fun pkh signature data -> + (fun pkh (signature, version) data -> + let pkh = + match (pkh, version) with + | _, None -> Signer_messages.Pkh pkh + | (Ed25519 _ | Secp256k1 _ | P256 _), Some _ -> + Signer_messages.Pkh pkh + | Bls _, Some version -> Pkh_with_version (pkh, version) + in Handler.sign ?magic_bytes ~check_high_watermark diff --git a/src/lib_client_base/client_keys.ml b/src/lib_client_base/client_keys.ml index bb8213e143aa..b8e792bad955 100644 --- a/src/lib_client_base/client_keys.ml +++ b/src/lib_client_base/client_keys.ml @@ -295,6 +295,8 @@ module type Signature_S = sig val concat : Bytes.t -> t -> Bytes.t + val version : Tezos_crypto.Signature.version + module Adapter : sig val public_key_hash : Tezos_crypto.Signature.Public_key_hash.t -> Public_key_hash.t tzresult @@ -310,6 +312,7 @@ module type SIMPLE_SIGNER = sig include COMMON_SIGNER with type pk_uri = pk_uri and type sk_uri = sk_uri val sign : + ?version:Tezos_crypto.Signature.version -> ?watermark:Tezos_crypto.Signature.watermark -> sk_uri -> Bytes.t -> @@ -599,9 +602,9 @@ module Make (Signature : Signature_S) : let*? pk = Option.map_e Signature.Adapter.public_key pk in return (pkh, pk) - let sign ?watermark sk msg = + let sign ?version ?watermark sk msg = let open Lwt_result_syntax in - let* signature = S.sign ?watermark sk msg in + let* signature = S.sign ?version ?watermark sk msg in let*? signature = Signature.Adapter.signature signature in return signature @@ -658,7 +661,9 @@ module Make (Signature : Signature_S) : let sign cctxt ?watermark sk_uri buf = let open Lwt_result_syntax in with_scheme_simple_signer sk_uri (fun (module Signer) -> - let* signature = Signer.sign ?watermark sk_uri buf in + let* signature = + Signer.sign ~version:Signature.version ?watermark sk_uri buf + in let* pk_uri = Signer.neuterize sk_uri in let* pubkey = let* o = Secret_key.rev_find cctxt sk_uri in diff --git a/src/lib_client_base/client_keys.mli b/src/lib_client_base/client_keys.mli index cf9cdb76bb80..385dba2d7dda 100644 --- a/src/lib_client_base/client_keys.mli +++ b/src/lib_client_base/client_keys.mli @@ -131,6 +131,7 @@ module type SIGNER = sig (** [sign ?watermark sk data] is signature obtained by signing [data] with [sk]. *) val sign : + ?version:Tezos_crypto.Signature.version -> ?watermark:Tezos_crypto.Signature.watermark -> sk_uri -> Bytes.t -> diff --git a/src/lib_client_base_unix/client_main_run.ml b/src/lib_client_base_unix/client_main_run.ml index 9fbdfd8826ca..1ccda4bf0225 100644 --- a/src/lib_client_base_unix/client_main_run.ml +++ b/src/lib_client_base_unix/client_main_run.ml @@ -104,7 +104,7 @@ let register_default_signer ?other_registrations ?logger | _ -> None) keys with - | sk_uri :: _ -> Client_keys.sign cctxt sk_uri payload + | sk_uri :: _ -> Client_keys.V2.sign cctxt sk_uri payload | [] -> failwith "remote signer expects authentication signature, but no authorized \ diff --git a/src/lib_crypto/signature.ml b/src/lib_crypto/signature.ml index ccac7d958a26..f87d15f63bdb 100644 --- a/src/lib_crypto/signature.ml +++ b/src/lib_crypto/signature.ml @@ -25,6 +25,33 @@ (* *) (*****************************************************************************) +type version = Version_0 | Version_1 | Version_2 + +let version_encoding = + let open Data_encoding in + conv_with_guard + (function Version_0 -> 0 | Version_1 -> 1 | Version_2 -> 2) + (function + | 0 -> Ok Version_0 + | 1 -> Ok Version_1 + | 2 -> Ok Version_2 + | _ -> Error "Invalid signature version") + int8 + +let version_arg = + let open Tezos_rpc.Arg in + make + ~descr:"Supported signature version are version '0','1' and '2'(default)" + ~name:"version" + ~destruct:(function + | "0" -> Ok Version_0 + | "1" -> Ok Version_1 + | "2" -> Ok Version_2 + | _ -> Error "Invalid signature version") + ~construct:(function + | Version_0 -> "0" | Version_1 -> "1" | Version_2 -> "2") + () + module type CONV = sig module V_from : S.COMMON_SIGNATURE @@ -69,11 +96,17 @@ module type CONV_OPT = sig val get_signature_exn : V_from.t -> V_to.t end -module V_latest = Signature_v2 +module V_latest = struct + let version = Version_2 + + include Signature_v2 +end module V0 = struct include Signature_v0 + let version = Version_0 + module Of_V_latest : CONV_OPT with module V_from := V_latest and module V_to := Signature_v0 = struct @@ -156,6 +189,8 @@ end module V1 = struct include Signature_v1 + let version = Version_1 + module Of_V_latest : CONV_OPT with module V_from := V_latest and module V_to := Signature_v1 = struct @@ -238,6 +273,8 @@ end module V2 = struct include Signature_v2 + let version = Version_2 + module Of_V_latest : CONV_OPT with module V_from := V_latest and module V_to := Signature_v2 = struct diff --git a/src/lib_crypto/signature.mli b/src/lib_crypto/signature.mli index e7faf48a6f95..af89933f7c14 100644 --- a/src/lib_crypto/signature.mli +++ b/src/lib_crypto/signature.mli @@ -28,6 +28,12 @@ (** Cryptographic signatures are versioned to expose different versions to different protocols, depending on the support. *) +type version = Version_0 | Version_1 | Version_2 + +val version_encoding : version Data_encoding.t + +val version_arg : version Resto.Arg.t + (** The type of conversion modules from one version to another. *) module type CONV = sig module V_from : S.COMMON_SIGNATURE @@ -76,10 +82,16 @@ end (** The module [V_latest] is to be used by the shell and points to the latest available version of signatures. *) -module V_latest : module type of Signature_v2 +module V_latest : sig + val version : version + + include module type of Signature_v2 +end (** [V0] supports Ed25519, Secp256k1, and P256. *) module V0 : sig + val version : version + include module type of Signature_v0 (** Converting from signatures of {!V_latest} to {!V0}. *) @@ -89,6 +101,8 @@ end (** [V1] supports Ed25519, Secp256k1, P256, and BLS (aug). *) module V1 : sig + val version : version + include module type of Signature_v1 (** Converting from signatures of {!V_latest} to {!V1}. *) @@ -98,6 +112,8 @@ end (** [V2] supports Ed25519, Secp256k1, P256, and BLS (aug). *) module V2 : sig + val version : version + include module type of Signature_v2 (** Converting from signatures of {!V_latest} to {!V2}. *) @@ -110,7 +126,7 @@ include module type of V_latest (** Converting from signatures of {!V_latest} to {!V_latest}. This module implements conversions which are the identity, so total, but we keep the signature as {!CONV_OPT} for compatibility with {!V0.Of_V_latest} and - {!V1.Of_V_latest} and to ease snapshotting. TODO ADAPT THIS WITH V2*) + {!V1.Of_V_latest} and to ease snapshotting. *) module Of_V_latest : CONV_OPT with module V_from := V_latest and module V_to := V_latest diff --git a/src/lib_signer_backends/encrypted.ml b/src/lib_signer_backends/encrypted.ml index 5ac98b89f231..79e0f9cb4c3d 100644 --- a/src/lib_signer_backends/encrypted.ml +++ b/src/lib_signer_backends/encrypted.ml @@ -504,10 +504,35 @@ struct let*? v = Unencrypted.make_pk (Signature.Secret_key.to_public_key sk) in return v - let sign ?watermark sk_uri buf = + let sign ?version ?watermark sk_uri buf = let open Lwt_result_syntax in let* sk = decrypt C.cctxt sk_uri in - return (Signature.sign ?watermark sk buf) + match version with + | Some Tezos_crypto.Signature.Version_0 -> ( + match Tezos_crypto.Signature.V0.Of_V_latest.secret_key sk with + | Some sk -> + let s = Tezos_crypto.Signature.V0.sign ?watermark sk buf in + return (Signature.V_latest.Of_V0.signature s) + | None -> + Error_monad.failwith + "Failed to handle secret key in Signature version 0") + | Some Version_1 -> ( + match Tezos_crypto.Signature.V1.Of_V_latest.secret_key sk with + | Some sk -> + let s = Tezos_crypto.Signature.V1.sign ?watermark sk buf in + return (Signature.V_latest.Of_V1.signature s) + | None -> + Error_monad.failwith + "Failed to handle secret key in Signature version 1") + | Some Version_2 -> ( + match Tezos_crypto.Signature.V2.Of_V_latest.secret_key sk with + | Some sk -> + let s = Tezos_crypto.Signature.V2.sign ?watermark sk buf in + return s + | None -> + Error_monad.failwith + "Failed to handle secret key in Signature version 2") + | None -> return (Tezos_crypto.Signature.V_latest.sign ?watermark sk buf) let deterministic_nonce sk_uri buf = let open Lwt_result_syntax in diff --git a/src/lib_signer_backends/http_gen.ml b/src/lib_signer_backends/http_gen.ml index 8979416c1e93..43cb1d098486 100644 --- a/src/lib_signer_backends/http_gen.ml +++ b/src/lib_signer_backends/http_gen.ml @@ -160,7 +160,7 @@ struct return_some signature | None -> return_none - let sign ?watermark uri msg = + let sign ?version ?watermark uri msg = let open Lwt_result_syntax in let* base, pkh = parse (uri : sk_uri :> Uri.t) in let msg = @@ -177,7 +177,7 @@ struct ~base Signer_services.sign ((), pkh) - signature + (signature, version) msg let deterministic_nonce uri msg = diff --git a/src/lib_signer_backends/unencrypted.ml b/src/lib_signer_backends/unencrypted.ml index d3d4cef8673e..7b382916198a 100644 --- a/src/lib_signer_backends/unencrypted.ml +++ b/src/lib_signer_backends/unencrypted.ml @@ -77,10 +77,35 @@ let public_key_hash pk_uri = let import_secret_key ~io:_ = public_key_hash -let sign ?watermark sk_uri buf = +let sign ?version ?watermark sk_uri buf = let open Lwt_result_syntax in let* sk = secret_key sk_uri in - return (Signature.sign ?watermark sk buf) + match version with + | Some Tezos_crypto.Signature.Version_0 -> ( + match Tezos_crypto.Signature.V0.Of_V_latest.secret_key sk with + | Some sk -> + let s = Tezos_crypto.Signature.V0.sign ?watermark sk buf in + return (Signature.V_latest.Of_V0.signature s) + | None -> + Error_monad.failwith + "Failed to handle secret key in Signature version 0") + | Some Version_1 -> ( + match Tezos_crypto.Signature.V1.Of_V_latest.secret_key sk with + | Some sk -> + let s = Tezos_crypto.Signature.V1.sign ?watermark sk buf in + return (Signature.V_latest.Of_V1.signature s) + | None -> + Error_monad.failwith + "Failed to handle secret key in Signature version 1") + | Some Version_2 -> ( + match Tezos_crypto.Signature.V2.Of_V_latest.secret_key sk with + | Some sk -> + let s = Tezos_crypto.Signature.V2.sign ?watermark sk buf in + return s + | None -> + Error_monad.failwith + "Failed to handle secret key in Signature version 2") + | None -> return (Tezos_crypto.Signature.V_latest.sign ?watermark sk buf) let deterministic_nonce sk_uri buf = let open Lwt_result_syntax in diff --git a/src/lib_signer_backends/unix/ledger.available.ml b/src/lib_signer_backends/unix/ledger.available.ml index 1a7c72bb00bd..00207d02b1ea 100644 --- a/src/lib_signer_backends/unix/ledger.available.ml +++ b/src/lib_signer_backends/unix/ledger.available.ml @@ -832,7 +832,9 @@ module Signer_implementation : Client_keys.SIGNER = struct let import_secret_key ~io pk_uri = public_key_hash_maybe_prompt ~first_import:io pk_uri - let sign ?watermark (sk_uri : sk_uri) msg = + let sign ?version:_ ?watermark (sk_uri : sk_uri) msg = + (* TODO: https://gitlab.com/tezos/tezos/-/issues/7812 + handle version *) let open Lwt_result_syntax in let* ledger_uri = Ledger_uri.parse (sk_uri :> Uri.t) in let* {curve; path; _} = Ledger_uri.full_account ledger_uri in diff --git a/src/lib_signer_backends/unix/ledger.none.ml b/src/lib_signer_backends/unix/ledger.none.ml index b88c4923dcf9..028b515837a7 100644 --- a/src/lib_signer_backends/unix/ledger.none.ml +++ b/src/lib_signer_backends/unix/ledger.none.ml @@ -60,7 +60,8 @@ module Signer_implementation : Client_keys.SIGNER = struct let import_secret_key ~io:_ _pk_uri = Lwt_result_syntax.tzfail NoLedgerSupport - let sign ?watermark:_k _sk_uri _msg = Lwt_result_syntax.tzfail NoLedgerSupport + let sign ?version:_v ?watermark:_k _sk_uri _msg = + Lwt_result_syntax.tzfail NoLedgerSupport let deterministic_nonce _sk_uri _msg = Lwt_result_syntax.tzfail NoLedgerSupport diff --git a/src/lib_signer_backends/unix/remote.ml b/src/lib_signer_backends/unix/remote.ml index ab243fe69e7a..3b2286122c29 100644 --- a/src/lib_signer_backends/unix/remote.ml +++ b/src/lib_signer_backends/unix/remote.ml @@ -105,10 +105,10 @@ struct let*? v = Client_keys.make_pk_uri (sk_uri : sk_uri :> Uri.t) in return v - let sign ?watermark sk_uri msg = + let sign ?version ?watermark sk_uri msg = let open Lwt_result_syntax in let*? sk_uri = Client_keys.make_sk_uri (key (sk_uri : sk_uri :> Uri.t)) in - Remote.sign ?watermark sk_uri msg + Remote.sign ?version ?watermark sk_uri msg let deterministic_nonce sk_uri msg = let open Lwt_result_syntax in diff --git a/src/lib_signer_backends/unix/socket.ml b/src/lib_signer_backends/unix/socket.ml index bac66775a689..8f1cc52da08e 100644 --- a/src/lib_signer_backends/unix/socket.ml +++ b/src/lib_signer_backends/unix/socket.ml @@ -45,8 +45,17 @@ struct | Deterministic_nonce_request | Deterministic_nonce_hash_request - let build_request pkh data signature = function - | Sign_request -> Request.Sign {Sign.Request.pkh; data; signature} + let build_request ?version (pkh : Tezos_crypto.Signature.public_key_hash) data + signature request = + match request with + | Sign_request -> + let pkh = + match (pkh, version) with + | _, None -> Pkh pkh + | (Ed25519 _ | Secp256k1 _ | P256 _), Some _ -> Pkh pkh + | Bls _, Some version -> Pkh_with_version (pkh, version) + in + Request.Sign {Sign.Request.pkh; data; signature} | Deterministic_nonce_request -> Request.Deterministic_nonce {Deterministic_nonce.Request.pkh; data; signature} @@ -73,12 +82,12 @@ struct in return_some signature - let with_signer_operation path pkh msg request_type enc = + let with_signer_operation ?version path pkh msg request_type enc = let open Lwt_result_syntax in let f () = Tezos_base_unix.Socket.with_connection path (fun conn -> let* signature = maybe_authenticate pkh msg conn in - let req = build_request pkh msg signature request_type in + let req = build_request ?version pkh msg signature request_type in let* () = Tezos_base_unix.Socket.send conn Request.encoding req in Tezos_base_unix.Socket.recv conn (result_encoding enc)) in @@ -98,14 +107,20 @@ struct in loop 3 - let sign ?watermark path pkh msg = + let sign ?version ?watermark path pkh msg = let msg = match watermark with | None -> msg | Some watermark -> Bytes.cat (Tezos_crypto.Signature.bytes_of_watermark watermark) msg in - with_signer_operation path pkh msg Sign_request Sign.Response.encoding + with_signer_operation + ?version + path + pkh + msg + Sign_request + Sign.Response.encoding let deterministic_nonce path pkh msg = with_signer_operation @@ -191,10 +206,10 @@ struct let import_secret_key ~io:_ = public_key_hash - let sign ?watermark uri msg = + let sign ?version ?watermark uri msg = let open Lwt_result_syntax in let* path, pkh = parse (uri : sk_uri :> Uri.t) in - sign ?watermark path pkh msg + sign ?version ?watermark path pkh msg let deterministic_nonce uri msg = let open Lwt_result_syntax in @@ -258,10 +273,10 @@ struct let import_secret_key ~io:_ = public_key_hash - let sign ?watermark uri msg = + let sign ?version ?watermark uri msg = let open Lwt_result_syntax in let* path, pkh = parse (uri : sk_uri :> Uri.t) in - sign ?watermark path pkh msg + sign ?version ?watermark path pkh msg let deterministic_nonce uri msg = let open Lwt_result_syntax in diff --git a/src/lib_signer_services/signer_messages.ml b/src/lib_signer_services/signer_messages.ml index 38d0fb282d78..5169f06175ac 100644 --- a/src/lib_signer_services/signer_messages.ml +++ b/src/lib_signer_services/signer_messages.ml @@ -23,9 +23,59 @@ (* *) (*****************************************************************************) -module type Authenticated_request = sig +type pkh = + | Pkh_with_version of + Tezos_crypto.Signature.Public_key_hash.t * Tezos_crypto.Signature.version + | Pkh of Tezos_crypto.Signature.Public_key_hash.t + +(* This encoding should be kept compatible with the one in + [Lib_crypto.Signature_V.raw_encoding] especially if a new case is + added. *) +let pkh_encoding = + let open Data_encoding in + def + "signer_messages.public_key_hash" + ~description:"signer messages public key hash encoding" + @@ union + [ + case + (Tag 0) + Tezos_crypto.Signature.Ed25519.Public_key_hash.encoding + ~title:"Ed25519" + (function Pkh (Ed25519 x) -> Some x | _ -> None) + (function x -> Pkh (Ed25519 x)); + case + (Tag 1) + Tezos_crypto.Signature.Secp256k1.Public_key_hash.encoding + ~title:"Secp256k1" + (function Pkh (Secp256k1 x) -> Some x | _ -> None) + (function x -> Pkh (Secp256k1 x)); + case + (Tag 2) + ~title:"P256" + Tezos_crypto.Signature.P256.Public_key_hash.encoding + (function Pkh (P256 x) -> Some x | _ -> None) + (function x -> Pkh (P256 x)); + case + (Tag 3) + ~title:"Bls" + (conv + (fun (pkh, version) -> (pkh, version)) + (fun (pkh, version) -> (pkh, version)) + (obj2 + (req "pkh" Tezos_crypto.Signature.Public_key_hash.encoding) + (req "version" Tezos_crypto.Signature.version_encoding))) + (function + | Pkh (Bls _ as x) -> + Some (x, Tezos_crypto.Signature.V_latest.version) + | Pkh_with_version ((Bls _ as x), version) -> Some (x, version) + | _ -> None) + (function x, version -> Pkh_with_version (x, version)); + ] + +module type Authenticated_signing_request = sig type t = { - pkh : Tezos_crypto.Signature.Public_key_hash.t; + pkh : pkh; data : Bytes.t; signature : Tezos_crypto.Signature.t option; } @@ -40,9 +90,10 @@ module type Tag = sig val tag : int end -module Make_authenticated_request (T : Tag) : Authenticated_request = struct +module Make_authenticated_signing_request (T : Tag) : + Authenticated_signing_request = struct type t = { - pkh : Tezos_crypto.Signature.Public_key_hash.t; + pkh : pkh; data : Bytes.t; signature : Tezos_crypto.Signature.t option; } @@ -62,13 +113,13 @@ module Make_authenticated_request (T : Tag) : Authenticated_request = struct (fun {pkh; data; signature} -> (pkh, data, signature)) (fun (pkh, data, signature) -> {pkh; data; signature}) (obj3 - (req "pkh" Tezos_crypto.Signature.Public_key_hash.encoding) + (req "pkh" pkh_encoding) (req "data" bytes) (opt "signature" Tezos_crypto.Signature.encoding)) end module Sign = struct - module Request = Make_authenticated_request (struct + module Request = Make_authenticated_signing_request (struct let tag = 1 end) @@ -82,6 +133,46 @@ module Sign = struct end end +module type Authenticated_request = sig + type t = { + pkh : Tezos_crypto.Signature.Public_key_hash.t; + data : Bytes.t; + signature : Tezos_crypto.Signature.t option; + } + + val to_sign : + pkh:Tezos_crypto.Signature.Public_key_hash.t -> data:Bytes.t -> Bytes.t + + val encoding : t Data_encoding.t +end + +module Make_authenticated_request (T : Tag) : Authenticated_request = struct + type t = { + pkh : Tezos_crypto.Signature.Public_key_hash.t; + data : Bytes.t; + signature : Tezos_crypto.Signature.t option; + } + + let x04 = Bytes.of_string "\x04" + + let to_sign ~pkh ~data = + let tag = Bytes.make 1 '0' in + TzEndian.set_int8 tag 0 T.tag ; + Bytes.concat + Bytes.empty + [x04; tag; Tezos_crypto.Signature.Public_key_hash.to_bytes pkh; data] + + let encoding = + let open Data_encoding in + conv + (fun {pkh; data; signature} -> (pkh, data, signature)) + (fun (pkh, data, signature) -> {pkh; data; signature}) + (obj3 + (req "pkh" Tezos_crypto.Signature.Public_key_hash.encoding) + (req "data" bytes) + (opt "signature" Tezos_crypto.Signature.encoding)) +end + module Deterministic_nonce = struct module Request = Make_authenticated_request (struct let tag = 2 diff --git a/src/lib_signer_services/signer_messages.mli b/src/lib_signer_services/signer_messages.mli index fa98fde95543..5cacd68bd318 100644 --- a/src/lib_signer_services/signer_messages.mli +++ b/src/lib_signer_services/signer_messages.mli @@ -23,9 +23,14 @@ (* *) (*****************************************************************************) -module type Authenticated_request = sig +type pkh = + | Pkh_with_version of + Tezos_crypto.Signature.Public_key_hash.t * Tezos_crypto.Signature.version + | Pkh of Tezos_crypto.Signature.Public_key_hash.t + +module type Authenticated_signing_request = sig type t = { - pkh : Tezos_crypto.Signature.Public_key_hash.t; + pkh : pkh; data : Bytes.t; signature : Tezos_crypto.Signature.t option; } @@ -37,7 +42,7 @@ module type Authenticated_request = sig end module Sign : sig - module Request : Authenticated_request + module Request : Authenticated_signing_request module Response : sig type t = Tezos_crypto.Signature.t @@ -46,6 +51,19 @@ module Sign : sig end end +module type Authenticated_request = sig + type t = { + pkh : Tezos_crypto.Signature.Public_key_hash.t; + data : Bytes.t; + signature : Tezos_crypto.Signature.t option; + } + + val to_sign : + pkh:Tezos_crypto.Signature.Public_key_hash.t -> data:Bytes.t -> Bytes.t + + val encoding : t Data_encoding.t +end + module Deterministic_nonce : sig module Request : Authenticated_request diff --git a/src/lib_signer_services/signer_services.ml b/src/lib_signer_services/signer_services.ml index 3ab2a4f3ce57..ca5f98c50ff3 100644 --- a/src/lib_signer_services/signer_services.ml +++ b/src/lib_signer_services/signer_services.ml @@ -23,23 +23,41 @@ (* *) (*****************************************************************************) +let signature_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." + let query = let open Tezos_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." + ~descr:signature_descr "authentication" Tezos_crypto.Signature.rpc_arg (fun signature -> signature) |> seal +let sign_query = + let open Tezos_rpc.Query in + query (fun signature version -> (signature, version)) + |+ opt_field + ~descr:signature_descr + "authentication" + Tezos_crypto.Signature.rpc_arg + fst + |+ opt_field + ~descr: + "Define the signature version that need to be used to sign the data" + "version" + Tezos_crypto.Signature.version_arg + snd + |> seal + let sign = Tezos_rpc.Service.post_service ~description:"Sign a piece of data with a given remote key" - ~query + ~query:sign_query ~input:Data_encoding.bytes ~output: Data_encoding.(obj1 (req "signature" Tezos_crypto.Signature.encoding)) diff --git a/src/lib_signer_services/signer_services.mli b/src/lib_signer_services/signer_services.mli index f4ee202da98b..756fa91eb85b 100644 --- a/src/lib_signer_services/signer_services.mli +++ b/src/lib_signer_services/signer_services.mli @@ -27,7 +27,7 @@ val sign : ( [`POST], unit, unit * Tezos_crypto.Signature.Public_key_hash.t, - Tezos_crypto.Signature.t option, + Tezos_crypto.Signature.t option * Signature.version option, Bytes.t, Tezos_crypto.Signature.t ) Tezos_rpc.Service.t -- GitLab From 90530a6d75188edebcca9b2bf59ce3933bc1ac7f Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Fri, 28 Feb 2025 16:05:52 +0100 Subject: [PATCH 2/5] kaitai: update struct --- .../files/signer_messages__request.ksy | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/client-libs/kaitai-struct-files/files/signer_messages__request.ksy b/client-libs/kaitai-struct-files/files/signer_messages__request.ksy index 33ab87f6034b..2820eef636c1 100644 --- a/client-libs/kaitai-struct-files/files/signer_messages__request.ksy +++ b/client-libs/kaitai-struct-files/files/signer_messages__request.ksy @@ -3,6 +3,13 @@ meta: endian: be doc: ! 'Encoding id: signer_messages.request' types: + bls: + seq: + - id: pkh + type: public_key_hash + doc: A Ed25519, Secp256k1, P256, or BLS public key hash + - id: version + type: s1 bytes_dyn_uint30: seq: - id: len_bytes_dyn_uint30 @@ -57,8 +64,8 @@ types: sign: seq: - id: pkh - type: public_key_hash - doc: A Ed25519, Secp256k1, P256, or BLS public key hash + type: signer_messages__public_key_hash + doc: signer messages public key hash encoding - id: data type: bytes_dyn_uint30 - id: signature_tag @@ -72,6 +79,23 @@ types: - id: pkh type: public_key_hash doc: A Ed25519, Secp256k1, P256, or BLS public key hash + signer_messages__public_key_hash: + seq: + - id: signer_messages__public_key_hash_tag + type: u1 + enum: signer_messages__public_key_hash_tag + - id: ed25519 + size: 20 + if: (signer_messages__public_key_hash_tag == signer_messages__public_key_hash_tag::ed25519) + - id: secp256k1 + size: 20 + if: (signer_messages__public_key_hash_tag == signer_messages__public_key_hash_tag::secp256k1) + - id: p256 + size: 20 + if: (signer_messages__public_key_hash_tag == signer_messages__public_key_hash_tag::p256) + - id: bls + type: bls + if: (signer_messages__public_key_hash_tag == signer_messages__public_key_hash_tag::bls) signer_messages__supports_deterministic_nonces__request: seq: - id: pkh @@ -86,6 +110,11 @@ enums: 1: secp256k1 2: p256 3: bls + signer_messages__public_key_hash_tag: + 0: ed25519 + 1: secp256k1 + 2: p256 + 3: bls signer_messages__request_tag: 0: sign 1: public_key -- GitLab From bbb840cdfd6eee78c1fd516a4e6d2f646bcfaf78 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Fri, 28 Feb 2025 16:06:07 +0100 Subject: [PATCH 3/5] bin_signer: add signing-version argument to force usage of a specific signature version --- src/bin_signer/handler.ml | 10 +++++++--- src/bin_signer/handler.mli | 1 + src/bin_signer/http_daemon.ml | 23 ++++++++++++----------- src/bin_signer/http_daemon.mli | 2 ++ src/bin_signer/main_signer.ml | 13 +++++++++++++ src/bin_signer/socket_daemon.ml | 22 +++++++++++++++------- src/bin_signer/socket_daemon.mli | 1 + 7 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/bin_signer/handler.ml b/src/bin_signer/handler.ml index 68d88c5e8d36..9b0fdbfad485 100644 --- a/src/bin_signer/handler.ml +++ b/src/bin_signer/handler.ml @@ -264,14 +264,18 @@ let check_authorization cctxt pkh data require_auth signature = then return_unit else failwith "invalid authentication signature" -let sign ?magic_bytes ~check_high_watermark ~require_auth +let sign ?signing_version ?magic_bytes ~check_high_watermark ~require_auth (cctxt : #Client_context.wallet) Signer_messages.Sign.Request.{pkh; data; signature} = let open Lwt_result_syntax in let pkh, version = match pkh with - | Pkh pkh -> (pkh, None) - | Pkh_with_version (pkh, version) -> (pkh, Some version) + | Pkh pkh -> (pkh, signing_version) + | Pkh_with_version (pkh, req_version) -> + ( pkh, + match signing_version with + | Some _v -> signing_version + | None -> Some req_version ) in let*! () = Events.(emit request_for_signing) diff --git a/src/bin_signer/handler.mli b/src/bin_signer/handler.mli index 2ac8232640d5..520942348bf2 100644 --- a/src/bin_signer/handler.mli +++ b/src/bin_signer/handler.mli @@ -37,6 +37,7 @@ val public_key : (** [sign cctxt req ?magic_bytes ~check_high_watermark ~require_auth] signs [req] and returns a signature. *) val sign : + ?signing_version:Signature.version -> ?magic_bytes:int list -> check_high_watermark:bool -> require_auth:bool -> diff --git a/src/bin_signer/http_daemon.ml b/src/bin_signer/http_daemon.ml index 15a16591d376..6a145e11567b 100644 --- a/src/bin_signer/http_daemon.ml +++ b/src/bin_signer/http_daemon.ml @@ -25,7 +25,7 @@ module Events = Signer_events.Http_daemon -let run (cctxt : #Client_context.wallet) ~hosts ?magic_bytes +let run (cctxt : #Client_context.wallet) ~hosts ?signing_version ?magic_bytes ~check_high_watermark ~require_auth mode = let open Lwt_result_syntax in let dir = Tezos_rpc.Directory.empty in @@ -33,15 +33,14 @@ let run (cctxt : #Client_context.wallet) ~hosts ?magic_bytes Tezos_rpc.Directory.register1 dir Signer_services.sign - (fun pkh (signature, version) data -> + (fun pkh (signature, req_version) data -> let pkh = - match (pkh, version) with - | _, None -> Signer_messages.Pkh pkh - | (Ed25519 _ | Secp256k1 _ | P256 _), Some _ -> - Signer_messages.Pkh pkh - | Bls _, Some version -> Pkh_with_version (pkh, version) + match req_version with + | Some version -> Signer_messages.Pkh_with_version (pkh, version) + | None -> Signer_messages.Pkh pkh in Handler.sign + ?signing_version ?magic_bytes ~check_high_watermark ~require_auth @@ -92,8 +91,8 @@ let run (cctxt : #Client_context.wallet) ~hosts ?magic_bytes failwith "Port already in use." | exn -> fail_with_exn exn) -let run_https ~host ~port ~cert ~key ?magic_bytes ~check_high_watermark - ~require_auth (cctxt : #Client_context.wallet) = +let run_https ~host ~port ~cert ~key ?signing_version ?magic_bytes + ~check_high_watermark ~require_auth (cctxt : #Client_context.wallet) = let open Lwt_syntax in let* points = Lwt_utils_unix.getaddrinfo @@ -112,13 +111,14 @@ let run_https ~host ~port ~cert ~key ?magic_bytes ~check_high_watermark run (cctxt : #Client_context.wallet) ~hosts + ?signing_version ?magic_bytes ~check_high_watermark ~require_auth mode -let run_http ~host ~port ?magic_bytes ~check_high_watermark ~require_auth - (cctxt : #Client_context.wallet) = +let run_http ~host ~port ?signing_version ?magic_bytes ~check_high_watermark + ~require_auth (cctxt : #Client_context.wallet) = let open Lwt_syntax in let* points = Lwt_utils_unix.getaddrinfo @@ -135,6 +135,7 @@ let run_http ~host ~port ?magic_bytes ~check_high_watermark ~require_auth run (cctxt : #Client_context.wallet) ~hosts + ?signing_version ?magic_bytes ~check_high_watermark ~require_auth diff --git a/src/bin_signer/http_daemon.mli b/src/bin_signer/http_daemon.mli index acc2c838e40c..fc0c9e9b2ef0 100644 --- a/src/bin_signer/http_daemon.mli +++ b/src/bin_signer/http_daemon.mli @@ -28,6 +28,7 @@ val run_https : port:int -> cert:string -> key:string -> + ?signing_version:Signature.version -> ?magic_bytes:int list -> check_high_watermark:bool -> require_auth:bool -> @@ -37,6 +38,7 @@ val run_https : val run_http : host:string -> port:int -> + ?signing_version:Signature.version -> ?magic_bytes:int list -> check_high_watermark:bool -> require_auth:bool -> diff --git a/src/bin_signer/main_signer.ml b/src/bin_signer/main_signer.ml index e92a48a86139..04ac02e2548c 100644 --- a/src/bin_signer/main_signer.ml +++ b/src/bin_signer/main_signer.ml @@ -54,6 +54,15 @@ let default_http_port = | None -> "6732" | Some port -> port +let signing_version_for_test = + Option.map + (function + | "0" -> Signature.Version_0 + | "1" -> Signature.Version_1 + | "2" -> Signature.Version_2 + | _ -> Signature.V_latest.version) + (Sys.getenv_opt "TEZOS_SIGNER_SIGNING_VERSION") + let group = { Tezos_clic.name = "signer"; @@ -158,6 +167,7 @@ let commands base_dir require_auth : Client_context.full Tezos_clic.command list cctxt (Tcp (host, port, [AI_SOCKTYPE SOCK_STREAM])) ?magic_bytes + ?signing_version:signing_version_for_test ~check_high_watermark ~require_auth ~timeout @@ -186,6 +196,7 @@ let commands base_dir require_auth : Client_context.full Tezos_clic.command list cctxt (Unix path) ?magic_bytes + ?signing_version:signing_version_for_test ~check_high_watermark ~require_auth in @@ -222,6 +233,7 @@ let commands base_dir require_auth : Client_context.full Tezos_clic.command list ~host ~port ?magic_bytes + ?signing_version:signing_version_for_test ~check_high_watermark ~require_auth); command @@ -276,6 +288,7 @@ let commands base_dir require_auth : Client_context.full Tezos_clic.command list ~cert ~key ?magic_bytes + ?signing_version:signing_version_for_test ~check_high_watermark ~require_auth); command diff --git a/src/bin_signer/socket_daemon.ml b/src/bin_signer/socket_daemon.ml index 54bf6817dfd1..7a4cdba07dd5 100644 --- a/src/bin_signer/socket_daemon.ml +++ b/src/bin_signer/socket_daemon.ml @@ -26,15 +26,21 @@ open Signer_messages module Events = Signer_events.Socket_daemon -let handle_client_step ?magic_bytes ?timeout ~check_high_watermark ~require_auth - cctxt fd = +let handle_client_step ?signing_version ?magic_bytes ?timeout + ~check_high_watermark ~require_auth cctxt fd = let open Lwt_result_syntax in let* recved = Tezos_base_unix.Socket.recv ?timeout fd Request.encoding in match recved with | Sign req -> let encoding = result_encoding Sign.Response.encoding in let*! res = - Handler.sign cctxt req ?magic_bytes ~check_high_watermark ~require_auth + Handler.sign + ?signing_version + cctxt + req + ?magic_bytes + ~check_high_watermark + ~require_auth in Tezos_base_unix.Socket.send fd encoding res | Deterministic_nonce req -> @@ -72,12 +78,13 @@ let handle_client_step ?magic_bytes ?timeout ~check_high_watermark ~require_auth in Tezos_base_unix.Socket.send fd encoding res -let handle_client_loop ?magic_bytes ?timeout ~check_high_watermark ~require_auth - cctxt fd = +let handle_client_loop ?signing_version ?magic_bytes ?timeout + ~check_high_watermark ~require_auth cctxt fd = let rec loop () = let open Lwt_result_syntax in let* () = handle_client_step + ?signing_version ?magic_bytes ?timeout ~check_high_watermark @@ -89,8 +96,8 @@ let handle_client_loop ?magic_bytes ?timeout ~check_high_watermark ~require_auth in loop () -let run ?magic_bytes ?timeout ~check_high_watermark ~require_auth - (cctxt : #Client_context.wallet) path = +let run ?signing_version ?magic_bytes ?timeout ~check_high_watermark + ~require_auth (cctxt : #Client_context.wallet) path = let open Lwt_result_syntax in let open Tezos_base_unix.Socket in let*! () = @@ -120,6 +127,7 @@ let run ?magic_bytes ?timeout ~check_high_watermark ~require_auth (fun () -> let* (_ : unit tzresult) = handle_client_loop + ?signing_version ?magic_bytes ?timeout ~check_high_watermark diff --git a/src/bin_signer/socket_daemon.mli b/src/bin_signer/socket_daemon.mli index 881838561f52..00f37ea99a83 100644 --- a/src/bin_signer/socket_daemon.mli +++ b/src/bin_signer/socket_daemon.mli @@ -24,6 +24,7 @@ (*****************************************************************************) val run : + ?signing_version:Signature.version -> ?magic_bytes:int list -> ?timeout:Time.System.Span.t -> check_high_watermark:bool -> -- GitLab From bae517d267d7a577596f2b68108fed9ec4a2e462 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Fri, 28 Feb 2025 16:58:04 +0100 Subject: [PATCH 4/5] client: add signing-version argument to force usage of a specific signature version --- src/lib_client_base_unix/client_config.ml | 36 +++++++++++++++----- src/lib_client_base_unix/client_main_run.ml | 26 ++++++++++---- src/lib_client_base_unix/client_main_run.mli | 1 + 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/lib_client_base_unix/client_config.ml b/src/lib_client_base_unix/client_config.ml index aac0212a11ba..4faafcbad2b8 100644 --- a/src/lib_client_base_unix/client_config.ml +++ b/src/lib_client_base_unix/client_config.ml @@ -217,6 +217,15 @@ let default_media_type = Media_type.Command_line.Any let default_daily_logs_path = None +let signing_version_for_test = + Option.map + (function + | "0" -> Signature.Version_0 + | "1" -> Signature.Version_1 + | "2" -> Signature.Version_2 + | _ -> Signature.V_latest.version) + (Sys.getenv_opt "TEZOS_SIGNER_SIGNING_VERSION") + open Filename.Infix module Cfg_file = struct @@ -232,6 +241,7 @@ module Cfg_file = struct endpoint : Uri.t option; web_port : int; remote_signer : Uri.t option; + signing_version : Signature.version option; confirmations : int option; password_filename : string option; internal_events : Tezos_base.Internal_event_config.t option; @@ -247,6 +257,7 @@ module Cfg_file = struct tls = None; web_port = 8080; remote_signer = None; + signing_version = None; confirmations = Some 0; password_filename = None; internal_events = None; @@ -265,6 +276,7 @@ module Cfg_file = struct endpoint; web_port; remote_signer; + signing_version; confirmations; password_filename; internal_events; @@ -277,9 +289,9 @@ module Cfg_file = struct endpoint, Some web_port, remote_signer, - confirmations, - password_filename ), - internal_events )) + signing_version, + confirmations ), + (password_filename, internal_events) )) (fun ( ( base_dir, node_addr, node_port, @@ -288,9 +300,9 @@ module Cfg_file = struct endpoint, web_port, remote_signer, - confirmations, - password_filename ), - internal_events ) -> + signing_version, + confirmations ), + (password_filename, internal_events) ) -> let web_port = Option.value ~default:default.web_port web_port in { base_dir; @@ -301,6 +313,7 @@ module Cfg_file = struct endpoint; web_port; remote_signer; + signing_version; confirmations; password_filename; internal_events; @@ -315,9 +328,10 @@ module Cfg_file = struct (opt "endpoint" Tezos_rpc.Encoding.uri_encoding) (opt "web_port" uint16) (opt "remote_signer" Tezos_rpc.Encoding.uri_encoding) - (opt "confirmations" int8) - (opt "password_filename" string)) - (obj1 + (opt "signing_version" Signature.version_encoding) + (opt "confirmations" int8)) + (obj2 + (opt "password_filename" string) (opt "internal_events" Tezos_base.Internal_event_config.encoding))) let from_json json = Data_encoding.Json.destruct encoding json @@ -1218,6 +1232,9 @@ let parse_config_args (ctx : #Client_context.full) argv = Option.either remote_signer @@ Option.either remote_signer_env cfg.remote_signer in + let signing_version = + Option.either signing_version_for_test cfg.signing_version + in let confirmations = Option.value ~default:cfg.confirmations confirmations in (* --password-filename has precedence over --config-file's "password-filename" json field *) @@ -1234,6 +1251,7 @@ let parse_config_args (ctx : #Client_context.full) argv = media_type; endpoint = Some endpoint; remote_signer; + signing_version; confirmations; password_filename; } diff --git a/src/lib_client_base_unix/client_main_run.ml b/src/lib_client_base_unix/client_main_run.ml index 1ccda4bf0225..4a0352f11163 100644 --- a/src/lib_client_base_unix/client_main_run.ml +++ b/src/lib_client_base_unix/client_main_run.ml @@ -84,7 +84,7 @@ module type M = sig val logger : RPC_client_unix.logger option end -let register_default_signer ?other_registrations ?logger +let register_default_signer ?signing_version ?other_registrations ?logger (cctxt : Client_context.io_wallet) = let module Remote_params = struct let authenticate pkhs payload = @@ -104,7 +104,16 @@ let register_default_signer ?other_registrations ?logger | _ -> None) keys with - | sk_uri :: _ -> Client_keys.V2.sign cctxt sk_uri payload + | sk_uri :: _ -> ( + match signing_version with + | Some Signature.Version_0 -> + let* sign = Client_keys.V0.sign cctxt sk_uri payload in + return @@ Signature.V_latest.Of_V0.signature sign + | Some Signature.Version_1 -> + let* sign = Client_keys.V1.sign cctxt sk_uri payload in + return @@ Signature.V_latest.Of_V1.signature sign + | Some Signature.Version_2 -> Client_keys.V2.sign cctxt sk_uri payload + | None -> Client_keys.V_latest.sign cctxt sk_uri payload) | [] -> failwith "remote signer expects authentication signature, but no authorized \ @@ -157,15 +166,18 @@ let register_signer (module C : M) (cctxt : Client_context.io_wallet) module C *) match C.logger with Some logger -> logger | None -> rpc_config.logger in - let other_registrations = + let other_registrations, signing_version = match parsed_config_file with - | None -> None + | None -> (None, None) | Some parsed_config_file -> ( + let version = + parsed_config_file.Client_config.Cfg_file.signing_version + in match C.other_registrations with - | Some r -> Some (r parsed_config_file) - | None -> None) + | Some r -> (Some (r parsed_config_file), version) + | None -> (None, version)) in - register_default_signer ?other_registrations ~logger cctxt + register_default_signer ?signing_version ?other_registrations ~logger cctxt (** Warn the user if there are duplicate URIs in the sources (may or may not be a misconfiguration). *) diff --git a/src/lib_client_base_unix/client_main_run.mli b/src/lib_client_base_unix/client_main_run.mli index 0796d1ae7a90..331502341b35 100644 --- a/src/lib_client_base_unix/client_main_run.mli +++ b/src/lib_client_base_unix/client_main_run.mli @@ -86,6 +86,7 @@ sig end val register_default_signer : + ?signing_version:Signature.version -> ?other_registrations:((module Client_config.Remote_params) -> unit) -> ?logger:RPC_client_unix.logger -> Client_context.io_wallet -> -- GitLab From d0dae6a4609c54caa2ec0af1b38cb0e7503aba62 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Tue, 4 Mar 2025 17:24:59 +0100 Subject: [PATCH 5/5] Changes: add entry for new signer message encoding --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f380a0d6f0a4..163f85d54fe9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -49,6 +49,9 @@ Node Client ------ +- Update signer messages encoding. Signing message for BLS (Tz4) addresses now + contain signing version. (MR :gl:`!16986`) + Baker ----- -- GitLab