From 20da46cb2c76a60106e70a2874c6c8b62b485f1f Mon Sep 17 00:00:00 2001 From: Victor Dumitrescu Date: Tue, 21 Mar 2023 09:53:27 +0100 Subject: [PATCH 1/6] Lib_hacl: expose secp256k1 signature scheme from HACL* --- src/lib_hacl/hacl.ml | 82 ++++++++++++++++++++++++ src/lib_hacl/test/test_hacl.ml | 113 ++++++++++++++++++++++++++++++++- 2 files changed, 194 insertions(+), 1 deletion(-) diff --git a/src/lib_hacl/hacl.ml b/src/lib_hacl/hacl.ml index 12348ea34234..05cbe86efc78 100644 --- a/src/lib_hacl/hacl.ml +++ b/src/lib_hacl/hacl.ml @@ -396,6 +396,88 @@ module Ed25519 : SIGNATURE = struct Hacl.Ed25519.verify ~pk ~msg ~signature end +module Secp256k1 : SIGNATURE = struct + type _ key = Sk : Bytes.t -> secret key | Pk : Bytes.t -> public key + + let size = 64 + + (* The size of a compressed key, which all functions in this module + * accept and export. For a more detailed explanation of public key forms + * see the [P256] module. *) + let pk_size = 33 + + let sk_size = 32 + + (* Generate a random sk_size buffer until it is valid to be used as + * secret key, i.e. non-zero and smaller than the prime order. + * This function is also used to generate signing secrets. *) + let rec get_valid_sk () = + let sk = Rand.gen sk_size in + if Hacl.K256.valid_sk ~sk then sk else get_valid_sk () + + let to_bytes : type a. a key -> Bytes.t = function + | Pk buf -> Bytes.copy buf + | Sk buf -> Bytes.copy buf + + let sk_of_bytes sk = + if Bytes.length sk = sk_size && Hacl.K256.valid_sk ~sk then + Some (Sk (Bytes.copy sk)) + else None + + (* A difference with respect to the P-256 interface, this function + * only accepts keys in the compressed form *) + let pk_of_bytes_without_validation pk = + if Bytes.length pk <> pk_size then None else Some (Pk (Bytes.copy pk)) + + let pk_of_bytes pk = + Option.bind (pk_of_bytes_without_validation pk) (fun (Pk pk) -> + (* [Hacl.K256.valid_pk] only accepts raw keys, but this function only + * exports compressed keys. Convert pk to raw in order to validate. *) + Option.bind (Hacl.K256.compressed_to_raw pk) (fun raw_pk -> + if Hacl.K256.valid_pk ~pk:raw_pk then Some (Pk pk) else None)) + + let blit_to_bytes : type a. a key -> ?pos:int -> Bytes.t -> unit = + fun key ?(pos = 0) buf -> + match key with + | Pk pk -> Bytes.blit pk 0 buf pos pk_size + | Sk sk -> Bytes.blit sk 0 buf pos sk_size + + let compare : type a. a key -> a key -> int = + fun a b -> + (* TODO re-group once coverage ppx is updated *) + match (a, b) with + | Pk a, Pk b -> Bytes.compare a b + | Sk a, Sk b -> Bytes.compare a b + + let equal : type a. a key -> a key -> bool = fun a b -> compare a b = 0 + + let neuterize : type a. a key -> public key = function + | Pk pk -> Pk pk + | Sk sk -> ( + match Hacl.K256.secret_to_public ~sk with + | Some pk -> Pk (Hacl.K256.raw_to_compressed pk) + | None -> failwith "Secp256k1.neuterize: failure") + + let keypair () = + let sk = Sk (get_valid_sk ()) in + (neuterize sk, sk) + + let sign ~sk:(Sk sk) ~msg = + (* A random non-zero signing secret k is generated which, similar to + * secret keys, needs to be non-zero and smaller than the prime order. *) + let k = get_valid_sk () in + let res = Hacl.K256.Libsecp256k1.sign ~sk ~msg ~k in + Bytes.fill k 0 32 '\x00' ; + match res with + | Some signature -> signature + | None -> failwith "Secp256k1.sign: signing failure" + + let verify ~pk:(Pk pk) ~msg ~signature = + match Hacl.K256.compressed_to_raw pk with + | Some pk -> Hacl.K256.Libsecp256k1.verify ~pk ~msg ~signature + | None -> failwith "Secp256k1.verify: unexpected pk format" +end + module P256 : SIGNATURE = struct (* A public key is an elliptic curve point with 2 32-byte coordinates (x, y). Internally we use 3 formats to represent public keys: diff --git a/src/lib_hacl/test/test_hacl.ml b/src/lib_hacl/test/test_hacl.ml index ff0b53e1e875..07be4dea3bed 100644 --- a/src/lib_hacl/test/test_hacl.ml +++ b/src/lib_hacl/test/test_hacl.ml @@ -426,7 +426,7 @@ let test_write_key_p256 () = let test_keypair_p256 () = let pk, sk = keypair () in let pk' = neuterize sk in - Alcotest.(check bytes "keccak_256" (P256.to_bytes pk) (P256.to_bytes pk')) + Alcotest.(check bytes "keypair_p256" (P256.to_bytes pk) (P256.to_bytes pk')) let test_keypair_p256 () = for _i = 0 to nb_iterations - 1 do @@ -435,6 +435,7 @@ let test_keypair_p256 () = let test_sign_p256 () = let pk, sk = keypair () in + let msg = Hash.SHA256.digest msg in let signature = sign ~sk ~msg in Alcotest.(check bool "sign_p256" true (verify ~pk ~msg ~signature)) @@ -491,6 +492,115 @@ let p256 = ("test_vectors", `Quick, test_vectors_p256); ] +open Secp256k1 + +let check_secp256k1_bytes_secret = + Alcotest.testable + (fun fmt bytes -> + Format.fprintf fmt "%S" (Bytes.to_string (Secp256k1.to_bytes bytes))) + Secp256k1.equal + +let check_secp256k1_bytes_public = + Alcotest.testable + (fun fmt bytes -> + Format.fprintf fmt "%S" (Bytes.to_string (Secp256k1.to_bytes bytes))) + Secp256k1.equal + +let nb_iterations = 10 + +let test_export_secp256k1 () = + let pk, sk = keypair () in + let sk_bytes = to_bytes sk in + let pk_bytes = to_bytes pk in + Alcotest.(check int __LOC__ sk_size (Bytes.length sk_bytes)) ; + Alcotest.(check int __LOC__ pk_size (Bytes.length pk_bytes)) ; + match (sk_of_bytes sk_bytes, pk_of_bytes pk_bytes) with + | Some sk', Some pk' -> + let pk'' = neuterize pk' in + Alcotest.(check check_secp256k1_bytes_secret "sk'" sk sk') ; + Alcotest.(check check_secp256k1_bytes_public "pk'" pk pk') ; + Alcotest.(check check_secp256k1_bytes_public "pk''" pk pk'') ; + Alcotest.(check check_secp256k1_bytes_public "pk" pk' pk') + | _ -> Alcotest.fail "export_secp256k1" + +let test_export_secp256k1 () = + for _i = 0 to nb_iterations - 1 do + test_export_secp256k1 () + done + +let test_write_key_secp256k1 () = + let pk, sk = keypair () in + let sk_bytes = to_bytes sk in + let pk_bytes = to_bytes pk in + let sk_buf = Bytes.create sk_size in + let pk_buf = Bytes.create pk_size in + blit_to_bytes sk sk_buf ; + blit_to_bytes pk pk_buf ; + Alcotest.(check bytes "write_key_secp256k1 sk" sk_bytes sk_buf) ; + Alcotest.(check bytes "write_key_secp256k1 pk" pk_bytes pk_buf) + +let test_write_key_pos_secp256k1 () = + let pos = 42 in + let pk, sk = keypair () in + let sk_bytes = to_bytes sk in + let pk_bytes = to_bytes pk in + let sk_buf = Bytes.create (sk_size + pos) in + let pk_buf = Bytes.create (pk_size + pos) in + blit_to_bytes ~pos sk sk_buf ; + blit_to_bytes ~pos pk pk_buf ; + Alcotest.( + check + bytes + "write_key_pos_secp256k1 sk" + sk_bytes + (Bytes.sub sk_buf pos sk_size)) ; + Alcotest.( + check + bytes + "write_key_pos_secp256k1 pk" + pk_bytes + (Bytes.sub pk_buf pos pk_size)) + +let test_write_key_secp256k1 () = + for _i = 0 to nb_iterations - 1 do + test_write_key_secp256k1 () ; + test_write_key_pos_secp256k1 () + done + +let test_keypair_secp256k1 () = + let pk, sk = keypair () in + let pk' = neuterize sk in + Alcotest.( + check + bytes + "keypair_secp256k1" + (Secp256k1.to_bytes pk) + (Secp256k1.to_bytes pk')) + +let test_keypair_secp256k1 () = + for _i = 0 to nb_iterations - 1 do + test_keypair_secp256k1 () + done + +let test_sign_secp256k1 () = + let pk, sk = keypair () in + let msg = Hash.SHA256.digest msg in + let signature = sign ~sk ~msg in + Alcotest.(check bool "sign_secp256k1" true (verify ~pk ~msg ~signature)) + +let test_sign_secp256k1 () = + for _i = 0 to nb_iterations - 1 do + test_sign_secp256k1 () + done + +let secp256k1 = + [ + ("export", `Quick, test_export_secp256k1); + ("write_key", `Quick, test_write_key_secp256k1); + ("keypair", `Quick, test_keypair_secp256k1); + ("sign", `Quick, test_sign_secp256k1); + ] + let tests = [ ("hash", hash); @@ -499,6 +609,7 @@ let tests = ("box", box); ("ed25519", ed25519); ("p256", p256); + ("secp256k1", secp256k1); ] let () = Alcotest.run ~__FILE__ "tezos-crypto" tests -- GitLab From 9ea6ecc92087dba4a062c02a678067d22b369319 Mon Sep 17 00:00:00 2001 From: Victor Dumitrescu Date: Mon, 15 May 2023 17:56:45 +0200 Subject: [PATCH 2/6] Lib_hacl: test vectors for secp256k1 --- manifest/main.ml | 1 + src/lib_hacl/test/dune | 3 +- src/lib_hacl/test/test_hacl.ml | 30 +++++++++++ src/lib_hacl/test/vectors_secp256k1.ml | 75 ++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 src/lib_hacl/test/vectors_secp256k1.ml diff --git a/manifest/main.ml b/manifest/main.ml index 822ef2d70021..68cf0180debe 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -806,6 +806,7 @@ let _octez_hacl_tests = "test"; "vectors_p256"; "vectors_ed25519"; + "vectors_secp256k1"; ] ~path:"src/lib_hacl/test" ~opam:"tezos-hacl" diff --git a/src/lib_hacl/test/dune b/src/lib_hacl/test/dune index dc75e64c5142..214c23ae82c8 100644 --- a/src/lib_hacl/test/dune +++ b/src/lib_hacl/test/dune @@ -37,7 +37,8 @@ test_prop_hacl_hash test vectors_p256 - vectors_ed25519)) + vectors_ed25519 + vectors_secp256k1)) (executable (name main) diff --git a/src/lib_hacl/test/test_hacl.ml b/src/lib_hacl/test/test_hacl.ml index 07be4dea3bed..2a3597d5f696 100644 --- a/src/lib_hacl/test/test_hacl.ml +++ b/src/lib_hacl/test/test_hacl.ml @@ -593,12 +593,42 @@ let test_sign_secp256k1 () = test_sign_secp256k1 () done +let test_vectors_secp256k1 () = + let open Vectors_secp256k1 in + let open Hacl_star.Hacl in + (* The test vectors from [vectors_secp256k1.ml] have public keys in + * the uncompressed format and require hashing their messages with SHA-256. + * We convert the public keys into the compressed format used in this library + * and hash the messages. *) + List.iter + (fun case -> + match + Option.map + K256.raw_to_compressed + (K256.uncompressed_to_raw (hex case.pub_key)) + with + | Some pk -> ( + match Secp256k1.pk_of_bytes pk with + | Some pk -> + let msg = Hacl.Hash.SHA256.digest (hex case.message) in + let signature = hex case.signature in + Alcotest.( + check + bool + "vectors_secp256k1" + (Hacl.Secp256k1.verify ~pk ~msg ~signature) + case.expected) + | None -> Alcotest.fail "pk_of_bytes") + | None -> Alcotest.fail "HACL* uncompressed_to_raw") + cases + let secp256k1 = [ ("export", `Quick, test_export_secp256k1); ("write_key", `Quick, test_write_key_secp256k1); ("keypair", `Quick, test_keypair_secp256k1); ("sign", `Quick, test_sign_secp256k1); + ("test_vectors", `Quick, test_vectors_secp256k1); ] let tests = diff --git a/src/lib_hacl/test/vectors_secp256k1.ml b/src/lib_hacl/test/vectors_secp256k1.ml new file mode 100644 index 000000000000..2f72412cd130 --- /dev/null +++ b/src/lib_hacl/test/vectors_secp256k1.ml @@ -0,0 +1,75 @@ +type secp256k1_case = { + message : string; + pub_key : string; + signature : string; + expected : bool; +} + +(* Test vectors from https://github.com/google/wycheproof/blob/b063b4aedae951c69df014cd25fa6d69ae9e8cb9/testvectors/ecdsa_secp256k1_sha256_test.json *) + +let cases = + [ + { + (* Test case id 1 *) + pub_key = + "04b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9"; + message = "313233343030"; + signature = + "813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365900e75ad233fcc908509dbff5922647db37c21f4afd3203ae8dc4ae7794b0f87"; + expected = false; + }; + { + (* Test case id 3 *) + pub_key = + "04b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9"; + message = "313233343030"; + signature = + "813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba"; + expected = true; + }; + { + (* Test case id 119 *) + pub_key = + "04b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9"; + message = "313233343030"; + signature = + "813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656df18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba"; + expected = false; + }; + { + (* Test case id 129 *) + pub_key = + "04b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9"; + message = "313233343030"; + signature = + "00000000000000000000000000000000000000000000000000000000000000006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba"; + expected = false; + }; + { + (* Test case id 285 *) + pub_key = + "0407310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc36226a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0"; + message = "313233343030"; + signature = + "000000000000000000000000000000014551231950b75fc4402da1722fc9baebfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e"; + expected = false; + }; + { + (* Test case id 286 *) + pub_key = + "0407310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc36226a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0"; + message = "313233343030"; + signature = + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2cfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e"; + expected = false; + }; + { + (* Test case id 375 *) + pub_key = + "04d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb93f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff"; + message = "4d657373616765"; + signature = + "592c41e16517f12fcabd98267674f974b588e9f35d35406c1a7bb2ed1d19b7b8c19a5f942607c3551484ff0dc97281f0cdc82bc48e2205a0645c0cf3d7f59da0"; + expected = false; + }; + ] -- GitLab From 54393536408e3cd127b1778ce5458e892b4eb331 Mon Sep 17 00:00:00 2001 From: Victor Dumitrescu Date: Tue, 21 Mar 2023 09:55:28 +0100 Subject: [PATCH 3/6] Lib_crypto: HACL* secp256k1 integration --- manifest/main.ml | 1 - opam/tezos-crypto.opam | 1 - src/lib_base/dune | 2 +- src/lib_base/test/dune | 2 +- src/lib_client_base/dune | 2 +- src/lib_client_base/test/dune | 2 +- src/lib_context/merkle_proof_encoding/dune | 2 +- src/lib_context/sigs/dune | 2 +- src/lib_crypto/dune | 3 +- src/lib_crypto/secp256k1.ml | 105 ++++++++++----------- src/lib_crypto/test/dune | 2 +- src/lib_p2p_services/dune | 2 +- src/lib_sapling/dune | 2 +- src/lib_sapling/test/dune | 2 +- src/lib_shell_services/dune | 2 +- src/lib_shell_services/test/dune | 2 +- src/lib_signer_services/dune | 2 +- src/lib_version/dune | 2 +- src/lib_version/test/dune | 2 +- 19 files changed, 66 insertions(+), 74 deletions(-) diff --git a/manifest/main.ml b/manifest/main.ml index 68cf0180debe..f8f0437c0538 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -921,7 +921,6 @@ let octez_crypto = octez_lwt_result_stdlib; lwt; octez_hacl; - secp256k1_internal; octez_error_monad |> open_ |> open_ ~m:"TzLwtreslib"; octez_rpc; aches; diff --git a/opam/tezos-crypto.opam b/opam/tezos-crypto.opam index 415aca8d2881..4be51d440a54 100644 --- a/opam/tezos-crypto.opam +++ b/opam/tezos-crypto.opam @@ -15,7 +15,6 @@ depends: [ "tezos-lwt-result-stdlib" "lwt" { >= "5.6.0" } "tezos-hacl" - "secp256k1-internal" { >= "0.4.0" } "tezos-error-monad" "tezos-rpc" "aches" { >= "1.0.0" } diff --git a/src/lib_base/dune b/src/lib_base/dune index ee3679db298f..5f788c0ffe73 100644 --- a/src/lib_base/dune +++ b/src/lib_base/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name tezos_base) diff --git a/src/lib_base/test/dune b/src/lib_base/test/dune index a65372282770..dc44371ea2e1 100644 --- a/src/lib_base/test/dune +++ b/src/lib_base/test/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name src_lib_base_test_tezt_lib) diff --git a/src/lib_client_base/dune b/src/lib_client_base/dune index 9232d01b452d..4da5e0e01f78 100644 --- a/src/lib_client_base/dune +++ b/src/lib_client_base/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name tezos_client_base) diff --git a/src/lib_client_base/test/dune b/src/lib_client_base/test/dune index aea92b18dfad..a30ac9b48bfb 100644 --- a/src/lib_client_base/test/dune +++ b/src/lib_client_base/test/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name src_lib_client_base_test_tezt_lib) diff --git a/src/lib_context/merkle_proof_encoding/dune b/src/lib_context/merkle_proof_encoding/dune index 101452408cff..8d926cb9b116 100644 --- a/src/lib_context/merkle_proof_encoding/dune +++ b/src/lib_context/merkle_proof_encoding/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name tezos_context_merkle_proof_encoding) diff --git a/src/lib_context/sigs/dune b/src/lib_context/sigs/dune index f01f71e5b5be..9868f0e3e3b4 100644 --- a/src/lib_context/sigs/dune +++ b/src/lib_context/sigs/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name tezos_context_sigs) diff --git a/src/lib_crypto/dune b/src/lib_crypto/dune index e6679a905b37..5583a9040fb9 100644 --- a/src/lib_crypto/dune +++ b/src/lib_crypto/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name tezos_crypto) @@ -13,7 +13,6 @@ tezos-lwt-result-stdlib lwt tezos-hacl - secp256k1-internal tezos-error-monad tezos-rpc aches diff --git a/src/lib_crypto/secp256k1.ml b/src/lib_crypto/secp256k1.ml index 275230395799..cd74c9ee3620 100644 --- a/src/lib_crypto/secp256k1.ml +++ b/src/lib_crypto/secp256k1.ml @@ -45,33 +45,26 @@ end let () = Base58.check_encoded_prefix Public_key_hash.b58check_encoding "tz2" 36 -open Libsecp256k1.External - -let context = - let ctx = Context.create () in - match Context.randomize ctx (Bigstring.of_bytes (Hacl.Rand.gen 32)) with - | false -> failwith "Secp256k1 context randomization failed. Aborting." - | true -> ctx +open Hacl.Secp256k1 module Public_key = struct - type t = Key.public Key.t + type t = Hacl.public key let name = "Secp256k1.Public_key" let title = "A Secp256k1 public key" - let to_bytes pk = Bigstring.to_bytes (Key.to_bytes context pk) - - let of_bytes_opt s = - Option.catch (fun () -> Key.read_pk_exn context (Bigstring.of_bytes s)) + let to_bytes = to_bytes let to_string s = Bytes.to_string (to_bytes s) + let of_bytes_opt = pk_of_bytes + let of_string_opt s = of_bytes_opt (Bytes.of_string s) - let of_bytes_without_validation = of_bytes_opt + let of_bytes_without_validation = pk_of_bytes_without_validation - let size _ = Key.compressed_pk_bytes + let size _ = pk_size type Base58.data += Data of t @@ -139,28 +132,23 @@ module Public_key = struct end module Secret_key = struct - type t = Key.secret Key.t + type t = Hacl.secret key let name = "Secp256k1.Secret_key" let title = "A Secp256k1 secret key" - let size = Key.secret_bytes - - let of_bytes_opt s = - match Key.read_sk context (Bigstring.of_bytes s) with - | Ok x -> Some x - | _ -> None - - let to_bigstring = Key.to_bytes context + let size = sk_size - let to_bytes x = Bigstring.to_bytes (to_bigstring x) + let to_bytes = to_bytes let to_string s = Bytes.to_string (to_bytes s) + let of_bytes_opt = sk_of_bytes + let of_string_opt s = of_bytes_opt (Bytes.of_string s) - let to_public_key key = Key.neuterize_exn context key + let to_public_key = neuterize type Base58.data += Data of t @@ -177,7 +165,7 @@ module Secret_key = struct include Compare.Make (struct type nonrec t = t - let compare a b = Bigstring.compare (Key.buffer a) (Key.buffer b) + let compare = compare end) include Helpers.MakeRaw (struct @@ -225,7 +213,7 @@ module Secret_key = struct let pp ppf t = Format.fprintf ppf "%s" (to_b58check t) end -type t = Sign.plain Sign.t +type t = Bytes.t type watermark = Bytes.t @@ -233,17 +221,14 @@ let name = "Secp256k1" let title = "A Secp256k1 signature" -let size = Sign.plain_bytes - -let of_bytes_opt s = - match Sign.read context (Bigstring.of_bytes s) with - | Ok s -> Some s - | Error _ -> None +let size = size -let to_bytes t = Bigstring.to_bytes (Sign.to_bytes ~der:false context t) +let to_bytes s = Bytes.copy s let to_string s = Bytes.to_string (to_bytes s) +let of_bytes_opt s = if Bytes.length s = size then Some s else None + let of_string_opt s = of_bytes_opt (Bytes.of_string s) type Base58.data += Data of t @@ -258,12 +243,6 @@ let b58check_encoding = let () = Base58.check_encoded_prefix b58check_encoding "spsig1" 99 -include Compare.Make (struct - type nonrec t = t - - let compare a b = Bigstring.compare (Sign.buffer a) (Sign.buffer b) -end) - include Helpers.MakeRaw (struct type nonrec t = t @@ -315,29 +294,45 @@ let sign ?watermark sk msg = Blake2B.to_bytes @@ Blake2B.hash_bytes @@ match watermark with None -> [msg] | Some prefix -> [prefix; msg] in - Sign.sign_exn context ~sk (Bigstring.of_bytes msg) + sign ~sk ~msg -let check ?watermark public_key signature msg = +let check ?watermark pk signature msg = let msg = Blake2B.to_bytes @@ Blake2B.hash_bytes @@ match watermark with None -> [msg] | Some prefix -> [prefix; msg] in - Sign.verify_exn - context - ~pk:public_key - ~msg:(Bigstring.of_bytes msg) - ~signature - -let generate_key ?(seed = Hacl.Rand.gen 32) () = - let sk = Key.read_sk_exn context (Bigstring.of_bytes seed) in - let pk = Key.neuterize_exn context sk in - let pkh = Public_key.hash pk in - (pkh, pk, sk) + verify ~pk ~msg ~signature + +let generate_key ?seed () = + match seed with + | None -> + let pk, sk = keypair () in + (Public_key.hash pk, pk, sk) + | Some seed -> ( + let seedlen = Bytes.length seed in + if seedlen < Secret_key.size then + invalid_arg + (Printf.sprintf + "Secp256k1.generate_key: seed must be at least %d bytes long (got \ + %d)" + Secret_key.size + seedlen) + else + match sk_of_bytes (Bytes.sub seed 0 Secret_key.size) with + | None -> invalid_arg "Secp256k1.generate_key: invalid seed" + | Some sk -> + let pk = neuterize sk in + (Public_key.hash pk, pk, sk)) let deterministic_nonce sk msg = let key = Secret_key.to_bytes sk in Hacl.Hash.SHA256.HMAC.digest ~key ~msg let deterministic_nonce_hash sk msg = - let nonce = deterministic_nonce sk msg in - Blake2B.to_bytes (Blake2B.hash_bytes [nonce]) + Blake2B.to_bytes (Blake2B.hash_bytes [deterministic_nonce sk msg]) + +include Compare.Make (struct + type nonrec t = t + + let compare = Bytes.compare +end) diff --git a/src/lib_crypto/test/dune b/src/lib_crypto/test/dune index f748e4468f78..a886251bb7dc 100644 --- a/src/lib_crypto/test/dune +++ b/src/lib_crypto/test/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name src_lib_crypto_test_tezt_lib) diff --git a/src/lib_p2p_services/dune b/src/lib_p2p_services/dune index f122b4b34e2e..33c9b6e5d804 100644 --- a/src/lib_p2p_services/dune +++ b/src/lib_p2p_services/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name tezos_p2p_services) diff --git a/src/lib_sapling/dune b/src/lib_sapling/dune index 2b9107077673..67b8ecb3ab57 100644 --- a/src/lib_sapling/dune +++ b/src/lib_sapling/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name tezos_sapling) diff --git a/src/lib_sapling/test/dune b/src/lib_sapling/test/dune index c957cee7aa17..16a4438ca8be 100644 --- a/src/lib_sapling/test/dune +++ b/src/lib_sapling/test/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name src_lib_sapling_test_tezt_lib) diff --git a/src/lib_shell_services/dune b/src/lib_shell_services/dune index 2c1f1eca35de..a27d2569f28f 100644 --- a/src/lib_shell_services/dune +++ b/src/lib_shell_services/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name tezos_shell_services) diff --git a/src/lib_shell_services/test/dune b/src/lib_shell_services/test/dune index 1d113c8ccaf1..124bc3e3969f 100644 --- a/src/lib_shell_services/test/dune +++ b/src/lib_shell_services/test/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name src_lib_shell_services_test_tezt_lib) diff --git a/src/lib_signer_services/dune b/src/lib_signer_services/dune index 020927621f69..daac1e1727f6 100644 --- a/src/lib_signer_services/dune +++ b/src/lib_signer_services/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name tezos_signer_services) diff --git a/src/lib_version/dune b/src/lib_version/dune index 0da0dcf6e004..1ef052cceeaa 100644 --- a/src/lib_version/dune +++ b/src/lib_version/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name tezos_version) diff --git a/src/lib_version/test/dune b/src/lib_version/test/dune index 2185770405c2..4fbbf22d1682 100644 --- a/src/lib_version/test/dune +++ b/src/lib_version/test/dune @@ -1,7 +1,7 @@ ; This file was automatically generated, do not edit. ; Edit file manifest/main.ml instead. -(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381,secp256k1-wasm)))) +(env (_ (env-vars (NODE_PRELOAD hacl-wasm,ocaml-bls12-381)))) (library (name src_lib_version_test_tezt_lib) -- GitLab From 5111ea37772362b5386638461a7ac27002599472 Mon Sep 17 00:00:00 2001 From: Victor Dumitrescu Date: Tue, 21 Mar 2023 09:56:05 +0100 Subject: [PATCH 4/6] Lib_signer_backends: use secp256k1 from lib_crypto --- manifest/main.ml | 1 + opam/tezos-signer-backends.opam | 1 + src/lib_signer_backends/dune | 3 ++- src/lib_signer_backends/encrypted.ml | 4 ++-- .../unix/ledger.available.ml | 20 ++++++++++--------- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/manifest/main.ml b/manifest/main.ml index f8f0437c0538..d2fed0785278 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -3307,6 +3307,7 @@ let octez_signer_backends = octez_signer_services |> open_; octez_shell_services |> open_; uri; + secp256k1_internal; ] let _octez_signer_backends_tests = diff --git a/opam/tezos-signer-backends.opam b/opam/tezos-signer-backends.opam index 533eb72a3650..bc1a64523a7c 100644 --- a/opam/tezos-signer-backends.opam +++ b/opam/tezos-signer-backends.opam @@ -18,6 +18,7 @@ depends: [ "tezos-signer-services" "tezos-shell-services" "uri" { >= "3.1.0" } + "secp256k1-internal" { >= "0.4.0" } "ocplib-endian" "fmt" { >= "0.8.7" } "tezos-clic" diff --git a/src/lib_signer_backends/dune b/src/lib_signer_backends/dune index 69f8b4915ba9..d7d19dee08fe 100644 --- a/src/lib_signer_backends/dune +++ b/src/lib_signer_backends/dune @@ -13,7 +13,8 @@ tezos-rpc-http-client tezos-signer-services tezos-shell-services - uri) + uri + secp256k1-internal) (flags (:standard) -open Tezos_base.TzPervasives diff --git a/src/lib_signer_backends/encrypted.ml b/src/lib_signer_backends/encrypted.ml index 45de1d2275b3..77ae5957e550 100644 --- a/src/lib_signer_backends/encrypted.ml +++ b/src/lib_signer_backends/encrypted.ml @@ -162,9 +162,9 @@ module Encodings = struct ~wrap:(fun sk -> Encrypted_ed25519 sk) let secp256k1 = - let open Libsecp256k1.External in let length = - Key.secret_bytes + Tezos_crypto.Crypto_box.tag_length + Raw.salt_len + Tezos_crypto.Hacl.Secp256k1.sk_size + Tezos_crypto.Crypto_box.tag_length + + Raw.salt_len in Tezos_crypto.Base58.register_encoding ~prefix:Tezos_crypto.Base58.Prefix.secp256k1_encrypted_secret_key diff --git a/src/lib_signer_backends/unix/ledger.available.ml b/src/lib_signer_backends/unix/ledger.available.ml index 885dde59ea7b..f448af77289a 100644 --- a/src/lib_signer_backends/unix/ledger.available.ml +++ b/src/lib_signer_backends/unix/ledger.available.ml @@ -212,15 +212,17 @@ module Ledger_commands = struct TzEndian.set_int8 pk 0 0 ; (* hackish, but works. *) Data_encoding.Binary.of_bytes_exn Signature.Public_key.encoding pk - | Secp256k1 -> - let open Libsecp256k1.External in - let buf = Bigstring.create (Key.compressed_pk_bytes + 1) in - let pk = Key.read_pk_exn secp256k1_ctx (Cstruct.to_bigarray pk) in - EndianBigstring.BigEndian.set_int8 buf 0 1 ; - let _nb_written = Key.write secp256k1_ctx ~pos:1 buf pk in - Data_encoding.Binary.of_bytes_exn - Signature.Public_key.encoding - (Bigstring.to_bytes buf) + | Secp256k1 -> ( + let open Tezos_crypto.Hacl.Secp256k1 in + let buf = Bytes.create (pk_size + 1) in + match pk_of_bytes (Cstruct.to_bytes pk) with + | None -> + Stdlib.failwith + "Impossible to read secp256k1 public key from Ledger" + | Some pk -> + TzEndian.set_int8 buf 0 1 ; + blit_to_bytes pk ~pos:1 buf ; + Data_encoding.Binary.of_bytes_exn Signature.Public_key.encoding buf) | Secp256r1 -> ( let open Tezos_crypto.Hacl.P256 in let buf = Bytes.create (pk_size + 1) in -- GitLab From 074486eae8ea9f3ff8b26d5e5078bea97b97ed08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Thu, 23 Feb 2023 16:11:15 +0100 Subject: [PATCH 5/6] Proto/Michelson: adapt the memory model of secp256k1 pk According to Obj.reachable_words, the new HACL* implementation of secp256k1 public keys uses a bit less memory than the libsecp256k1 one. 8 bytes less exactly. This commit adapts the memory model in proto Alpha. --- src/proto_alpha/lib_protocol/script_typed_ir_size.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proto_alpha/lib_protocol/script_typed_ir_size.ml b/src/proto_alpha/lib_protocol/script_typed_ir_size.ml index 2bb0e205b604..7de7bb9e077b 100644 --- a/src/proto_alpha/lib_protocol/script_typed_ir_size.ml +++ b/src/proto_alpha/lib_protocol/script_typed_ir_size.ml @@ -137,7 +137,7 @@ let public_key_size (x : public_key) = +? match x with | Ed25519 _ -> 64 - | Secp256k1 _ -> 72 + | Secp256k1 _ -> 64 | P256 _ -> 96 | Bls _ -> 64 -- GitLab From fe5ffc3b321d9db228001268d970f23a1ffa4464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Thu, 23 Feb 2023 16:14:30 +0100 Subject: [PATCH 6/6] Tests/Michelson: fix size model test for public keys Allow a difference of 8 bytes between the protocol size model and the actual size (as measured by Obj.reachable_words). This is needed to account for a reduction in the memory footprint of the underlying implementation which has changed to HACL*. --- .../test/integration/michelson/test_script_typed_ir_size.ml | 2 +- .../test/integration/michelson/test_script_typed_ir_size.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proto_016_PtMumbai/lib_protocol/test/integration/michelson/test_script_typed_ir_size.ml b/src/proto_016_PtMumbai/lib_protocol/test/integration/michelson/test_script_typed_ir_size.ml index 46edd4851068..74f1f639053d 100644 --- a/src/proto_016_PtMumbai/lib_protocol/test/integration/michelson/test_script_typed_ir_size.ml +++ b/src/proto_016_PtMumbai/lib_protocol/test/integration/michelson/test_script_typed_ir_size.ml @@ -265,7 +265,7 @@ let check_value_size () = ===== *) @ (let show = Tezos_crypto.Signature.Public_key.pp in - exs nsample show Key_t ": key_t") + exs ~error:8 nsample show Key_t ": key_t") (* Timestamp_t =========== diff --git a/src/proto_017_PtNairob/lib_protocol/test/integration/michelson/test_script_typed_ir_size.ml b/src/proto_017_PtNairob/lib_protocol/test/integration/michelson/test_script_typed_ir_size.ml index 2e437f1ee0df..02960952f3d5 100644 --- a/src/proto_017_PtNairob/lib_protocol/test/integration/michelson/test_script_typed_ir_size.ml +++ b/src/proto_017_PtNairob/lib_protocol/test/integration/michelson/test_script_typed_ir_size.ml @@ -265,7 +265,7 @@ let check_value_size () = ===== *) @ (let show = Signature.Public_key.pp in - exs nsample show Key_t ": key_t") + exs ~error:8 nsample show Key_t ": key_t") (* Timestamp_t =========== -- GitLab