From 6b5cbe2212af217a92b48fbee2bc7d3abb31a497 Mon Sep 17 00:00:00 2001 From: Guillaume Genestier Date: Tue, 13 May 2025 16:38:58 +0200 Subject: [PATCH 1/4] Client/Signer: Add prove_possession request for BLS keys Kaitai Signer --- ...ssages__bls_prove_possession__response.ksy | 7 ++++ .../files/signer_messages__request.ksy | 9 ++++++ src/bin_signer/handler.ml | 6 ++++ src/bin_signer/handler.mli | 5 +++ src/bin_signer/http_daemon.ml | 6 ++++ src/bin_signer/signer_events.ml | 8 +++++ src/bin_signer/socket_daemon.ml | 4 +++ src/lib_client_base/client_keys.ml | 11 +++++++ src/lib_client_base/client_keys.mli | 5 +++ src/lib_signer_backends/encrypted.ml | 10 ++++++ src/lib_signer_backends/http_gen.ml | 13 ++++++++ src/lib_signer_backends/unencrypted.ml | 9 ++++++ .../unix/ledger.available.ml | 17 ++++++++++ src/lib_signer_backends/unix/remote.ml | 5 +++ src/lib_signer_backends/unix/socket.ml | 25 +++++++++++++++ src/lib_signer_services/signer_messages.ml | 32 ++++++++++++++++++- src/lib_signer_services/signer_messages.mli | 15 +++++++++ src/lib_signer_services/signer_services.ml | 11 +++++++ src/lib_signer_services/signer_services.mli | 9 ++++++ tezt/tests/bls_signature.ml | 17 ++++++---- 20 files changed, 216 insertions(+), 8 deletions(-) create mode 100644 client-libs/kaitai-struct-files/files/signer_messages__bls_prove_possession__response.ksy diff --git a/client-libs/kaitai-struct-files/files/signer_messages__bls_prove_possession__response.ksy b/client-libs/kaitai-struct-files/files/signer_messages__bls_prove_possession__response.ksy new file mode 100644 index 000000000000..a08a4465560a --- /dev/null +++ b/client-libs/kaitai-struct-files/files/signer_messages__bls_prove_possession__response.ksy @@ -0,0 +1,7 @@ +meta: + id: signer_messages__bls_prove_possession__response + endian: be +doc: ! 'Encoding id: signer_messages.bls_prove_possession.response' +seq: +- id: bls_prove_possession + size: 96 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 f9b79889fbbe..4ef369c077a8 100644 --- a/client-libs/kaitai-struct-files/files/signer_messages__request.ksy +++ b/client-libs/kaitai-struct-files/files/signer_messages__request.ksy @@ -74,6 +74,11 @@ types: - id: signature size-eos: true if: (signature_tag == bool::true) + signer_messages__bls_prove_possession__request: + seq: + - id: pkh + type: public_key_hash + doc: A Ed25519, Secp256k1, P256, or BLS public key hash signer_messages__public_key__request: seq: - id: pkh @@ -123,6 +128,7 @@ enums: 4: deterministic_nonce_hash 5: supports_deterministic_nonces 6: known_keys + 7: bls_prove_possession seq: - id: signer_messages__request_tag type: u1 @@ -142,3 +148,6 @@ seq: - id: supports_deterministic_nonces type: signer_messages__supports_deterministic_nonces__request if: (signer_messages__request_tag == signer_messages__request_tag::supports_deterministic_nonces) +- id: bls_prove_possession + type: signer_messages__bls_prove_possession__request + if: (signer_messages__request_tag == signer_messages__request_tag::bls_prove_possession) diff --git a/src/bin_signer/handler.ml b/src/bin_signer/handler.ml index 1912d250626a..1c22c7e980fd 100644 --- a/src/bin_signer/handler.ml +++ b/src/bin_signer/handler.ml @@ -352,3 +352,9 @@ let known_keys (cctxt : #Client_context.wallet) = let*! () = Events.(emit request_for_known_keys ()) in let+ all_keys = Client_keys.list_keys cctxt in List.map (fun (_, pkh, _, _) -> pkh) all_keys + +let bls_prove_possession (cctxt : #Client_context.wallet) pkh = + let open Lwt_result_syntax in + let*! () = Events.(emit request_for_proof_of_possession pkh) in + let* _name, _pkh, sk_uri = Client_keys.get_key cctxt pkh in + Client_keys.bls_prove_possession sk_uri diff --git a/src/bin_signer/handler.mli b/src/bin_signer/handler.mli index 50c8fe56f0db..caae65931ec2 100644 --- a/src/bin_signer/handler.mli +++ b/src/bin_signer/handler.mli @@ -74,3 +74,8 @@ val supports_deterministic_nonces : val known_keys : #Client_context.wallet -> Tezos_crypto.Signature.public_key_hash list tzresult Lwt.t + +val bls_prove_possession : + #Client_context.wallet -> + Signature.public_key_hash -> + Tezos_crypto.Signature.Bls.t tzresult Lwt.t diff --git a/src/bin_signer/http_daemon.ml b/src/bin_signer/http_daemon.ml index 3b94520e08f7..0a3b7ec8a0c6 100644 --- a/src/bin_signer/http_daemon.ml +++ b/src/bin_signer/http_daemon.ml @@ -53,6 +53,12 @@ let run (cctxt : #Client_context.wallet) ~hosts ?signing_version ?magic_bytes Signer_services.public_key (fun pkh () () -> Handler.public_key cctxt pkh) in + let dir = + Tezos_rpc.Directory.register1 + dir + Signer_services.bls_prove_possession + (fun pkh () () -> Handler.bls_prove_possession cctxt pkh) + in let dir = Tezos_rpc.Directory.register0 dir diff --git a/src/bin_signer/signer_events.ml b/src/bin_signer/signer_events.ml index 9c3ff956f104..08c91dbf7492 100644 --- a/src/bin_signer/signer_events.ml +++ b/src/bin_signer/signer_events.ml @@ -171,6 +171,14 @@ module Handler = struct ~name:"request_for_known_keys" ~msg:"request for known keys" () + + let request_for_proof_of_possession = + declare_1 + ~section + ~level + ~name:"request_for_proof_of_possession" + ~msg:"Request for proof of possession of {pkh}" + ("pkh", Tezos_crypto.Signature.Public_key_hash.encoding) end module Socket_daemon = struct diff --git a/src/bin_signer/socket_daemon.ml b/src/bin_signer/socket_daemon.ml index 38b1d0f97e05..edf79d809d31 100644 --- a/src/bin_signer/socket_daemon.ml +++ b/src/bin_signer/socket_daemon.ml @@ -85,6 +85,10 @@ let handle_client_step ?signing_version ?magic_bytes ?timeout else failwith "List known keys request not allowed." in Tezos_base_unix.Socket.send fd encoding res + | Bls_prove_possession pkh -> + let encoding = result_encoding Bls_prove_possession.Response.encoding in + let*! res = Handler.bls_prove_possession cctxt pkh in + Tezos_base_unix.Socket.send fd encoding res let handle_client_loop ?signing_version ?magic_bytes ?timeout ?allow_list_known_keys ~check_high_watermark ~require_auth cctxt fd = diff --git a/src/lib_client_base/client_keys.ml b/src/lib_client_base/client_keys.ml index b112d46014e1..396b88175423 100644 --- a/src/lib_client_base/client_keys.ml +++ b/src/lib_client_base/client_keys.ml @@ -348,6 +348,9 @@ module type SIMPLE_SIGNER = sig val list_known_keys : Uri.t -> Tezos_crypto.Signature.Public_key_hash.t list tzresult Lwt.t + + val bls_prove_possession : + sk_uri -> Tezos_crypto.Signature.Bls.t tzresult Lwt.t end module type S = sig @@ -415,6 +418,8 @@ module type S = sig Bytes.t -> bool tzresult Lwt.t + val bls_prove_possession : sk_uri -> Tezos_crypto.Signature.Bls.t tzresult Lwt.t + val deterministic_nonce : sk_uri -> Bytes.t -> Bytes.t tzresult Lwt.t val deterministic_nonce_hash : sk_uri -> Bytes.t -> Bytes.t tzresult Lwt.t @@ -643,6 +648,8 @@ module Make (Signature : Signature_S) : let deterministic_nonce_hash = S.deterministic_nonce_hash let supports_deterministic_nonces = S.supports_deterministic_nonces + + let bls_prove_possession = S.bls_prove_possession end let adapt_signer (module Signer : SIGNER) = @@ -719,6 +726,10 @@ module Make (Signature : Signature_S) : in return signature) + let bls_prove_possession sk_uri = + with_scheme_simple_signer sk_uri (fun (module Signer) -> + Signer.bls_prove_possession sk_uri) + let append cctxt ?watermark loc buf = let open Lwt_result_syntax in let+ signature = sign cctxt ?watermark loc buf in diff --git a/src/lib_client_base/client_keys.mli b/src/lib_client_base/client_keys.mli index 9256c97bcc7f..fc484ab2d467 100644 --- a/src/lib_client_base/client_keys.mli +++ b/src/lib_client_base/client_keys.mli @@ -159,6 +159,9 @@ module type SIGNER = sig the signer. *) val list_known_keys : Uri.t -> Tezos_crypto.Signature.Public_key_hash.t list tzresult Lwt.t + + val bls_prove_possession : + sk_uri -> Tezos_crypto.Signature.Bls.t tzresult Lwt.t end type signer = (module SIGNER) @@ -236,6 +239,8 @@ module type S = sig Bytes.t -> bool tzresult Lwt.t + val bls_prove_possession : sk_uri -> Tezos_crypto.Signature.Bls.t tzresult Lwt.t + val deterministic_nonce : sk_uri -> Bytes.t -> Bytes.t tzresult Lwt.t val deterministic_nonce_hash : sk_uri -> Bytes.t -> Bytes.t tzresult Lwt.t diff --git a/src/lib_signer_backends/encrypted.ml b/src/lib_signer_backends/encrypted.ml index 967b99d47800..d5ae7349b2c1 100644 --- a/src/lib_signer_backends/encrypted.ml +++ b/src/lib_signer_backends/encrypted.ml @@ -547,4 +547,14 @@ struct return (Signature.deterministic_nonce_hash sk buf) let supports_deterministic_nonces _ = Lwt_result_syntax.return_true + + let bls_prove_possession sk_uri = + let open Lwt_result_syntax in + let* sk = decrypt C.cctxt sk_uri in + match sk with + | Bls sk -> + return @@ Signature.Bls.of_bytes_exn (Signature.Bls.pop_prove sk) + | _ -> + Error_monad.failwith + "Proof of possession can only be requested for BLS keys." end diff --git a/src/lib_signer_backends/http_gen.ml b/src/lib_signer_backends/http_gen.ml index f735436dd6f9..72ebf243da5f 100644 --- a/src/lib_signer_backends/http_gen.ml +++ b/src/lib_signer_backends/http_gen.ml @@ -237,6 +237,19 @@ struct | Ok ans -> return ans | Error (Tezos_rpc.Context.Not_found _ :: _) -> return_false | Error _ as res -> Lwt.return res + + let bls_prove_possession uri = + let open Lwt_result_syntax in + let* base, pkh = parse (uri : sk_uri :> Uri.t) in + RPC_client.call_service + ~logger:P.logger + ?headers + Media_type.all_media_types + ~base + Signer_services.bls_prove_possession + ((), pkh) + () + () end let make_base host port = Uri.make ~scheme ~host ~port () diff --git a/src/lib_signer_backends/unencrypted.ml b/src/lib_signer_backends/unencrypted.ml index 4d6d1fe30b3e..456d6606c559 100644 --- a/src/lib_signer_backends/unencrypted.ml +++ b/src/lib_signer_backends/unencrypted.ml @@ -120,3 +120,12 @@ let deterministic_nonce_hash sk_uri buf = return (Signature.deterministic_nonce_hash sk buf) let supports_deterministic_nonces _ = Lwt_result_syntax.return_true + +let bls_prove_possession sk_uri = + let open Lwt_result_syntax in + let* sk = secret_key sk_uri in + match sk with + | Bls sk -> return @@ Signature.Bls.of_bytes_exn (Signature.Bls.pop_prove sk) + | _ -> + Error_monad.failwith + "Proof of possession can only be requested for BLS keys." diff --git a/src/lib_signer_backends/unix/ledger.available.ml b/src/lib_signer_backends/unix/ledger.available.ml index bb46345e6096..24bf7f0290b0 100644 --- a/src/lib_signer_backends/unix/ledger.available.ml +++ b/src/lib_signer_backends/unix/ledger.available.ml @@ -49,6 +49,7 @@ type error += | LedgerError of Ledgerwallet.Transport.error | Ledger_signing_hash_mismatch of string * string | Ledger_msg_chunk_too_long of string + | Ledger_BLS_and_proof_of_possession_not_supported let error_encoding = let open Data_encoding in @@ -113,6 +114,19 @@ let () = | Ledger_signing_hash_mismatch (lh, ch) -> Some (lh, ch) | _ -> None) (fun (lh, ch) -> Ledger_signing_hash_mismatch (lh, ch)) +let () = + let description = "Ledger does not support BLS and proof of possession." in + register_error_kind + `Permanent + ~id:"signer.ledger.BLS_and_proof_of_possession_not_supported" + ~title:description + ~description + ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) + Data_encoding.unit + (function + | Ledger_BLS_and_proof_of_possession_not_supported -> Some () | _ -> None) + (fun () -> Ledger_BLS_and_proof_of_possession_not_supported) + let pp_round_opt fmt = function | None -> () | Some x -> Format.fprintf fmt " (round: %ld)" x @@ -868,6 +882,9 @@ module Signer_implementation : Client_keys.SIGNER = struct (Tezos_crypto.Blake2B.to_bytes (Tezos_crypto.Blake2B.hash_bytes [nonce])) let supports_deterministic_nonces _ = Lwt_result_syntax.return_true + + let bls_prove_possession _sk_uri = + Lwt_result_syntax.tzfail Ledger_BLS_and_proof_of_possession_not_supported end (* The Ledger uses a special value 0x00000000 for the “any” chain-id: *) diff --git a/src/lib_signer_backends/unix/remote.ml b/src/lib_signer_backends/unix/remote.ml index cd0307c85caa..4ddf23a2c505 100644 --- a/src/lib_signer_backends/unix/remote.ml +++ b/src/lib_signer_backends/unix/remote.ml @@ -126,6 +126,11 @@ struct let open Lwt_result_syntax in let*? v = Client_keys.make_sk_uri (key (sk_uri : sk_uri :> Uri.t)) in Remote.supports_deterministic_nonces v + + let bls_prove_possession sk_uri = + let open Lwt_result_syntax in + let*? sk_uri = Client_keys.make_sk_uri (key (sk_uri : sk_uri :> Uri.t)) in + Remote.bls_prove_possession sk_uri end let make_sk sk = diff --git a/src/lib_signer_backends/unix/socket.ml b/src/lib_signer_backends/unix/socket.ml index fc948a70c651..090a58de94e2 100644 --- a/src/lib_signer_backends/unix/socket.ml +++ b/src/lib_signer_backends/unix/socket.ml @@ -164,6 +164,21 @@ struct let* pkhs = Tezos_base_unix.Socket.recv conn encoding in match pkhs with Error _ as e -> Lwt.return e | Ok pkhs -> return pkhs) + let bls_prove_possession path pkh = + let open Lwt_result_syntax in + Tezos_base_unix.Socket.with_connection path (fun conn -> + let* () = + Tezos_base_unix.Socket.send + conn + Request.encoding + (Request.Bls_prove_possession pkh) + in + let encoding = result_encoding Bls_prove_possession.Response.encoding in + let* proof_of_possession = Tezos_base_unix.Socket.recv conn encoding in + match proof_of_possession with + | Error _ as e -> Lwt.return e + | Ok proof_of_possession -> return proof_of_possession) + let public_key path pkh = let open Lwt_result_syntax in Tezos_base_unix.Socket.with_connection path (fun conn -> @@ -248,6 +263,11 @@ struct let open Lwt_result_syntax in let* path, pkh = parse (uri : sk_uri :> Uri.t) in supports_deterministic_nonces path pkh + + let bls_prove_possession uri = + let open Lwt_result_syntax in + let* path, pkh = parse (uri : sk_uri :> Uri.t) in + bls_prove_possession path pkh end module Tcp = struct @@ -328,6 +348,11 @@ struct let open Lwt_result_syntax in let* path, pkh = parse (uri : sk_uri :> Uri.t) in supports_deterministic_nonces path pkh + + let bls_prove_possession uri = + let open Lwt_result_syntax in + let* path, pkh = parse (uri : sk_uri :> Uri.t) in + bls_prove_possession path pkh end end diff --git a/src/lib_signer_services/signer_messages.ml b/src/lib_signer_services/signer_messages.ml index 7a3aee655f59..e6ff01be235b 100644 --- a/src/lib_signer_services/signer_messages.ml +++ b/src/lib_signer_services/signer_messages.ml @@ -283,6 +283,26 @@ module Known_keys = struct end end +module Bls_prove_possession = struct + module Request = struct + type t = Tezos_crypto.Signature.Public_key_hash.t + + let encoding = + let open Data_encoding in + def "signer_messages.bls_prove_possession.request" + @@ obj1 (req "pkh" Tezos_crypto.Signature.Public_key_hash.encoding) + end + + module Response = struct + type t = Tezos_crypto.Signature.Bls.t + + let encoding = + let open Data_encoding in + def "signer_messages.bls_prove_possession.response" + @@ obj1 (req "bls_prove_possession" Tezos_crypto.Signature.Bls.encoding) + end +end + module Request = struct type t = | Sign of Sign.Request.t @@ -292,6 +312,7 @@ module Request = struct | Deterministic_nonce_hash of Deterministic_nonce_hash.Request.t | Supports_deterministic_nonces of Supports_deterministic_nonces.Request.t | Known_keys + | Bls_prove_possession of Bls_prove_possession.Request.t let encoding = let open Data_encoding in @@ -352,6 +373,14 @@ module Request = struct (obj1 (req "kind" (constant "known_keys"))) (function Known_keys -> Some () | _ -> None) (fun () -> Known_keys); + case + (Tag 7) + ~title:"Bls_prove_possession" + (merge_objs + (obj1 (req "kind" (constant "Bls_prove_possession"))) + Bls_prove_possession.Request.encoding) + (function Bls_prove_possession req -> Some ((), req) | _ -> None) + (fun ((), req) -> Bls_prove_possession req); ] end @@ -363,4 +392,5 @@ let () = register Deterministic_nonce_hash.Response.encoding ; register Supports_deterministic_nonces.Response.encoding ; register Public_key.Response.encoding ; - register Known_keys.Response.encoding + register Known_keys.Response.encoding ; + register Bls_prove_possession.Response.encoding diff --git a/src/lib_signer_services/signer_messages.mli b/src/lib_signer_services/signer_messages.mli index 5b17eb3c73f4..a8636827e279 100644 --- a/src/lib_signer_services/signer_messages.mli +++ b/src/lib_signer_services/signer_messages.mli @@ -130,6 +130,20 @@ module Known_keys : sig end end +module Bls_prove_possession : sig + module Request : sig + type t = Tezos_crypto.Signature.Public_key_hash.t + + val encoding : t Data_encoding.t + end + + module Response : sig + type t = Tezos_crypto.Signature.Bls.t + + val encoding : t Data_encoding.t + end +end + module Request : sig type t = | Sign of Sign.Request.t @@ -139,6 +153,7 @@ module Request : sig | Deterministic_nonce_hash of Deterministic_nonce_hash.Request.t | Supports_deterministic_nonces of Supports_deterministic_nonces.Request.t | Known_keys + | Bls_prove_possession of Bls_prove_possession.Request.t 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 4b4f2ca82d74..f4ff99d36404 100644 --- a/src/lib_signer_services/signer_services.ml +++ b/src/lib_signer_services/signer_services.ml @@ -96,6 +96,17 @@ let supports_deterministic_nonces = Tezos_rpc.Path.( root / "keys" /: Tezos_crypto.Signature.Public_key_hash.rpc_arg) +let bls_prove_possession = + Tezos_rpc.Service.get_service + ~description:"Obtain a proof of possession for a given remote key" + ~query:Tezos_rpc.Query.empty + ~output: + Data_encoding.( + obj1 (req "bls_prove_possession" Tezos_crypto.Signature.Bls.encoding)) + Tezos_rpc.Path.( + root / "bls_prove_possession" + /: Tezos_crypto.Signature.Public_key_hash.rpc_arg) + let public_key = Tezos_rpc.Service.get_service ~description:"Retrieve the public key of a given remote key" diff --git a/src/lib_signer_services/signer_services.mli b/src/lib_signer_services/signer_services.mli index 3ef599689089..9370709255fd 100644 --- a/src/lib_signer_services/signer_services.mli +++ b/src/lib_signer_services/signer_services.mli @@ -85,3 +85,12 @@ val known_keys : unit, Tezos_crypto.Signature.Public_key_hash.t list ) Tezos_rpc.Service.t + +val bls_prove_possession : + ( [`GET], + unit, + unit * Tezos_crypto.Signature.Public_key_hash.t, + unit, + unit, + Tezos_crypto.Signature.Bls.t ) + Tezos_rpc.Service.t diff --git a/tezt/tests/bls_signature.ml b/tezt/tests/bls_signature.ml index c763ddc27949..b67bf34e4d51 100644 --- a/tezt/tests/bls_signature.ml +++ b/tezt/tests/bls_signature.ml @@ -247,15 +247,18 @@ module Local_helpers = struct return op let create_proof ~(signer : Account.key) = - let public_key = - Tezos_crypto.Signature.Public_key.of_b58check_exn signer.public_key + let b58_secret_key = + Account.require_unencrypted_secret_key ~__LOC__ signer.secret_key in - let msg = - Data_encoding.Binary.to_bytes_exn - Tezos_crypto.Signature.Public_key.encoding - public_key + let secret_key = + Tezos_crypto.Signature.Secret_key.of_b58check_exn b58_secret_key in - Account.sign_bytes ~signer msg |> Tezos_crypto.Signature.to_b58check + match secret_key with + | Bls sk -> + let proof = Tezos_crypto.Signature.Bls.pop_prove sk in + Tezos_crypto.Signature.of_bytes_exn proof + |> Tezos_crypto.Signature.to_b58check + | _ -> Test.fail "Proof-of-Possession is only required for BLS keys" let inject_bls_sign_op ~baker ~(signer : Account.key) (op : Operation.t) client = -- GitLab From 9902d41c4569c4c50b6f9519b84fb43a2e6f6843 Mon Sep 17 00:00:00 2001 From: Guillaume Genestier Date: Tue, 13 May 2025 16:36:30 +0200 Subject: [PATCH 2/4] Proto: Use prove_possession to construct proofs when updating consensus key Proto --- .../lib_client/client_proto_context.ml | 41 ++++--------------- .../client_bls_commands.ml | 21 ++-------- src/proto_alpha/lib_protocol/apply.ml | 11 +---- .../lib_protocol/test/helpers/op.ml | 11 ++--- 4 files changed, 16 insertions(+), 68 deletions(-) diff --git a/src/proto_alpha/lib_client/client_proto_context.ml b/src/proto_alpha/lib_client/client_proto_context.ml index 2099e9334a14..f154247eb46f 100644 --- a/src/proto_alpha/lib_client/client_proto_context.ml +++ b/src/proto_alpha/lib_client/client_proto_context.ml @@ -32,24 +32,6 @@ open Client_proto_contracts open Client_keys module Alpha_services = Plugin.Alpha_services -type error += Unexpected_proof_of_possession_format of Signature.t - -let () = - register_error_kind - `Permanent - ~id:"client.unexpected_proof_of_possession_format" - ~title:"Unexpected proof of possession format" - ~description:"Proof of possession should be a BLS signature." - ~pp:(fun ppf s -> - Format.fprintf - ppf - "Proof of possession should be a BLS signature (%a)." - Signature.pp - s) - Data_encoding.(obj1 (req "proof_of_possession" Signature.encoding)) - (function Unexpected_proof_of_possession_format s -> Some s | _ -> None) - (fun s -> Unexpected_proof_of_possession_format s) - let get_balance (rpc : #rpc_context) ~chain ~block contract = Alpha_services.Contract.balance rpc (chain, block) contract @@ -354,21 +336,14 @@ let set_delegate cctxt ~chain ~block ?confirmations ?dry_run ?verbose_signing ~fee_parameter opt_delegate -let build_update_consensus_key cctxt ?fee ?gas_limit ?storage_limit - ?secret_key_uri ~kind public_key = +let build_update_consensus_key ?fee ?gas_limit ?storage_limit ?secret_key_uri + ~kind public_key = let open Lwt_result_syntax in let* proof = match ((public_key : Signature.public_key), secret_key_uri) with - | Bls _, Some secret_key_uri -> ( - let bytes = - Data_encoding.Binary.to_bytes_exn - Signature.Public_key.encoding - public_key - in - let* proof = Client_keys.sign cctxt secret_key_uri bytes in - match proof with - | Bls proof -> return_some proof - | _ -> tzfail (Unexpected_proof_of_possession_format proof)) + | Bls _, Some secret_key_uri -> + let* proof = Client_keys.bls_prove_possession secret_key_uri in + return_some proof | _ -> return_none in let operation = Update_consensus_key {public_key; proof; kind} in @@ -416,7 +391,7 @@ let register_as_delegate cctxt ~chain ~block ?confirmations ?dry_run else Companion in let+ operation_content = - build_update_consensus_key cctxt ~kind ?fee ?secret_key_uri public_key + build_update_consensus_key ~kind ?fee ?secret_key_uri public_key in Annotated_manager_operation.Cons_manager ( delegate_op, @@ -454,7 +429,6 @@ let register_as_delegate cctxt ~chain ~block ?confirmations ?dry_run let* operation = let* operation_consensus = build_update_consensus_key - cctxt ~kind:Consensus ?fee ?secret_key_uri:secret_key_uri_consensus @@ -462,7 +436,6 @@ let register_as_delegate cctxt ~chain ~block ?confirmations ?dry_run in let* operation_companion = build_update_consensus_key - cctxt ~kind:Companion ?fee ?secret_key_uri:secret_key_uri_companion @@ -518,7 +491,7 @@ let update_consensus_or_companion_key ~kind cctxt ~chain ~block ?confirmations let open Lwt_result_syntax in let source = Signature.Public_key.hash src_pk in let* operation = - build_update_consensus_key cctxt ?fee ?secret_key_uri ~kind public_key + build_update_consensus_key ?fee ?secret_key_uri ~kind public_key in let operation = Annotated_manager_operation.Single_manager operation in let* oph, _, op, result = diff --git a/src/proto_alpha/lib_client_commands/client_bls_commands.ml b/src/proto_alpha/lib_client_commands/client_bls_commands.ml index 95e54c751b46..026e2408183b 100644 --- a/src/proto_alpha/lib_client_commands/client_bls_commands.ml +++ b/src/proto_alpha/lib_client_commands/client_bls_commands.ml @@ -102,12 +102,7 @@ let threshold_signature_encoding = (obj2 (req "id" int8) (req "signature" Signature.Bls.encoding)) let check_public_key_with_proof pk proof = - let msg = - Data_encoding.Binary.to_bytes_exn - Signature.Public_key.encoding - (Signature.Bls pk) - in - Signature.Bls.check pk proof msg + Signature.Bls.pop_verify pk (Signature.Bls.to_bytes proof) let commands () = let open Lwt_result_syntax in @@ -141,17 +136,9 @@ let commands () = @@ Client_keys.Secret_key.source_param @@ stop) (fun () sk_uri (cctxt : #Protocol_client_context.full) -> let open Lwt_result_syntax in - let* pk_uri = Client_keys.neuterize sk_uri in - let* pk = Client_keys.public_key pk_uri in - match pk with - | Bls _ -> - let msg = - Data_encoding.Binary.to_bytes_exn Signature.Public_key.encoding pk - in - let* proof = Client_keys.sign cctxt sk_uri msg in - let*! () = cctxt#message "%a" Signature.pp proof in - return_unit - | _ -> cctxt#error "Failed to produce a proof: input is not a BLS key"); + let* proof = Client_keys.bls_prove_possession sk_uri in + let*! () = cctxt#message "%a" Signature.Bls.pp proof in + return_unit); command ~group ~desc:"Check a BLS proof" diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 84d04f96bae1..51b8a1b5ffea 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -1445,18 +1445,9 @@ let apply_manager_operation : in Gas.consume ctxt gas_cost_for_sig_check in - let bytes = - match - Data_encoding.Binary.to_bytes_opt - Signature.Public_key.encoding - public_key - with - | None -> Bytes.empty (* Should never happen *) - | Some bytes -> bytes - in let* () = fail_unless - (Signature.check public_key (Bls proof) bytes) + (Signature.pop_verify bls_public_key (Bls.to_bytes proof)) (Validate_errors.Manager .Update_consensus_key_with_incorrect_proof {kind; public_key; proof}) diff --git a/src/proto_alpha/lib_protocol/test/helpers/op.ml b/src/proto_alpha/lib_protocol/test/helpers/op.ml index 6117bf472d83..b54fb0e10174 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/op.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/op.ml @@ -1206,13 +1206,10 @@ let update_consensus_or_companion ~kind ?force_reveal ?counter ?fee ?gas_limit | None -> return_none | Some account -> ( let* account = Context.Contract.manager ctxt account in - let bytes = - Data_encoding.Binary.to_bytes_exn - Signature.Public_key.encoding - public_key - in - match Signature.sign account.sk bytes with - | Bls proof -> return_some proof + match account.sk with + | Bls sk -> + return_some + @@ Signature.Bls.of_bytes_exn (Signature.Bls.pop_prove sk) | _ -> failwith "Can't forge an Update_consensus_key with a non-BLS proof of \ -- GitLab From f7433696868dfbf8a670a5d4091a1bd4c4a4c9ec Mon Sep 17 00:00:00 2001 From: Guillaume Genestier Date: Wed, 14 May 2025 10:09:52 +0200 Subject: [PATCH 3/4] Shell: Use pop_verify for BLS proofs --- src/lib_shell/bls_directory.ml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/lib_shell/bls_directory.ml b/src/lib_shell/bls_directory.ml index d6ed859142de..3a0c56632855 100644 --- a/src/lib_shell/bls_directory.ml +++ b/src/lib_shell/bls_directory.ml @@ -9,12 +9,7 @@ module Bls = Tezos_crypto.Signature.Bls let check_public_key_with_proof pk proof = - let msg = - Data_encoding.Binary.to_bytes_exn - Signature.Public_key.encoding - (Signature.Bls pk) - in - Bls.check pk proof msg + Signature.Bls.pop_verify pk (Signature.Bls.to_bytes proof) let build_rpc_directory () = let open Lwt_result_syntax in -- GitLab From 17ab2e4af16320ad8a67e8ab19a38b77d5ade33e Mon Sep 17 00:00:00 2001 From: Guillaume Genestier Date: Wed, 14 May 2025 19:05:15 +0200 Subject: [PATCH 4/4] Client: Add a pop_verify check in the prove_possession function of Client_keys --- src/bin_signer/handler.ml | 2 +- src/lib_client_base/client_keys.ml | 71 +++++++++++++++---- src/lib_client_base/client_keys.mli | 5 +- src/lib_crypto/aggregate_signature.ml | 3 + src/lib_crypto/ed25519.ml | 2 + src/lib_crypto/p256.ml | 2 + src/lib_crypto/s.ml | 6 ++ src/lib_crypto/secp256k1.ml | 2 + src/lib_crypto/signature_v0.ml | 2 + src/lib_crypto/signature_v1.ml | 5 ++ src/lib_crypto/signature_v2.ml | 5 ++ .../lib_client/client_proto_context.ml | 12 ++-- .../client_bls_commands.ml | 2 +- 13 files changed, 97 insertions(+), 22 deletions(-) diff --git a/src/bin_signer/handler.ml b/src/bin_signer/handler.ml index 1c22c7e980fd..c501d93175a7 100644 --- a/src/bin_signer/handler.ml +++ b/src/bin_signer/handler.ml @@ -357,4 +357,4 @@ let bls_prove_possession (cctxt : #Client_context.wallet) pkh = let open Lwt_result_syntax in let*! () = Events.(emit request_for_proof_of_possession pkh) in let* _name, _pkh, sk_uri = Client_keys.get_key cctxt pkh in - Client_keys.bls_prove_possession sk_uri + Client_keys.bls_prove_possession cctxt sk_uri diff --git a/src/lib_client_base/client_keys.ml b/src/lib_client_base/client_keys.ml index 396b88175423..25feec6df5f2 100644 --- a/src/lib_client_base/client_keys.ml +++ b/src/lib_client_base/client_keys.ml @@ -153,23 +153,40 @@ let make_sk_uri (x : Uri.t) : sk_uri tzresult = tzfail (Exn (Failure "Error while parsing URI: SK_URI needs a scheme")) | Some _ -> return x -type error += Signature_mismatch of sk_uri +type signer_output = Signature | Proof_of_possession + +let signer_output_pp fmt out = + Format.fprintf + fmt + "%s" + (match out with + | Signature -> "signature" + | Proof_of_possession -> "proof of possession") + +let signer_output_encoding = + Data_encoding.string_enum + [("Signature", Signature); ("Proof of possession", Proof_of_possession)] + +type error += Signer_output_mismatch of signer_output * sk_uri let () = register_error_kind `Permanent - ~id:"cli.signature_mismatch" - ~title:"Signature mismatch" - ~description:"The signer produced an invalid signature" - ~pp:(fun ppf sk -> + ~id:"cli.signer_output_mismatch" + ~title:"Signer output mismatch" + ~description:"The signer produced an invalid output" + ~pp:(fun ppf (out, sk) -> Format.fprintf ppf - "The signer for %a produced an invalid signature" + "The signer for %a produced an invalid %a" Uri.pp_hum - sk) - Data_encoding.(obj1 (req "locator" uri_encoding)) - (function Signature_mismatch sk -> Some sk | _ -> None) - (fun sk -> Signature_mismatch sk) + sk + signer_output_pp + out) + Data_encoding.( + obj2 (req "output" signer_output_encoding) (req "locator" uri_encoding)) + (function Signer_output_mismatch (out, sk) -> Some (out, sk) | _ -> None) + (fun (out, sk) -> Signer_output_mismatch (out, sk)) type sapling_uri = Uri.t @@ -418,7 +435,10 @@ module type S = sig Bytes.t -> bool tzresult Lwt.t - val bls_prove_possession : sk_uri -> Tezos_crypto.Signature.Bls.t tzresult Lwt.t + val bls_prove_possession : + #Client_context.wallet -> + sk_uri -> + Tezos_crypto.Signature.Bls.t tzresult Lwt.t val deterministic_nonce : sk_uri -> Bytes.t -> Bytes.t tzresult Lwt.t @@ -722,13 +742,36 @@ module Make (Signature : Signature_S) : let* () = fail_unless (Signature.check ?watermark pubkey signature buf) - (Signature_mismatch sk_uri) + (Signer_output_mismatch (Signature, sk_uri)) in return signature) - let bls_prove_possession sk_uri = + let bls_prove_possession cctxt sk_uri = + let open Lwt_result_syntax in with_scheme_simple_signer sk_uri (fun (module Signer) -> - Signer.bls_prove_possession sk_uri) + let* proof = Signer.bls_prove_possession sk_uri in + let* pk_uri = Signer.neuterize sk_uri in + let* pubkey = + let* o = Secret_key.rev_find cctxt sk_uri in + match o with + | None -> public_key pk_uri + | Some name -> ( + let* r = Public_key.find cctxt name in + match r with + | _, None -> + let* pk = public_key pk_uri in + let* () = Public_key.update cctxt name (pk_uri, Some pk) in + return pk + | _, Some pubkey -> return pubkey) + in + let* () = + fail_unless + (Signature.pop_verify + pubkey + (Tezos_crypto.Signature.Bls.to_bytes proof)) + (Signer_output_mismatch (Proof_of_possession, sk_uri)) + in + return proof) let append cctxt ?watermark loc buf = let open Lwt_result_syntax in diff --git a/src/lib_client_base/client_keys.mli b/src/lib_client_base/client_keys.mli index fc484ab2d467..db01c74c46fc 100644 --- a/src/lib_client_base/client_keys.mli +++ b/src/lib_client_base/client_keys.mli @@ -239,7 +239,10 @@ module type S = sig Bytes.t -> bool tzresult Lwt.t - val bls_prove_possession : sk_uri -> Tezos_crypto.Signature.Bls.t tzresult Lwt.t + val bls_prove_possession : + #Client_context.wallet -> + sk_uri -> + Tezos_crypto.Signature.Bls.t tzresult Lwt.t val deterministic_nonce : sk_uri -> Bytes.t -> Bytes.t tzresult Lwt.t diff --git a/src/lib_crypto/aggregate_signature.ml b/src/lib_crypto/aggregate_signature.ml index 109ab0c79653..d4e036283db1 100644 --- a/src/lib_crypto/aggregate_signature.ml +++ b/src/lib_crypto/aggregate_signature.ml @@ -538,3 +538,6 @@ let aggregate_public_key_weighted_opt ?subgroup_check pks_with_weights = List.map (function w, Public_key.Bls12_381 s -> (w, s)) pks_with_weights |> Bls.aggregate_public_key_weighted_opt ?subgroup_check |> Option.map (fun s -> Public_key.Bls12_381 s) + +let pop_verify (Bls12_381 pubkey : public_key) proof = + Bls12_381_signature.MinPk.Pop.pop_verify pubkey proof diff --git a/src/lib_crypto/ed25519.ml b/src/lib_crypto/ed25519.ml index ec0615403ef8..bba6b0bb36d1 100644 --- a/src/lib_crypto/ed25519.ml +++ b/src/lib_crypto/ed25519.ml @@ -368,4 +368,6 @@ let deterministic_nonce sk msg = let deterministic_nonce_hash sk msg = Blake2B.to_bytes (Blake2B.hash_bytes [deterministic_nonce sk msg]) +let pop_verify _ _ = false + include (Compare.Bytes : Compare.S with type t := t) diff --git a/src/lib_crypto/p256.ml b/src/lib_crypto/p256.ml index e8ad4f79489f..635dece0bcb8 100644 --- a/src/lib_crypto/p256.ml +++ b/src/lib_crypto/p256.ml @@ -330,6 +330,8 @@ let deterministic_nonce sk msg = let deterministic_nonce_hash sk msg = Blake2B.to_bytes (Blake2B.hash_bytes [deterministic_nonce sk msg]) +let pop_verify _ _ = false + include Compare.Make (struct type nonrec t = t diff --git a/src/lib_crypto/s.ml b/src/lib_crypto/s.ml index bdbd099b68ea..78af2c5d3136 100644 --- a/src/lib_crypto/s.ml +++ b/src/lib_crypto/s.ml @@ -435,6 +435,12 @@ module type SIGNATURE = sig deterministic_nonce_hash sk msg] *) val deterministic_nonce_hash : Secret_key.t -> Bytes.t -> Bytes.t + + (** [pop_verify pk proof] checks if [proof] is a valid proof of possesssion + for [pk]. + If [pk] is not a BLS key, [pop_verify pk _ = false]. + *) + val pop_verify : Public_key.t -> Bytes.t -> bool end module type AGGREGATE_SIGNATURE = sig diff --git a/src/lib_crypto/secp256k1.ml b/src/lib_crypto/secp256k1.ml index 47330dbe796c..2b4c176e8fbf 100644 --- a/src/lib_crypto/secp256k1.ml +++ b/src/lib_crypto/secp256k1.ml @@ -351,6 +351,8 @@ let deterministic_nonce_hash sk msg = let nonce = deterministic_nonce sk msg in Blake2B.to_bytes (Blake2B.hash_bytes [nonce]) +let pop_verify _ _ = false + let recover signature msg = let open Error_monad.Result_syntax in (* Decode the signature. *) diff --git a/src/lib_crypto/signature_v0.ml b/src/lib_crypto/signature_v0.ml index 6d2c6fd56d86..16e7ea500e73 100644 --- a/src/lib_crypto/signature_v0.ml +++ b/src/lib_crypto/signature_v0.ml @@ -845,3 +845,5 @@ let deterministic_nonce_hash sk msg = | Secret_key.Ed25519 sk -> Ed25519.deterministic_nonce_hash sk msg | Secret_key.Secp256k1 sk -> Secp256k1.deterministic_nonce_hash sk msg | Secret_key.P256 sk -> P256.deterministic_nonce_hash sk msg + +let pop_verify _ _ = false diff --git a/src/lib_crypto/signature_v1.ml b/src/lib_crypto/signature_v1.ml index a0914fe643c5..732609548334 100644 --- a/src/lib_crypto/signature_v1.ml +++ b/src/lib_crypto/signature_v1.ml @@ -1010,6 +1010,11 @@ let deterministic_nonce_hash sk msg = | Secret_key.P256 sk -> P256.deterministic_nonce_hash sk msg | Secret_key.Bls sk -> Bls.deterministic_nonce_hash sk msg +let pop_verify pubkey proof = + match pubkey with + | Public_key.Bls pk -> Bls12_381_signature.MinPk.Pop.pop_verify pk proof + | _ -> false + module Of_V0 = struct let public_key_hash : Signature_v0.Public_key_hash.t -> Public_key_hash.t = function diff --git a/src/lib_crypto/signature_v2.ml b/src/lib_crypto/signature_v2.ml index c324e1692012..3db3203c8364 100644 --- a/src/lib_crypto/signature_v2.ml +++ b/src/lib_crypto/signature_v2.ml @@ -990,6 +990,11 @@ let deterministic_nonce_hash sk msg = | Secret_key.P256 sk -> P256.deterministic_nonce_hash sk msg | Secret_key.Bls sk -> Bls.deterministic_nonce_hash sk msg +let pop_verify pubkey proof = + match pubkey with + | Public_key.Bls pk -> Bls12_381_signature.MinPk.Pop.pop_verify pk proof + | _ -> false + module Of_V0 = struct let public_key_hash : Signature_v0.Public_key_hash.t -> Public_key_hash.t = function diff --git a/src/proto_alpha/lib_client/client_proto_context.ml b/src/proto_alpha/lib_client/client_proto_context.ml index f154247eb46f..327ec880bb09 100644 --- a/src/proto_alpha/lib_client/client_proto_context.ml +++ b/src/proto_alpha/lib_client/client_proto_context.ml @@ -336,13 +336,13 @@ let set_delegate cctxt ~chain ~block ?confirmations ?dry_run ?verbose_signing ~fee_parameter opt_delegate -let build_update_consensus_key ?fee ?gas_limit ?storage_limit ?secret_key_uri - ~kind public_key = +let build_update_consensus_key cctxt ?fee ?gas_limit ?storage_limit + ?secret_key_uri ~kind public_key = let open Lwt_result_syntax in let* proof = match ((public_key : Signature.public_key), secret_key_uri) with | Bls _, Some secret_key_uri -> - let* proof = Client_keys.bls_prove_possession secret_key_uri in + let* proof = Client_keys.bls_prove_possession cctxt secret_key_uri in return_some proof | _ -> return_none in @@ -391,7 +391,7 @@ let register_as_delegate cctxt ~chain ~block ?confirmations ?dry_run else Companion in let+ operation_content = - build_update_consensus_key ~kind ?fee ?secret_key_uri public_key + build_update_consensus_key cctxt ~kind ?fee ?secret_key_uri public_key in Annotated_manager_operation.Cons_manager ( delegate_op, @@ -429,6 +429,7 @@ let register_as_delegate cctxt ~chain ~block ?confirmations ?dry_run let* operation = let* operation_consensus = build_update_consensus_key + cctxt ~kind:Consensus ?fee ?secret_key_uri:secret_key_uri_consensus @@ -436,6 +437,7 @@ let register_as_delegate cctxt ~chain ~block ?confirmations ?dry_run in let* operation_companion = build_update_consensus_key + cctxt ~kind:Companion ?fee ?secret_key_uri:secret_key_uri_companion @@ -491,7 +493,7 @@ let update_consensus_or_companion_key ~kind cctxt ~chain ~block ?confirmations let open Lwt_result_syntax in let source = Signature.Public_key.hash src_pk in let* operation = - build_update_consensus_key ?fee ?secret_key_uri ~kind public_key + build_update_consensus_key cctxt ?fee ?secret_key_uri ~kind public_key in let operation = Annotated_manager_operation.Single_manager operation in let* oph, _, op, result = diff --git a/src/proto_alpha/lib_client_commands/client_bls_commands.ml b/src/proto_alpha/lib_client_commands/client_bls_commands.ml index 026e2408183b..a79b51181a08 100644 --- a/src/proto_alpha/lib_client_commands/client_bls_commands.ml +++ b/src/proto_alpha/lib_client_commands/client_bls_commands.ml @@ -136,7 +136,7 @@ let commands () = @@ Client_keys.Secret_key.source_param @@ stop) (fun () sk_uri (cctxt : #Protocol_client_context.full) -> let open Lwt_result_syntax in - let* proof = Client_keys.bls_prove_possession sk_uri in + let* proof = Client_keys.bls_prove_possession cctxt sk_uri in let*! () = cctxt#message "%a" Signature.Bls.pp proof in return_unit); command -- GitLab