From 0d28d109de3e61176edb4c4015da020e044def72 Mon Sep 17 00:00:00 2001 From: Victor Dumitrescu Date: Tue, 16 Dec 2025 15:49:09 +0100 Subject: [PATCH 1/6] Manifest: Add libcrux bindings and lib_ml_dsa library --- .gitlab/ci/pipelines/opam.daily.yml | 3 ++ dune-project | 4 ++ manifest/product_octez.ml | 72 +++++++++++++++++++++++++++++ opam/octez-libcrux-ml-dsa.opam | 20 ++++++++ opam/octez-libs.opam | 1 + opam/octez-ml-dsa-44-api.opam | 21 +++++++++ opam/octez-ml-dsa-test.opam | 25 ++++++++++ opam/octez-ml-dsa.opam | 21 +++++++++ script-inputs/ci-opam-package-tests | 3 ++ scripts/lint.sh | 2 + src/lib_crypto/dune | 1 + tezt/tests/dune | 1 + tobi/config | 4 ++ 13 files changed, 178 insertions(+) create mode 100644 opam/octez-libcrux-ml-dsa.opam create mode 100644 opam/octez-ml-dsa-44-api.opam create mode 100644 opam/octez-ml-dsa-test.opam create mode 100644 opam/octez-ml-dsa.opam diff --git a/.gitlab/ci/pipelines/opam.daily.yml b/.gitlab/ci/pipelines/opam.daily.yml index 8368eb9f0551..5f12b54ff51d 100644 --- a/.gitlab/ci/pipelines/opam.daily.yml +++ b/.gitlab/ci/pipelines/opam.daily.yml @@ -762,8 +762,11 @@ opam:all_7: - octez-rust-deps - octez-riscv-api - octez-protocol-compiler-compat + - octez-ml-dsa-44-api + - octez-ml-dsa - octez-lwt-domain - octez-libs + - octez-libcrux-ml-dsa - octez-lib-upnp-args - octez-internal-libs - octez-igd-next diff --git a/dune-project b/dune-project index bed1b1899eb8..e13e2195215f 100644 --- a/dune-project +++ b/dune-project @@ -49,8 +49,12 @@ (package (name octez-l2-libs)) (package (name octez-lib-upnp)) (package (name octez-lib-upnp-args)) +(package (name octez-libcrux-ml-dsa)) (package (name octez-libs)) (package (name octez-lwt-domain)) +(package (name octez-ml-dsa)) +(package (name octez-ml-dsa-44-api)) +(package (name octez-ml-dsa-test)(allow_empty)) (package (name octez-node)) (package (name octez-node-config)) (package (name octez-performance-metrics)) diff --git a/manifest/product_octez.ml b/manifest/product_octez.ml index 3a97892fc78a..793c5f295474 100644 --- a/manifest/product_octez.ml +++ b/manifest/product_octez.ml @@ -834,6 +834,63 @@ let octez_event_logging_test_helpers = ~linkall:true ~bisect_ppx:No +let octez_libcrux_ml_dsa = + public_lib + "octez-libcrux-ml-dsa" + ~path:"src/rust_libcrux" + ~synopsis:"Rust foreign archive for libcrux_ml_dsa" + ~foreign_archives:["octez_libcrux_ml_dsa"] + ~dune: + Dune. + [ + [ + S "dirs"; + S ":standard"; + (* We need this stanza to ensure .cargo can be used as a + dependency via source_tree. *) + S ".cargo"; + (* Do not track Cargo output directory. *) + [S "not"; S "target"]; + ]; + [ + S "rule"; + [ + S "targets"; + S "liboctez_libcrux_ml_dsa.a"; + S "dlloctez_libcrux_ml_dsa.so"; + ]; + [ + S "deps"; + [S "file"; S "build.sh"]; + [S "file"; S "Cargo.toml"]; + [S "file"; S "Cargo.lock"]; + [S "file"; S "../../rust-toolchain"]; + [S "source_tree"; S ".cargo"]; + [S "source_tree"; S "api"]; + ]; + [S "action"; [S "no-infer"; [S "bash"; S "./build.sh"]]]; + ]; + ] + +let octez_ml_dsa_44_api = + public_lib + "octez-ml-dsa-44-api" + ~path:"src/lib_ml_dsa/bindings" + ~synopsis:"OCaml bindings for the ML-DSA-44 signature scheme from libcrux" + ~flags:(Flags.standard ~disable_warnings:[9; 27; 66] ()) + ~dep_globs_rec:["../../rust_libcrux/*"] + ~modules:["octez_ml_dsa_44_api"] + ~deps:[octez_libcrux_ml_dsa] + ~dune: + Dune.[[S "copy_files"; S "../../rust_libcrux/api/octez_ml_dsa_44_api.*"]] + +let octez_ml_dsa = + public_lib + "octez-ml-dsa" + ~path:"src/lib_ml_dsa" + ~synopsis:"ML-DSA-44 signature scheme" + ~deps:[octez_ml_dsa_44_api] + let octez_crypto = octez_lib "crypto" @@ -847,6 +904,7 @@ let octez_crypto = octez_lwt_result_stdlib; lwt; octez_hacl; + octez_ml_dsa; secp256k1_internal; octez_error_monad |> open_ |> open_ ~m:"TzLwtreslib"; octez_rpc; @@ -5212,6 +5270,20 @@ let _octez_riscv_pvm_test = octez_riscv_pvm; ] +let _octez_ml_dsa_test = + tezt + ["test_main"; "test_bindings"] + ~path:"src/lib_ml_dsa/test" + ~opam:"octez-ml-dsa-test" + ~synopsis:"Tests for ML-DSA-44" + ~deps: + [ + octez_base |> open_ ~m:"TzPervasives"; + octez_stdlib_unix |> open_; + alcotezt; + octez_ml_dsa; + ] + let octez_layer2_store = octez_l2_lib "layer2_store" diff --git a/opam/octez-libcrux-ml-dsa.opam b/opam/octez-libcrux-ml-dsa.opam new file mode 100644 index 000000000000..985adb9e0b15 --- /dev/null +++ b/opam/octez-libcrux-ml-dsa.opam @@ -0,0 +1,20 @@ +# This file was automatically generated, do not edit. +# Edit file manifest/main.ml instead. +opam-version: "2.0" +maintainer: "contact@tezos.com" +authors: ["Tezos devteam"] +homepage: "https://www.tezos.com/" +bug-reports: "https://gitlab.com/tezos/tezos/issues" +dev-repo: "git+https://gitlab.com/tezos/tezos.git" +license: "MIT" +depends: [ + "dune" { >= "3.11.1" } + "ocaml" { >= "4.14" } +] +build: [ + ["rm" "-rf" "vendors" "contrib"] + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} +] +available: os-family != "windows" +synopsis: "Rust foreign archive for libcrux_ml_dsa" diff --git a/opam/octez-libs.opam b/opam/octez-libs.opam index 7056ad6b5e30..caa2c77d5ec9 100644 --- a/opam/octez-libs.opam +++ b/opam/octez-libs.opam @@ -42,6 +42,7 @@ depends: [ "hacl-star-raw" "bls12-381" { = version } "octez-alcotezt" { = version } + "octez-ml-dsa" { = version } "secp256k1-internal" { >= "0.4.0" } "bigarray-compat" "eqaf" diff --git a/opam/octez-ml-dsa-44-api.opam b/opam/octez-ml-dsa-44-api.opam new file mode 100644 index 000000000000..73b6c3ca1ec9 --- /dev/null +++ b/opam/octez-ml-dsa-44-api.opam @@ -0,0 +1,21 @@ +# This file was automatically generated, do not edit. +# Edit file manifest/main.ml instead. +opam-version: "2.0" +maintainer: "contact@tezos.com" +authors: ["Tezos devteam"] +homepage: "https://www.tezos.com/" +bug-reports: "https://gitlab.com/tezos/tezos/issues" +dev-repo: "git+https://gitlab.com/tezos/tezos.git" +license: "MIT" +depends: [ + "dune" { >= "3.11.1" } + "ocaml" { >= "4.14" } + "octez-libcrux-ml-dsa" { = version } +] +build: [ + ["rm" "-rf" "vendors" "contrib"] + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} +] +available: os-family != "windows" +synopsis: "OCaml bindings for the ML-DSA-44 signature scheme from libcrux" diff --git a/opam/octez-ml-dsa-test.opam b/opam/octez-ml-dsa-test.opam new file mode 100644 index 000000000000..d7e7e1286a49 --- /dev/null +++ b/opam/octez-ml-dsa-test.opam @@ -0,0 +1,25 @@ +# This file was automatically generated, do not edit. +# Edit file manifest/main.ml instead. +opam-version: "2.0" +maintainer: "contact@tezos.com" +authors: ["Tezos devteam"] +homepage: "https://www.tezos.com/" +bug-reports: "https://gitlab.com/tezos/tezos/issues" +dev-repo: "git+https://gitlab.com/tezos/tezos.git" +license: "MIT" +depends: [ + "dune" { >= "3.11.1" } + "ocaml" { >= "4.14" } + "tezt" { with-test & >= "4.1.0" & < "5.0.0" } + "bls12-381" {with-test} + "octez-libs" {with-test} + "octez-alcotezt" {with-test} + "octez-ml-dsa" {with-test} +] +build: [ + ["rm" "-rf" "vendors" "contrib"] + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} +] +available: os-family != "windows" +synopsis: "Tests for ML-DSA-44" diff --git a/opam/octez-ml-dsa.opam b/opam/octez-ml-dsa.opam new file mode 100644 index 000000000000..22f2b12c8fcd --- /dev/null +++ b/opam/octez-ml-dsa.opam @@ -0,0 +1,21 @@ +# This file was automatically generated, do not edit. +# Edit file manifest/main.ml instead. +opam-version: "2.0" +maintainer: "contact@tezos.com" +authors: ["Tezos devteam"] +homepage: "https://www.tezos.com/" +bug-reports: "https://gitlab.com/tezos/tezos/issues" +dev-repo: "git+https://gitlab.com/tezos/tezos.git" +license: "MIT" +depends: [ + "dune" { >= "3.11.1" } + "ocaml" { >= "4.14" } + "octez-ml-dsa-44-api" { = version } +] +build: [ + ["rm" "-rf" "vendors" "contrib"] + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} +] +available: os-family != "windows" +synopsis: "ML-DSA-44 signature scheme" diff --git a/script-inputs/ci-opam-package-tests b/script-inputs/ci-opam-package-tests index 8d2e125dac40..55ad08871c02 100644 --- a/script-inputs/ci-opam-package-tests +++ b/script-inputs/ci-opam-package-tests @@ -22,8 +22,11 @@ octez-internal-libs all 7 octez-l2-libs all 6 octez-lib-upnp all 6 octez-lib-upnp-args all 7 +octez-libcrux-ml-dsa all 7 octez-libs all 7 octez-lwt-domain all 7 +octez-ml-dsa all 7 +octez-ml-dsa-44-api all 7 octez-node exec 1 octez-node-config all 4 octez-performance-metrics all 6 diff --git a/scripts/lint.sh b/scripts/lint.sh index 88cf07633b18..68b52be6bb53 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -56,6 +56,8 @@ src/rust_deps/rust_tezos_context/ocaml-api/rust_tezedge_gen.ml src/rust_deps/rust_tezos_context/ocaml-api/rust_tezedge_gen.mli etherlink/lwt_domain/lwt_domain.ml etherlink/lwt_domain/lwt_domain.mli +src/rust_libcrux/api/octez_ml_dsa_44_api.ml +src/rust_libcrux/api/octez_ml_dsa_44_api.mli EOF ) diff --git a/src/lib_crypto/dune b/src/lib_crypto/dune index f6014c63a1bc..73ec9d454ac6 100644 --- a/src/lib_crypto/dune +++ b/src/lib_crypto/dune @@ -11,6 +11,7 @@ octez-libs.lwt-result-stdlib lwt octez-libs.hacl + octez-ml-dsa secp256k1-internal octez-libs.error-monad octez-libs.rpc diff --git a/tezt/tests/dune b/tezt/tests/dune index df4ce072e122..5f9999795b5e 100644 --- a/tezt/tests/dune +++ b/tezt/tests/dune @@ -93,6 +93,7 @@ src_lib_polynomial_test_tezt_lib src_lib_p2p_tezt_tezt_lib src_lib_mockup_test_tezt_lib + src_lib_ml_dsa_test_tezt_lib src_lib_mec_test_tezt_lib src_lib_lwt_result_stdlib_test_tezt_lib src_lib_lazy_containers_test_tezt_lib diff --git a/tobi/config b/tobi/config index e0cf354cf52f..9139bdfd2cb8 100644 --- a/tobi/config +++ b/tobi/config @@ -80,8 +80,12 @@ octez-internal-libs: irmin/lib_irmin, irmin/lib_irmin/data, irmin/lib_irmin/mem, octez-l2-libs: src/lib_layer2_irmin_context, src/lib_layer2_riscv_context, src/lib_layer2_shell, src/lib_layer2_store, src/lib_layer2_store/test/, src/lib_scoru_wasm/bench, src/lib_scoru_wasm/bench/executable, src/lib_scoru_wasm/fast, src/lib_scoru_wasm/fast/test, src/lib_scoru_wasm/helpers, src/lib_scoru_wasm/test, src/lib_scoru_wasm/test/durable_snapshot, src/lib_scoru_wasm/test/helpers, src/lib_smart_rollup, src/lib_smart_rollup_node, src/lib_smart_rollup_node/migrations, src/lib_sqlite, src/lib_wasmer, src/lib_wasmer/test, src/lib_webassembly/extra, src/lib_webassembly/tests octez-lib-upnp: src/lib_upnp octez-lib-upnp-args: src/lib_upnp/args +octez-libcrux-ml-dsa: src/rust_libcrux octez-libs: brassaia-eio/lib_brassaia, brassaia-eio/lib_brassaia/data, brassaia-eio/lib_brassaia/mem, brassaia-eio/lib_brassaia_pack, brassaia-eio/lib_brassaia_pack/io, brassaia-eio/lib_brassaia_pack/mem, brassaia-eio/lib_brassaia_tezos, brassaia-eio/lib_ppx_brassaia, brassaia-eio/lib_ppx_brassaia/internal, brassaia-eio/test/helpers, brassaia/index/src/, brassaia/index/src/unix, brassaia/lib_brassaia, brassaia/lib_brassaia/data, brassaia/lib_brassaia/mem, brassaia/lib_brassaia_pack, brassaia/lib_brassaia_pack/mem, brassaia/lib_brassaia_pack/unix, brassaia/lib_brassaia_tezos, brassaia/lib_ppx_brassaia, brassaia/lib_ppx_brassaia/internal, brassaia/test/helpers, data-encoding/json-data-encoding/src, data-encoding/json-data-encoding/test, data-encoding/json-data-encoding/test-bson, data-encoding/src, data-encoding/test, data-encoding/test/expect, data-encoding/test/pbt, prometheus/app, prometheus/examples, prometheus/src, prometheus/tests, resto/src, resto/test, src/lib_aplonk, src/lib_aplonk/plonk-aggregation, src/lib_aplonk/test, src/lib_base, src/lib_base/p2p_identity_file, src/lib_base/test, src/lib_base/test_helpers, src/lib_base/unix, src/lib_base/unix/test, src/lib_bees, src/lib_bees/test, src/lib_bls12_381_hash, src/lib_bls12_381_hash/test, src/lib_bls12_381_polynomial, src/lib_bls12_381_polynomial/test, src/lib_bls12_381_signature, src/lib_bls12_381_signature/test, src/lib_clic, src/lib_clic/test, src/lib_clic/unix, src/lib_context, src/lib_context/disk, src/lib_context/encoding, src/lib_context/helpers, src/lib_context/memory, src/lib_context/memory/test, src/lib_context/merkle_proof_encoding, src/lib_context/sigs, src/lib_context/test, src/lib_context_brassaia, src/lib_context_brassaia/disk, src/lib_context_brassaia/encoding, src/lib_context_brassaia/helpers, src/lib_context_brassaia/memory, src/lib_context_brassaia/merkle_proof_encoding, src/lib_context_tezedge, src/lib_crypto, src/lib_crypto/test, src/lib_crypto/test-unix, src/lib_crypto_dal, src/lib_crypto_dal/dal_config, src/lib_crypto_dal/test, src/lib_distributed_plonk, src/lib_distributed_plonk/communication, src/lib_distributed_plonk/distribution, src/lib_distributed_plonk/distribution/test, src/lib_distributed_plonk/test, src/lib_epoxy_tx, src/lib_epoxy_tx/test, src/lib_error_monad, src/lib_error_monad/test, src/lib_error_monad_legacy, src/lib_event_logging, src/lib_event_logging/test_helpers, src/lib_expect_helper, src/lib_expect_helper/test, src/lib_gossipsub, src/lib_gossipsub/test, src/lib_hacl, src/lib_hacl/test, src/lib_kzg, src/lib_lazy_containers, src/lib_lwt_result_stdlib, src/lib_lwt_result_stdlib/bare/functor_outputs, src/lib_lwt_result_stdlib/bare/sigs, src/lib_lwt_result_stdlib/bare/structs, src/lib_lwt_result_stdlib/examples/traces, src/lib_lwt_result_stdlib/test, src/lib_lwt_result_stdlib/traced/functor_outputs, src/lib_lwt_result_stdlib/traced/sigs, src/lib_lwt_result_stdlib/traced/structs, src/lib_mec, src/lib_mec/test, src/lib_micheline, src/lib_micheline/test, src/lib_p2p_services, src/lib_plompiler, src/lib_plonk, src/lib_plonk/test, src/lib_plonk/test_plompiler, src/lib_polynomial, src/lib_polynomial/test, src/lib_ppx_profiler, src/lib_profiler, src/lib_profiler/backends, src/lib_profiler/backends/complex, src/lib_profiler/backends/test, src/lib_profiler/unix, src/lib_protocol_environment/ppinclude, src/lib_rpc, src/lib_rpc_http, src/lib_rpc_http/test, src/lib_sapling, src/lib_sapling/bindings, src/lib_sapling/test, src/lib_scoru_wasm, src/lib_srs_extraction, src/lib_srs_extraction/test, src/lib_stdlib, src/lib_stdlib/test, src/lib_stdlib/test-unix, src/lib_stdlib_unix, src/lib_stdlib_unix/test/, src/lib_telemetry, src/lib_test, src/lib_tree_encoding, src/lib_version, src/lib_version/parser, src/lib_version/test, src/lib_webassembly, src/lib_workers, src/lib_workers/test, tezt/lib_alcotezt_process, tezt/lib_qcheck, tezt/lib_wrapper octez-lwt-domain: lwt_domain +octez-ml-dsa: src/lib_ml_dsa +octez-ml-dsa-44-api: src/lib_ml_dsa/bindings +octez-ml-dsa-test: src/lib_ml_dsa/test octez-node: src/bin_node octez-node-config: src/lib_node_config octez-performance-metrics: src/lib_performance_metrics -- GitLab From 9c1a7a099f514d54affeca92e225836adc22ead9 Mon Sep 17 00:00:00 2001 From: Victor Dumitrescu Date: Tue, 16 Dec 2025 15:52:54 +0100 Subject: [PATCH 2/6] Rust_libcrux: Initial library and bindings --- .cargo/config.toml | 3 + src/rust_libcrux/.cargo/config.toml | 2 + src/rust_libcrux/Cargo.lock | 567 +++++++++++++++++++ src/rust_libcrux/Cargo.toml | 34 ++ src/rust_libcrux/api/.ocamlformat-ignore | 1 + src/rust_libcrux/api/Cargo.toml | 23 + src/rust_libcrux/api/build.rs | 14 + src/rust_libcrux/api/octez_ml_dsa_44_api.ml | 21 + src/rust_libcrux/api/octez_ml_dsa_44_api.mli | 21 + src/rust_libcrux/api/src/lib.rs | 267 +++++++++ src/rust_libcrux/build.sh | 31 + src/rust_libcrux/dune | 21 + 12 files changed, 1005 insertions(+) create mode 100644 src/rust_libcrux/.cargo/config.toml create mode 100644 src/rust_libcrux/Cargo.lock create mode 100644 src/rust_libcrux/Cargo.toml create mode 100644 src/rust_libcrux/api/.ocamlformat-ignore create mode 100644 src/rust_libcrux/api/Cargo.toml create mode 100644 src/rust_libcrux/api/build.rs create mode 100644 src/rust_libcrux/api/octez_ml_dsa_44_api.ml create mode 100644 src/rust_libcrux/api/octez_ml_dsa_44_api.mli create mode 100644 src/rust_libcrux/api/src/lib.rs create mode 100755 src/rust_libcrux/build.sh create mode 100644 src/rust_libcrux/dune diff --git a/.cargo/config.toml b/.cargo/config.toml index b082c8bf8c2b..0259bfb0c6e8 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -11,3 +11,6 @@ rustflags = [ "-C", "default-linker-libraries", ] + +[profile.release] +lto = "off" \ No newline at end of file diff --git a/src/rust_libcrux/.cargo/config.toml b/src/rust_libcrux/.cargo/config.toml new file mode 100644 index 000000000000..67ba795ec324 --- /dev/null +++ b/src/rust_libcrux/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.'cfg(target_os = "macos")'] +rustflags = ["-C", "link-args=-Wl,-undefined,dynamic_lookup"] diff --git a/src/rust_libcrux/Cargo.lock b/src/rust_libcrux/Cargo.lock new file mode 100644 index 000000000000..c6a6b422759c --- /dev/null +++ b/src/rust_libcrux/Cargo.lock @@ -0,0 +1,567 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "cc" +version = "1.2.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "core-models" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0940496e5c83c54f3b753d5317daec82e8edac71c33aaa1f666d76f518de2444" +dependencies = [ + "hax-lib", + "pastey", + "rand", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "hax-lib" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d9ba66d1739c68e0219b2b2238b5c4145f491ebf181b9c6ab561a19352ae86" +dependencies = [ + "hax-lib-macros", + "num-bigint", + "num-traits", +] + +[[package]] +name = "hax-lib-macros" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ba777a231a58d1bce1d68313fa6b6afcc7966adef23d60f45b8a2b9b688bf1" +dependencies = [ + "hax-lib-macros-types", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "hax-lib-macros-types" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "867e19177d7425140b417cd27c2e05320e727ee682e98368f88b7194e80ad515" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "uuid", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "libcrux-intrinsics" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9ee7ef66569dd7516454fe26de4e401c0c62073929803486b96744594b9632" +dependencies = [ + "core-models", + "hax-lib", +] + +[[package]] +name = "libcrux-macros" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffd6aa2dcd5be681662001b81d493f1569c6d49a32361f470b0c955465cd0338" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "libcrux-ml-dsa" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3adca5778a61aa1707ad612e52a02ecbcd7e0b2912e011392def331e6e8b2aa" +dependencies = [ + "core-models", + "hax-lib", + "libcrux-intrinsics", + "libcrux-macros", + "libcrux-platform", + "libcrux-sha3", +] + +[[package]] +name = "libcrux-platform" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db82d058aa76ea315a3b2092f69dfbd67ddb0e462038a206e1dcd73f058c0778" +dependencies = [ + "libc", +] + +[[package]] +name = "libcrux-secrets" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4dbbf6bc9f2bc0f20dc3bea3e5c99adff3bdccf6d2a40488963da69e2ec307" +dependencies = [ + "hax-lib", +] + +[[package]] +name = "libcrux-sha3" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2400bec764d1c75b8a496d5747cffe32f1fb864a12577f0aca2f55a92021c962" +dependencies = [ + "hax-lib", + "libcrux-intrinsics", + "libcrux-platform", + "libcrux-traits", +] + +[[package]] +name = "libcrux-traits" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9adfd58e79d860f6b9e40e35127bfae9e5bd3ade33201d1347459011a2add034" +dependencies = [ + "libcrux-secrets", + "rand", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "ocaml" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8395e6be68fbf7c3d011b0dd3699660d22b17e981596cbad42454ccff041549b" +dependencies = [ + "ocaml-boxroot-sys", + "ocaml-derive", + "ocaml-sys", +] + +[[package]] +name = "ocaml-boxroot-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3c2664a427c8046d334bf3a50fc466170a3dc53c65bc926a9be31a8e8debd1" +dependencies = [ + "cc", +] + +[[package]] +name = "ocaml-build" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51b05aa0083eaec54b22a3f2a3d49175e04b4fb77ca7abb5a85731736239c3" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "ocaml-derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3de4a0decff0fd3ee0928dfa15dac08651157f8f814e93b34fdf962190354035" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ocaml-sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14e0db66a4df18164b2318518bea3891b6fd05526253a71023267f8955dbaefa" +dependencies = [ + "cc", + "cty", +] + +[[package]] +name = "octez-ml-dsa-api" +version = "0.0.0" +dependencies = [ + "libcrux-ml-dsa", + "ocaml", + "ocaml-boxroot-sys", + "ocaml-build", + "ocaml-sys", + "zeroize", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "uuid" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +dependencies = [ + "getrandom", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" diff --git a/src/rust_libcrux/Cargo.toml b/src/rust_libcrux/Cargo.toml new file mode 100644 index 000000000000..e0ad3a2ad66b --- /dev/null +++ b/src/rust_libcrux/Cargo.toml @@ -0,0 +1,34 @@ +[workspace] +resolver = "3" +members = ["api"] + +[workspace.lints.clippy] +allow_attributes = "deny" +allow_attributes_without_reason = "deny" + +[workspace.lints.rustdoc] +broken_intra_doc_links = "deny" +private_intra_doc_links = "allow" + +[workspace.dependencies] +ocaml-build = "1.0.0" +zeroize = "1.8" + +[workspace.dependencies.libcrux-ml-dsa] +version = "0.0.4" +default-features = false +features = ["mldsa44"] + +[workspace.dependencies.ocaml] +version = "1.2.1" +default-features = false + +[workspace.dependencies.ocaml-sys] +# We don't care about the version. Ideally this gets pinned to what `ocaml` needs. +version = "*" +default-features = false + +[workspace.dependencies.ocaml-boxroot-sys] +# We don't care about the version. Ideally this gets pinned to what `ocaml` needs. +version = "*" +default-features = false diff --git a/src/rust_libcrux/api/.ocamlformat-ignore b/src/rust_libcrux/api/.ocamlformat-ignore new file mode 100644 index 000000000000..72e8ffc0db8a --- /dev/null +++ b/src/rust_libcrux/api/.ocamlformat-ignore @@ -0,0 +1 @@ +* diff --git a/src/rust_libcrux/api/Cargo.toml b/src/rust_libcrux/api/Cargo.toml new file mode 100644 index 000000000000..48dcf4ed2bb7 --- /dev/null +++ b/src/rust_libcrux/api/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "octez-ml-dsa-api" +version = "0.0.0" +edition = "2021" + +[lib] +crate-type = ["staticlib", "cdylib"] + +[lints] +workspace = true + +[dependencies] +libcrux-ml-dsa.workspace = true +ocaml.workspace = true +ocaml-sys.workspace = true +ocaml-boxroot-sys.workspace = true +zeroize.workspace = true + +[build-dependencies] +ocaml-build.workspace = true + +[features] +default = ["ocaml/default"] diff --git a/src/rust_libcrux/api/build.rs b/src/rust_libcrux/api/build.rs new file mode 100644 index 000000000000..322d8f26ec3c --- /dev/null +++ b/src/rust_libcrux/api/build.rs @@ -0,0 +1,14 @@ +use std::env; + +fn generate_ocaml_sigs() { + ocaml_build::Sigs::new("octez_ml_dsa_44_api.ml") + .generate() + .unwrap(); +} + +pub fn main() { + if env::var("INSIDE_DUNE").is_err() { + // Generate the OCaml signatures only when building outside the Dune rule. + generate_ocaml_sigs(); + } +} diff --git a/src/rust_libcrux/api/octez_ml_dsa_44_api.ml b/src/rust_libcrux/api/octez_ml_dsa_44_api.ml new file mode 100644 index 000000000000..45cd7a73c72c --- /dev/null +++ b/src/rust_libcrux/api/octez_ml_dsa_44_api.ml @@ -0,0 +1,21 @@ +(* Generated by ocaml-rs *) + +open! Bigarray + +(* file: lib.rs *) + +type key_pair +type signing_key +type verification_key +type signature +external octez_ml_dsa_44_generate_key_pair: bytes -> (key_pair, string) result = "octez_ml_dsa_44_generate_key_pair" +external octez_ml_dsa_44_key_pair_get_signing_key: key_pair -> signing_key = "octez_ml_dsa_44_key_pair_get_signing_key" +external octez_ml_dsa_44_key_pair_get_verification_key: key_pair -> verification_key = "octez_ml_dsa_44_key_pair_get_verification_key" +external octez_ml_dsa_44_sign: signing_key -> bytes -> bytes -> bytes -> (signature, string) result = "octez_ml_dsa_44_sign" +external octez_ml_dsa_44_verify: verification_key -> bytes -> bytes -> signature -> (unit, string) result = "octez_ml_dsa_44_verify" +external octez_ml_dsa_44_verification_key_to_bytes: verification_key -> bytes = "octez_ml_dsa_44_verification_key_to_bytes" +external octez_ml_dsa_44_verification_key_from_bytes: bytes -> (verification_key, string) result = "octez_ml_dsa_44_verification_key_from_bytes" +external octez_ml_dsa_44_signing_key_to_bytes: signing_key -> bytes = "octez_ml_dsa_44_signing_key_to_bytes" +external octez_ml_dsa_44_signing_key_from_bytes: bytes -> (signing_key, string) result = "octez_ml_dsa_44_signing_key_from_bytes" +external octez_ml_dsa_44_signature_to_bytes: signature -> bytes = "octez_ml_dsa_44_signature_to_bytes" +external octez_ml_dsa_44_signature_from_bytes: bytes -> (signature, string) result = "octez_ml_dsa_44_signature_from_bytes" diff --git a/src/rust_libcrux/api/octez_ml_dsa_44_api.mli b/src/rust_libcrux/api/octez_ml_dsa_44_api.mli new file mode 100644 index 000000000000..45cd7a73c72c --- /dev/null +++ b/src/rust_libcrux/api/octez_ml_dsa_44_api.mli @@ -0,0 +1,21 @@ +(* Generated by ocaml-rs *) + +open! Bigarray + +(* file: lib.rs *) + +type key_pair +type signing_key +type verification_key +type signature +external octez_ml_dsa_44_generate_key_pair: bytes -> (key_pair, string) result = "octez_ml_dsa_44_generate_key_pair" +external octez_ml_dsa_44_key_pair_get_signing_key: key_pair -> signing_key = "octez_ml_dsa_44_key_pair_get_signing_key" +external octez_ml_dsa_44_key_pair_get_verification_key: key_pair -> verification_key = "octez_ml_dsa_44_key_pair_get_verification_key" +external octez_ml_dsa_44_sign: signing_key -> bytes -> bytes -> bytes -> (signature, string) result = "octez_ml_dsa_44_sign" +external octez_ml_dsa_44_verify: verification_key -> bytes -> bytes -> signature -> (unit, string) result = "octez_ml_dsa_44_verify" +external octez_ml_dsa_44_verification_key_to_bytes: verification_key -> bytes = "octez_ml_dsa_44_verification_key_to_bytes" +external octez_ml_dsa_44_verification_key_from_bytes: bytes -> (verification_key, string) result = "octez_ml_dsa_44_verification_key_from_bytes" +external octez_ml_dsa_44_signing_key_to_bytes: signing_key -> bytes = "octez_ml_dsa_44_signing_key_to_bytes" +external octez_ml_dsa_44_signing_key_from_bytes: bytes -> (signing_key, string) result = "octez_ml_dsa_44_signing_key_from_bytes" +external octez_ml_dsa_44_signature_to_bytes: signature -> bytes = "octez_ml_dsa_44_signature_to_bytes" +external octez_ml_dsa_44_signature_from_bytes: bytes -> (signature, string) result = "octez_ml_dsa_44_signature_from_bytes" diff --git a/src/rust_libcrux/api/src/lib.rs b/src/rust_libcrux/api/src/lib.rs new file mode 100644 index 000000000000..fa9492f1d341 --- /dev/null +++ b/src/rust_libcrux/api/src/lib.rs @@ -0,0 +1,267 @@ +// SPDX-FileCopyrightText: 2025 Nomadic Labs +// +// SPDX-License-Identifier: MIT + +//! This module defines OCaml bindings for the ML-DSA-44 signature scheme from +//! the libcrux-ml-dsa crate and serves as a basis for building +//! the octez-ml-dsa-44 OCaml library. + +use ocaml::Pointer; +use zeroize::Zeroize; + +use libcrux_ml_dsa::ml_dsa_44::portable::{generate_key_pair, sign, verify}; +use libcrux_ml_dsa::ml_dsa_44::{ + MLDSA44KeyPair, MLDSA44Signature, MLDSA44SigningKey, MLDSA44VerificationKey, +}; + +/// Key generation randomness size +pub const KEY_GENERATION_RANDOMNESS_SIZE: usize = libcrux_ml_dsa::KEY_GENERATION_RANDOMNESS_SIZE; + +/// Signing randomness size +pub const SIGNING_RANDOMNESS_SIZE: usize = libcrux_ml_dsa::SIGNING_RANDOMNESS_SIZE; + +/// Maximum context size +pub const MAX_CONTEXT_SIZE: usize = 255; + +/// Verification key size for ML-DSA-44 +pub const VERIFICATION_KEY_SIZE: usize = MLDSA44VerificationKey::len(); + +/// Signing key size for ML-DSA-44 +pub const SIGNING_KEY_SIZE: usize = MLDSA44SigningKey::len(); + +/// Signature size for ML-DSA-44 +pub const SIGNATURE_SIZE: usize = MLDSA44Signature::len(); + +/// An ML-DSA-44 key pair containing both signing and verification keys +#[ocaml::sig] +pub struct KeyPair(MLDSA44KeyPair); + +impl Drop for KeyPair { + fn drop(&mut self) { + self.0.signing_key.as_ref_mut().zeroize() + } +} + +/// An ML-DSA-44 signing key +#[ocaml::sig] +pub struct SigningKey(MLDSA44SigningKey); + +impl Drop for SigningKey { + fn drop(&mut self) { + self.0.as_ref_mut().zeroize() + } +} + +/// An ML-DSA-44 verification key +#[ocaml::sig] +pub struct VerificationKey(MLDSA44VerificationKey); + +/// An ML-DSA-44 signature +#[ocaml::sig] +pub struct Signature(MLDSA44Signature); + +ocaml::custom!(KeyPair); +ocaml::custom!(SigningKey); +ocaml::custom!(VerificationKey); +ocaml::custom!(Signature); + +/// Generate a new ML-DSA-44 key pair +/// +/// Returns an error if the size of `randomness` is not [`KEY_GENERATION_RANDOMNESS_SIZE`]. +/// +/// # Security +/// +/// `randomness` must be cryptographically secure random bytes. +#[ocaml::func] +#[ocaml::sig("bytes -> (key_pair, string) result")] +pub fn octez_ml_dsa_44_generate_key_pair(randomness: &[u8]) -> Result, String> { + let randomness_array: [u8; KEY_GENERATION_RANDOMNESS_SIZE] = + randomness.try_into().map_err(|_| { + format!( + "Invalid key generation randomness size: expected {}, got {}", + KEY_GENERATION_RANDOMNESS_SIZE, + randomness.len() + ) + })?; + + let key_pair = generate_key_pair(randomness_array); + Ok(KeyPair(key_pair).into()) +} + +/// Extract the signing key from a key pair +#[ocaml::func] +#[ocaml::sig("key_pair -> signing_key")] +pub fn octez_ml_dsa_44_key_pair_get_signing_key(key_pair: Pointer) -> Pointer { + let signing_key = key_pair.as_ref().0.signing_key.clone(); + SigningKey(signing_key).into() +} + +/// Extract the verification key from a key pair +#[ocaml::func] +#[ocaml::sig("key_pair -> verification_key")] +pub fn octez_ml_dsa_44_key_pair_get_verification_key( + key_pair: Pointer, +) -> Pointer { + let verification_key = key_pair.as_ref().0.verification_key.clone(); + VerificationKey(verification_key).into() +} + +/// Sign a message using ML-DSA-44 +/// +/// Returns an error if: +/// - The size of `randomness` is not [`SIGNING_RANDOMNESS_SIZE`] +/// - The size of `context` exceeds [`MAX_CONTEXT_SIZE`] +/// - Signing fails +/// +/// # Security +/// +/// `randomness` must be cryptographically secure random bytes. +#[ocaml::func] +#[ocaml::sig("signing_key -> bytes -> bytes -> bytes -> (signature, string) result")] +pub fn octez_ml_dsa_44_sign( + signing_key: Pointer, + message: &[u8], + context: &[u8], + randomness: &[u8], +) -> Result, String> { + let randomness_array: [u8; SIGNING_RANDOMNESS_SIZE] = randomness.try_into().map_err(|_| { + format!( + "Invalid signing randomness size: expected {}, got {}", + SIGNING_RANDOMNESS_SIZE, + randomness.len() + ) + })?; + + if context.len() > MAX_CONTEXT_SIZE { + return Err(format!( + "Signing context too long: expected at most {} bytes, got {}", + MAX_CONTEXT_SIZE, + context.len() + )); + } + + match sign(&signing_key.as_ref().0, message, context, randomness_array) { + Ok(signature) => Ok(Signature(signature).into()), + Err(e) => Err(format!("Signing failed: {e:?}")), + } +} + +/// Verify an ML-DSA-44 signature +/// +/// Returns an error if: +/// - The size of `context` exceeds [`MAX_CONTEXT_SIZE`] +/// - Verification fails +#[ocaml::func] +#[ocaml::sig("verification_key -> bytes -> bytes -> signature -> (unit, string) result")] +pub fn octez_ml_dsa_44_verify( + verification_key: Pointer, + message: &[u8], + context: &[u8], + signature: Pointer, +) -> Result<(), String> { + if context.len() > MAX_CONTEXT_SIZE { + return Err(format!( + "Context too long: expected at most {} bytes, got {}", + MAX_CONTEXT_SIZE, + context.len() + )); + } + + verify( + &verification_key.as_ref().0, + message, + context, + &signature.as_ref().0, + ) + .map_err(|e| format!("Verification failed: {e:?}")) +} + +/// Return a new byte buffer of size [`VERIFICATION_KEY_SIZE`] containing +/// the verification key bytes. +#[ocaml::func] +#[ocaml::sig("verification_key -> bytes")] +pub fn octez_ml_dsa_44_verification_key_to_bytes( + verification_key: Pointer, +) -> [u8; VERIFICATION_KEY_SIZE] { + *verification_key.as_ref().0.as_ref() +} + +/// Create a new verification key from bytes. +/// +/// Returns an error if the size of `bytes` is not [`VERIFICATION_KEY_SIZE`]. +#[ocaml::func] +#[ocaml::sig("bytes -> (verification_key, string) result")] +pub fn octez_ml_dsa_44_verification_key_from_bytes( + bytes: &[u8], +) -> Result, String> { + let verification_key_bytes: [u8; VERIFICATION_KEY_SIZE] = bytes.try_into().map_err(|_| { + format!( + "Invalid verification key size: expected {}, got {}", + VERIFICATION_KEY_SIZE, + bytes.len() + ) + })?; + + Ok(VerificationKey(MLDSA44VerificationKey::new(verification_key_bytes)).into()) +} + +/// Return a new byte buffer of size [`SIGNING_KEY_SIZE`] containing +/// the signing key bytes. +/// +/// # Security +/// +/// This function exposes sensitive key material. Callers are responsible for +/// securely handling the returned bytes. +#[ocaml::func] +#[ocaml::sig("signing_key -> bytes")] +pub fn octez_ml_dsa_44_signing_key_to_bytes( + signing_key: Pointer, +) -> [u8; SIGNING_KEY_SIZE] { + *signing_key.as_ref().0.as_ref() +} + +/// Create a new signing key from bytes. +/// +/// Returns an error if the size of `bytes` is not [`SIGNING_KEY_SIZE`]. +#[ocaml::func] +#[ocaml::sig("bytes -> (signing_key, string) result")] +pub fn octez_ml_dsa_44_signing_key_from_bytes(bytes: &[u8]) -> Result, String> { + let mut signing_key_bytes: [u8; SIGNING_KEY_SIZE] = bytes.try_into().map_err(|_| { + format!( + "Invalid signing key size: expected {}, got {}", + SIGNING_KEY_SIZE, + bytes.len() + ) + })?; + + let signing_key = SigningKey(MLDSA44SigningKey::new(signing_key_bytes)).into(); + + signing_key_bytes.zeroize(); + + Ok(signing_key) +} + +/// Return a new byte buffer of size [`SIGNATURE_SIZE`] containing +/// the signature bytes. +#[ocaml::func] +#[ocaml::sig("signature -> bytes")] +pub fn octez_ml_dsa_44_signature_to_bytes(signature: Pointer) -> [u8; SIGNATURE_SIZE] { + *signature.as_ref().0.as_ref() +} + +/// Create a new signature from bytes. +/// +/// Returns an error if the size of `bytes` is not [`SIGNATURE_SIZE`]. +#[ocaml::func] +#[ocaml::sig("bytes -> (signature, string) result")] +pub fn octez_ml_dsa_44_signature_from_bytes(bytes: &[u8]) -> Result, String> { + let signature_bytes: [u8; SIGNATURE_SIZE] = bytes.try_into().map_err(|_| { + format!( + "Invalid signature size: expected {}, got {}", + SIGNATURE_SIZE, + bytes.len() + ) + })?; + + Ok(Signature(MLDSA44Signature::new(signature_bytes)).into()) +} diff --git a/src/rust_libcrux/build.sh b/src/rust_libcrux/build.sh new file mode 100755 index 000000000000..250b869d6987 --- /dev/null +++ b/src/rust_libcrux/build.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: 2024 TriliTech +# +# SPDX-License-Identifier: MIT + +# This is a build script to be used in the Dune rule to make writing the rule less difficult, +# similar to the one used to build lib_wasm_runtime. + +# Ensure that failures bubble up. +set -e + +OUT_DIR=$(dirname "$0") +CARGO_TARGET_DIR="${OCTEZ_LIBCRUX_TARGET_DIR:-$OUT_DIR/target}" + +# Make sure the subsequent processes have access to these. +export CARGO_TARGET_DIR + +# Build the Rust project. +cargo build --release --locked --target-dir="$CARGO_TARGET_DIR" + +# Copy the built artifacts to the output directory. +cp -f "$CARGO_TARGET_DIR/release/liboctez_ml_dsa_api.a" "$OUT_DIR/liboctez_libcrux_ml_dsa.a" + +if [[ -r "$CARGO_TARGET_DIR/release/liboctez_ml_dsa_api.so" ]]; then + cp -f "$CARGO_TARGET_DIR/release/liboctez_ml_dsa_api.so" "$OUT_DIR/dlloctez_libcrux_ml_dsa.so" +fi + +if [[ -r "$CARGO_TARGET_DIR/release/liboctez_ml_dsa_api.dylib" ]]; then + cp -f "$CARGO_TARGET_DIR/release/liboctez_ml_dsa_api.dylib" "$OUT_DIR/dlloctez_libcrux_ml_dsa.so" +fi diff --git a/src/rust_libcrux/dune b/src/rust_libcrux/dune new file mode 100644 index 000000000000..45968abf3fd7 --- /dev/null +++ b/src/rust_libcrux/dune @@ -0,0 +1,21 @@ +; This file was automatically generated, do not edit. +; Edit file manifest/main.ml instead. + +(library + (name octez_libcrux_ml_dsa) + (public_name octez-libcrux-ml-dsa) + (instrumentation (backend bisect_ppx)) + (foreign_archives octez_libcrux_ml_dsa)) + +(dirs :standard .cargo (not target)) + +(rule + (targets liboctez_libcrux_ml_dsa.a dlloctez_libcrux_ml_dsa.so) + (deps + (file build.sh) + (file Cargo.toml) + (file Cargo.lock) + (file ../../rust-toolchain) + (source_tree .cargo) + (source_tree api)) + (action (no-infer (bash ./build.sh)))) -- GitLab From 9f95ca05782713e2ffb792cfbbf22e78cc8df856 Mon Sep 17 00:00:00 2001 From: Victor Dumitrescu Date: Tue, 16 Dec 2025 15:58:28 +0100 Subject: [PATCH 3/6] Lib_ml_dsa: Initial library and bindings tests --- src/lib_ml_dsa/bindings/dune | 15 +++ src/lib_ml_dsa/dune | 9 ++ src/lib_ml_dsa/octez_ml_dsa.ml | 8 ++ src/lib_ml_dsa/test/dune | 40 ++++++ src/lib_ml_dsa/test/test_bindings.ml | 177 +++++++++++++++++++++++++++ src/lib_ml_dsa/test/test_main.ml | 21 ++++ 6 files changed, 270 insertions(+) create mode 100644 src/lib_ml_dsa/bindings/dune create mode 100644 src/lib_ml_dsa/dune create mode 100644 src/lib_ml_dsa/octez_ml_dsa.ml create mode 100644 src/lib_ml_dsa/test/dune create mode 100644 src/lib_ml_dsa/test/test_bindings.ml create mode 100644 src/lib_ml_dsa/test/test_main.ml diff --git a/src/lib_ml_dsa/bindings/dune b/src/lib_ml_dsa/bindings/dune new file mode 100644 index 000000000000..aeaff1a9a872 --- /dev/null +++ b/src/lib_ml_dsa/bindings/dune @@ -0,0 +1,15 @@ +; This file was automatically generated, do not edit. +; Edit file manifest/main.ml instead. + +(library + (name octez_ml_dsa_44_api) + (public_name octez-ml-dsa-44-api) + (instrumentation (backend bisect_ppx)) + (libraries + octez-libcrux-ml-dsa) + (flags + (:standard) + -w -9-27-66) + (modules octez_ml_dsa_44_api)) + +(copy_files ../../rust_libcrux/api/octez_ml_dsa_44_api.*) diff --git a/src/lib_ml_dsa/dune b/src/lib_ml_dsa/dune new file mode 100644 index 000000000000..96495c561821 --- /dev/null +++ b/src/lib_ml_dsa/dune @@ -0,0 +1,9 @@ +; This file was automatically generated, do not edit. +; Edit file manifest/main.ml instead. + +(library + (name octez_ml_dsa) + (public_name octez-ml-dsa) + (instrumentation (backend bisect_ppx)) + (libraries + octez-ml-dsa-44-api)) diff --git a/src/lib_ml_dsa/octez_ml_dsa.ml b/src/lib_ml_dsa/octez_ml_dsa.ml new file mode 100644 index 000000000000..06029bbf40d9 --- /dev/null +++ b/src/lib_ml_dsa/octez_ml_dsa.ml @@ -0,0 +1,8 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* SPDX-FileCopyrightText: 2025 Nomadic Labs *) +(* *) +(*****************************************************************************) + +include Octez_ml_dsa_44_api diff --git a/src/lib_ml_dsa/test/dune b/src/lib_ml_dsa/test/dune new file mode 100644 index 000000000000..bced5f6eea8e --- /dev/null +++ b/src/lib_ml_dsa/test/dune @@ -0,0 +1,40 @@ +; This file was automatically generated, do not edit. +; Edit file manifest/main.ml instead. + +(library + (name src_lib_ml_dsa_test_tezt_lib) + (instrumentation (backend bisect_ppx)) + (libraries + tezt.core + bls12-381.archive + octez-libs.base + octez-libs.stdlib-unix + octez-alcotezt + octez-ml-dsa) + (library_flags (:standard -linkall)) + (flags + (:standard) + -open Tezt_core + -open Tezt_core.Base + -open Tezos_base.TzPervasives + -open Tezos_stdlib_unix + -open Octez_alcotezt) + (modules test_main test_bindings)) + +(executable + (name main) + (instrumentation (backend bisect_ppx --bisect-sigterm)) + (libraries + src_lib_ml_dsa_test_tezt_lib + tezt) + (modules main)) + +(rule + (alias runtest) + (package octez-ml-dsa-test) + (enabled_if (<> false %{env:RUNTEZTALIAS=true})) + (action (run %{dep:./main.exe} /flaky /ci_disabled))) + +(rule + (targets main.ml) + (action (with-stdout-to %{targets} (echo "let () = Tezt.Test.run ()")))) diff --git a/src/lib_ml_dsa/test/test_bindings.ml b/src/lib_ml_dsa/test/test_bindings.ml new file mode 100644 index 000000000000..a0932766a4c1 --- /dev/null +++ b/src/lib_ml_dsa/test/test_bindings.ml @@ -0,0 +1,177 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* SPDX-FileCopyrightText: 2025 Nomadic Labs *) +(* *) +(*****************************************************************************) + +module Bindings = Octez_ml_dsa + +let ml_dsa_44_signing_key_size = 2560 + +let ml_dsa_44_verification_key_size = 1312 + +let ml_dsa_44_signature_size = 2420 + +let key_generation_randomness_size = 32 + +let signing_randomness_size = 32 + +let generate_random_bytes size = + Bytes.init size (fun _ -> char_of_int (Random.int 256)) + +let test_generate_key_pair () = + let seed = generate_random_bytes key_generation_randomness_size in + + let key_pair = + match Bindings.octez_ml_dsa_44_generate_key_pair seed with + | Ok key_pair -> key_pair + | Error err -> Alcotest.failf "Key pair generation failed: %s" err + in + + (* Extract signing and verification keys from key pair *) + let signing_key = + Bindings.octez_ml_dsa_44_key_pair_get_signing_key key_pair + in + let verification_key = + Bindings.octez_ml_dsa_44_key_pair_get_verification_key key_pair + in + + (* Convert keys to bytes and verify sizes *) + let signing_key_bytes = + Bindings.octez_ml_dsa_44_signing_key_to_bytes signing_key + in + let verification_key_bytes = + Bindings.octez_ml_dsa_44_verification_key_to_bytes verification_key + in + Alcotest.(check int) + "signing key size" + ml_dsa_44_signing_key_size + (Bytes.length signing_key_bytes) ; + Alcotest.(check int) + "verification key size" + ml_dsa_44_verification_key_size + (Bytes.length verification_key_bytes) ; + + (* Initialise keys from bytes *) + match + ( Bindings.octez_ml_dsa_44_signing_key_from_bytes signing_key_bytes, + Bindings.octez_ml_dsa_44_verification_key_from_bytes + verification_key_bytes ) + with + | Ok _signing_key, Ok _verification_key -> Lwt.return_unit + | Error err, _ -> + Alcotest.failf "Initialising signing key from bytes failed: %s" err + | _, Error err -> + Alcotest.failf "Initialising verification key from bytes failed: %s" err + +let test_sign () = + let seed = generate_random_bytes key_generation_randomness_size in + + let key_pair = + match Bindings.octez_ml_dsa_44_generate_key_pair seed with + | Ok key_pair -> key_pair + | Error err -> Alcotest.failf "Key pair generation failed: %s" err + in + + let signing_key = + Bindings.octez_ml_dsa_44_key_pair_get_signing_key key_pair + in + + let message = generate_random_bytes 16 in + let randomness = generate_random_bytes signing_randomness_size in + + match + Bindings.octez_ml_dsa_44_sign signing_key message Bytes.empty randomness + with + | Ok signature -> ( + let signature_bytes = + Bindings.octez_ml_dsa_44_signature_to_bytes signature + in + Alcotest.(check int) + "signature size" + ml_dsa_44_signature_size + (Bytes.length signature_bytes) ; + + match Bindings.octez_ml_dsa_44_signature_from_bytes signature_bytes with + | Ok _signature -> Lwt.return_unit + | Error err -> + Alcotest.failf "Initialising signature from bytes failed: %s" err) + | Error err -> Alcotest.failf "Signing failed: %s" err + +let test_verify () = + let seed = generate_random_bytes key_generation_randomness_size in + + let key_pair = + match Bindings.octez_ml_dsa_44_generate_key_pair seed with + | Ok key_pair -> key_pair + | Error err -> Alcotest.failf "Key pair generation failed: %s" err + in + + let signing_key = + Bindings.octez_ml_dsa_44_key_pair_get_signing_key key_pair + in + let verification_key = + Bindings.octez_ml_dsa_44_key_pair_get_verification_key key_pair + in + + let message = generate_random_bytes 16 in + let randomness = generate_random_bytes signing_randomness_size in + let context = Bytes.empty in + + match + Bindings.octez_ml_dsa_44_sign signing_key message context randomness + with + | Ok signature -> ( + match + Bindings.octez_ml_dsa_44_verify + verification_key + message + context + signature + with + | Ok () -> Lwt.return_unit + | Error err -> Alcotest.failf "Verification failed: %s" err) + | Error err -> Alcotest.failf "Signing failed: %s" err + +let test_verify_invalid_signature () = + let seed = generate_random_bytes key_generation_randomness_size in + + let key_pair = + match Bindings.octez_ml_dsa_44_generate_key_pair seed with + | Ok key_pair -> key_pair + | Error err -> Alcotest.failf "Key pair generation failed: %s" err + in + + let signing_key = + Bindings.octez_ml_dsa_44_key_pair_get_signing_key key_pair + in + let verification_key = + Bindings.octez_ml_dsa_44_key_pair_get_verification_key key_pair + in + + (* Sign two different messages *) + let message1 = generate_random_bytes 16 in + let message2 = generate_random_bytes 16 in + let randomness1 = generate_random_bytes signing_randomness_size in + let randomness2 = generate_random_bytes signing_randomness_size in + let context = Bytes.empty in + + match + ( Bindings.octez_ml_dsa_44_sign signing_key message1 context randomness1, + Bindings.octez_ml_dsa_44_sign signing_key message2 context randomness2 ) + with + | Ok _signature1, Ok signature2 -> ( + (* Try to verify message1 with signature2 *) + match + Bindings.octez_ml_dsa_44_verify + verification_key + message1 + context + signature2 + with + | Ok () -> + Alcotest.failf "Verification should have failed with wrong signature" + | Error _ -> Lwt.return_unit) + | Error err, _ -> Alcotest.failf "Signing message1 failed: %s" err + | _, Error err -> Alcotest.failf "Signing message2 failed: %s" err diff --git a/src/lib_ml_dsa/test/test_main.ml b/src/lib_ml_dsa/test/test_main.ml new file mode 100644 index 000000000000..5a6554c496c3 --- /dev/null +++ b/src/lib_ml_dsa/test/test_main.ml @@ -0,0 +1,21 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* SPDX-FileCopyrightText: 2025 Nomadic Labs *) +(* *) +(*****************************************************************************) + +let tests = + [ + ( "Bindings", + [ + ("Generate key pair", `Quick, Test_bindings.test_generate_key_pair); + ("Sign message", `Quick, Test_bindings.test_sign); + ("Verify signature", `Quick, Test_bindings.test_verify); + ( "Verify invalid signature", + `Quick, + Test_bindings.test_verify_invalid_signature ); + ] ); + ] + +let () = Alcotest_lwt.run ~__FILE__ "ML-DSA-44" tests |> Lwt_main.run -- GitLab From 60a984fc8ca2da5b44aea2c002e30b3201cad8ab Mon Sep 17 00:00:00 2001 From: Bruno Bernardo Date: Thu, 18 Dec 2025 14:06:39 +0100 Subject: [PATCH 4/6] CI/Cache: enable cargo for [oc.unit:webassembly-x86_64] and [oc.script:test-gen-genesis] Co-authored-by: Victor Dumitrescu To enable downloading of crates: cf. https://gitlab.com/tezos/tezos/-/jobs/12482039165 --- .gitlab/ci/pipelines/before_merging.yml | 14 ++++++++++++++ .gitlab/ci/pipelines/merge_train.yml | 14 ++++++++++++++ .gitlab/ci/pipelines/schedule_extended_test.yml | 14 ++++++++++++++ ci/lib_tezos_ci_jobs/misc.ml | 2 ++ 4 files changed, 44 insertions(+) diff --git a/.gitlab/ci/pipelines/before_merging.yml b/.gitlab/ci/pipelines/before_merging.yml index a719b0e5fff2..a76738ddb43a 100644 --- a/.gitlab/ci/pipelines/before_merging.yml +++ b/.gitlab/ci/pipelines/before_merging.yml @@ -2023,6 +2023,11 @@ oc.script:test-gen-genesis: dependencies: - oc.docker:ci:amd64 timeout: 60 minutes + cache: + key: cargo-$CI_JOB_NAME_SLUG + paths: + - $CI_PROJECT_DIR/.cargo/registry/cache + policy: pull-push interruptible: true before_script: - SCRIPT_STEP_BEGIN=$(date +%s) @@ -2034,6 +2039,8 @@ oc.script:test-gen-genesis: - . ./scripts/ci/datadog_send_job_script_step_time.sh || true after_script: - . ./scripts/ci/datadog_send_job_cache_info.sh 'after' + variables: + CARGO_NET_OFFLINE: "false" oc.script:snapshot_alpha_and_link: image: ${ci_image_name}/build:${ci_image_tag} @@ -2320,6 +2327,11 @@ oc.unit:webassembly-x86_64: dependencies: - oc.docker:ci:amd64 timeout: 20 minutes + cache: + key: cargo-$CI_JOB_NAME_SLUG + paths: + - $CI_PROJECT_DIR/.cargo/registry/cache + policy: pull-push interruptible: true before_script: - SCRIPT_STEP_BEGIN=$(date +%s) @@ -2332,6 +2344,8 @@ oc.unit:webassembly-x86_64: - . ./scripts/ci/datadog_send_job_script_step_time.sh || true after_script: - . ./scripts/ci/datadog_send_job_cache_info.sh 'after' + variables: + CARGO_NET_OFFLINE: "false" oc.unit:non-proto-x86_64: image: ${ci_image_name}/test:${ci_image_tag} diff --git a/.gitlab/ci/pipelines/merge_train.yml b/.gitlab/ci/pipelines/merge_train.yml index 65489360a969..544e4bdc3c42 100644 --- a/.gitlab/ci/pipelines/merge_train.yml +++ b/.gitlab/ci/pipelines/merge_train.yml @@ -1938,6 +1938,11 @@ oc.script:test-gen-genesis: dependencies: - oc.docker:ci:amd64 timeout: 60 minutes + cache: + key: cargo-$CI_JOB_NAME_SLUG + paths: + - $CI_PROJECT_DIR/.cargo/registry/cache + policy: pull-push interruptible: true before_script: - SCRIPT_STEP_BEGIN=$(date +%s) @@ -1949,6 +1954,8 @@ oc.script:test-gen-genesis: - . ./scripts/ci/datadog_send_job_script_step_time.sh || true after_script: - . ./scripts/ci/datadog_send_job_cache_info.sh 'after' + variables: + CARGO_NET_OFFLINE: "false" oc.script:snapshot_alpha_and_link: image: ${ci_image_name}/build:${ci_image_tag} @@ -2228,6 +2235,11 @@ oc.unit:webassembly-x86_64: dependencies: - oc.docker:ci:amd64 timeout: 20 minutes + cache: + key: cargo-$CI_JOB_NAME_SLUG + paths: + - $CI_PROJECT_DIR/.cargo/registry/cache + policy: pull-push interruptible: true before_script: - SCRIPT_STEP_BEGIN=$(date +%s) @@ -2240,6 +2252,8 @@ oc.unit:webassembly-x86_64: - . ./scripts/ci/datadog_send_job_script_step_time.sh || true after_script: - . ./scripts/ci/datadog_send_job_cache_info.sh 'after' + variables: + CARGO_NET_OFFLINE: "false" oc.unit:non-proto-x86_64: image: ${ci_image_name}/test:${ci_image_tag} diff --git a/.gitlab/ci/pipelines/schedule_extended_test.yml b/.gitlab/ci/pipelines/schedule_extended_test.yml index b06ddccf5583..ef5bdd0a7992 100644 --- a/.gitlab/ci/pipelines/schedule_extended_test.yml +++ b/.gitlab/ci/pipelines/schedule_extended_test.yml @@ -1266,6 +1266,11 @@ oc.script:test-gen-genesis: dependencies: - oc.docker:ci:amd64 timeout: 60 minutes + cache: + key: cargo-$CI_JOB_NAME_SLUG + paths: + - $CI_PROJECT_DIR/.cargo/registry/cache + policy: pull-push interruptible: false before_script: - SCRIPT_STEP_BEGIN=$(date +%s) @@ -1277,6 +1282,8 @@ oc.script:test-gen-genesis: - . ./scripts/ci/datadog_send_job_script_step_time.sh || true after_script: - . ./scripts/ci/datadog_send_job_cache_info.sh 'after' + variables: + CARGO_NET_OFFLINE: "false" oc.script:snapshot_alpha_and_link: image: ${ci_image_name}/build:${ci_image_tag} @@ -1473,6 +1480,11 @@ oc.unit:webassembly-x86_64: dependencies: - oc.docker:ci:amd64 timeout: 20 minutes + cache: + key: cargo-$CI_JOB_NAME_SLUG + paths: + - $CI_PROJECT_DIR/.cargo/registry/cache + policy: pull-push interruptible: false before_script: - SCRIPT_STEP_BEGIN=$(date +%s) @@ -1485,6 +1497,8 @@ oc.unit:webassembly-x86_64: - . ./scripts/ci/datadog_send_job_script_step_time.sh || true after_script: - . ./scripts/ci/datadog_send_job_cache_info.sh 'after' + variables: + CARGO_NET_OFFLINE: "false" oc.unit:non-proto-x86_64: image: ${ci_image_name}/test:${ci_image_tag} diff --git a/ci/lib_tezos_ci_jobs/misc.ml b/ci/lib_tezos_ci_jobs/misc.ml index b111da0c500c..ebe00fe8601f 100644 --- a/ci/lib_tezos_ci_jobs/misc.ml +++ b/ci/lib_tezos_ci_jobs/misc.ml @@ -78,6 +78,7 @@ let job_script_test_gen_genesis = ~stage:Test ~description:"Check that scripts/gen-genesis/gen_genesis.exe still builds." ~image:Tezos_ci.Images.CI.build + ~cargo_cache:true ~only_if_changed:(Changesets.changeset_octez |> Tezos_ci.Changeset.encode) ["eval $(opam env)"; "dune build scripts/gen-genesis/gen_genesis.exe"] @@ -255,6 +256,7 @@ let job_oc_unit_webassembly_x86_64 = ~description:"Run the tests for WASM." ~arch:Amd64 (* The wasm tests are written in Python *) ~image:Tezos_ci.Images.CI.test + ~cargo_cache:true ~stage:Test ~only_if_changed:(Tezos_ci.Changeset.encode Changesets.changeset_octez) ~timeout:(Minutes 20) -- GitLab From e829ada74f7d0e18905f938b349f6e817093ec4c Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Mon, 15 Dec 2025 10:47:26 +0100 Subject: [PATCH 5/6] lib_crypto: introduce ML-DSA-44 module --- src/lib_crypto/base58.ml | 14 ++ src/lib_crypto/base58.mli | 10 + src/lib_crypto/mldsa44.ml | 369 +++++++++++++++++++++++++++++++++++++ src/lib_crypto/mldsa44.mli | 12 ++ 4 files changed, 405 insertions(+) create mode 100644 src/lib_crypto/mldsa44.ml create mode 100644 src/lib_crypto/mldsa44.mli diff --git a/src/lib_crypto/base58.ml b/src/lib_crypto/base58.ml index 0c8d96f57008..7facd9e3694e 100644 --- a/src/lib_crypto/base58.ml +++ b/src/lib_crypto/base58.ml @@ -384,6 +384,8 @@ module Prefix = struct let bls12_381_public_key_hash = "\006\161\166" (* tz4(36) *) + let mldsa44_public_key_hash = "\006\161\169" (* tz5(36) *) + let smart_rollup_address = "\006\124\117" (* sr1(36) *) (* 16 *) @@ -462,4 +464,16 @@ module Prefix = struct (* 48 *) let slot_header = "\002\116\180" (* sh(74) *) + + (* 1312 *) + let mldsa44_public_key = "\013\007\237\067" (* mdpk(1802) *) + + (* 3872 *) + let mldsa44_secret_key = "\009\057\116\057" (* mdsk(5298) *) + + (* 3896 *) + let mldsa44_encrypted_seed = "\005\049\133\039\172" (* mdesk(5332) *) + + (* 2420 *) + let mldsa44_signature = "\001\156\045\210\003" (* mdsig(3316) *) end diff --git a/src/lib_crypto/base58.mli b/src/lib_crypto/base58.mli index 7e9c3a12d8bf..3c66cd3a4112 100644 --- a/src/lib_crypto/base58.mli +++ b/src/lib_crypto/base58.mli @@ -55,6 +55,8 @@ module Prefix : sig val bls12_381_public_key_hash : string + val mldsa44_public_key_hash : string + val smart_rollup_address : string val smart_rollup_commitment : string @@ -117,6 +119,14 @@ module Prefix : sig val bls12_381_secret_key : string + val mldsa44_secret_key : string + + val mldsa44_public_key : string + + val mldsa44_encrypted_seed : string + + val mldsa44_signature : string + val slot_header : string end diff --git a/src/lib_crypto/mldsa44.ml b/src/lib_crypto/mldsa44.ml new file mode 100644 index 000000000000..17022902c1b8 --- /dev/null +++ b/src/lib_crypto/mldsa44.ml @@ -0,0 +1,369 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* SPDX-FileCopyrightText: 2025 Nomadic Labs *) +(* *) +(*****************************************************************************) + +open Error_monad + +module Public_key_hash = struct + include + Blake2B.Make + (Base58) + (struct + let name = "MLDSA44.Public_key_hash" + + let title = "An ML-DSA-44 public key hash" + + let b58check_prefix = Base58.Prefix.mldsa44_public_key_hash + + let size = Some 20 + end) + + module Logging = struct + let tag = Tag.def ~doc:title name pp + end +end + +let () = Base58.check_encoded_prefix Public_key_hash.b58check_encoding "tz5" 36 + +module Public_key = struct + type t = Octez_ml_dsa.verification_key + + let name = "MLDSA44.Public_key" + + let title = "MLDSA44 public key" + + let to_bytes = Octez_ml_dsa.octez_ml_dsa_44_verification_key_to_bytes + + let to_string s = Bytes.to_string (to_bytes s) + + let of_bytes_opt bytes = + Result.to_option + @@ Octez_ml_dsa.octez_ml_dsa_44_verification_key_from_bytes bytes + + let of_string_opt s = of_bytes_opt (Bytes.of_string s) + + let of_bytes_without_validation = of_bytes_opt + + let size _ = 1312 + + type Base58.data += Data of t + + let b58check_encoding = + Base58.register_encoding + ~prefix:Base58.Prefix.mldsa44_public_key + ~length:(size ()) + ~to_raw:to_string + ~of_raw:of_string_opt + ~wrap:(fun x -> Data x) + + let () = Base58.check_encoded_prefix b58check_encoding "mdpk" 1802 + + let hash v = Public_key_hash.hash_bytes [to_bytes v] + + include Compare.Make (struct + type nonrec t = t + + let compare = compare + end) + + include Helpers.MakeRaw (struct + type nonrec t = t + + let name = name + + let of_bytes_opt = of_bytes_opt + + let of_string_opt = of_string_opt + + let to_string = to_string + end) + + include Helpers.MakeB58 (struct + type nonrec t = t + + let name = name + + let b58check_encoding = b58check_encoding + end) + + include Helpers.MakeEncoder (struct + type nonrec t = t + + let name = name + + let title = title + + let raw_encoding = + let open Data_encoding in + conv to_bytes of_bytes_exn (Fixed.bytes (size ())) + + let of_b58check = of_b58check + + let of_b58check_opt = of_b58check_opt + + let of_b58check_exn = of_b58check_exn + + let to_b58check = to_b58check + + let to_short_b58check = to_short_b58check + end) + + let pp ppf t = Format.fprintf ppf "%s" (to_b58check t) +end + +module Secret_key = struct + type t = Octez_ml_dsa.signing_key * Octez_ml_dsa.verification_key + + let name = "MLDSA44.Secret_key" + + let title = "An ML-DSA-44 secret key" + + let signing_key_size = 2560 + + let verification_key_size = 1312 + + let size = signing_key_size + verification_key_size + + let to_bytes (signing_key, verification_key) = + let signing_key_bytes = + Octez_ml_dsa.octez_ml_dsa_44_signing_key_to_bytes signing_key + in + let verification_key_bytes = + Octez_ml_dsa.octez_ml_dsa_44_verification_key_to_bytes verification_key + in + Bytes.cat signing_key_bytes verification_key_bytes + + let to_string s = Bytes.to_string (to_bytes s) + + let of_bytes_opt bytes = + let open Option_syntax in + let* signing_key = + Result.to_option + @@ Octez_ml_dsa.octez_ml_dsa_44_signing_key_from_bytes + (Bytes.sub bytes 0 signing_key_size) + in + let* verification_key = + Result.to_option + @@ Octez_ml_dsa.octez_ml_dsa_44_verification_key_from_bytes + (Bytes.sub bytes signing_key_size verification_key_size) + in + Some (signing_key, verification_key) + + let of_string_opt s = of_bytes_opt (Bytes.of_string s) + + let to_public_key (_signing_key, verification_key) = verification_key + + type Base58.data += Data of t + + let b58check_encoding = + Base58.register_encoding + ~prefix:Base58.Prefix.mldsa44_secret_key + ~length:size + ~to_raw:to_string + ~of_raw:of_string_opt + ~wrap:(fun sk -> Data sk) + + let of_b58check_opt s = + match Base58.simple_decode b58check_encoding s with + | Some x -> Some x + | None -> None + + let of_b58check_exn s = + match of_b58check_opt s with + | Some x -> x + | None -> Format.kasprintf Stdlib.failwith "Unexpected data (%s)" name + + let of_b58check s = + match of_b58check_opt s with + | Some x -> Ok x + | None -> + error_with "Failed to read a b58check_encoding data (%s): %S" name s + + let to_b58check s = Base58.simple_encode b58check_encoding s + + let to_short_b58check s = + String.sub + (to_b58check s) + 0 + (10 + String.length (Base58.prefix b58check_encoding)) + + let () = Base58.check_encoded_prefix b58check_encoding "mdsk" 5298 + + include Compare.Make (struct + type nonrec t = t + + let compare = compare + end) + + include Helpers.MakeRaw (struct + type nonrec t = t + + let name = name + + let of_bytes_opt = of_bytes_opt + + let of_string_opt = of_string_opt + + let to_string = to_string + end) + + include Helpers.MakeEncoder (struct + type nonrec t = t + + let name = name + + let title = title + + let raw_encoding = + let open Data_encoding in + conv to_bytes of_bytes_exn (Fixed.bytes size) + + let of_b58check = of_b58check + + let of_b58check_opt = of_b58check_opt + + let of_b58check_exn = of_b58check_exn + + let to_b58check = to_b58check + + let to_short_b58check = to_short_b58check + end) + + let pp ppf t = Format.fprintf ppf "%s" (to_b58check t) +end + +type t = Bytes.t + +type watermark = Bytes.t + +let name = "MLDSA44" + +let title = "An ML-DSA-44 signature" + +let size = 2420 + +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 + +let b58check_encoding = + Base58.register_encoding + ~prefix:Base58.Prefix.mldsa44_signature + ~length:size + ~to_raw:to_string + ~of_raw:of_string_opt + ~wrap:(fun x -> Data x) + +let () = Base58.check_encoded_prefix b58check_encoding "mdsig" 3316 + +include Helpers.MakeRaw (struct + type nonrec t = t + + let name = name + + let of_bytes_opt = of_bytes_opt + + let of_string_opt = of_string_opt + + let to_string = to_string +end) + +include Helpers.MakeB58 (struct + type nonrec t = t + + let name = name + + let b58check_encoding = b58check_encoding +end) + +include Helpers.MakeEncoder (struct + type nonrec t = t + + let name = name + + let title = title + + let raw_encoding = + let open Data_encoding in + conv to_bytes of_bytes_exn (Fixed.bytes size) + + let of_b58check = of_b58check + + let of_b58check_opt = of_b58check_opt + + let of_b58check_exn = of_b58check_exn + + let to_b58check = to_b58check + + let to_short_b58check = to_short_b58check +end) + +let pp ppf t = Format.fprintf ppf "%s" (to_b58check t) + +let zero = Bytes.make size '\000' + +let sign ?watermark ((signing_key, _verification_key) : Secret_key.t) msg = + let msg = + Blake2B.to_bytes @@ Blake2B.hash_bytes + @@ match watermark with None -> [msg] | Some prefix -> [prefix; msg] + in + match + Octez_ml_dsa.octez_ml_dsa_44_sign + signing_key + msg + Bytes.empty + (Rand.generate 32) + with + | Error err -> Stdlib.failwith err + | Ok signature -> Octez_ml_dsa.octez_ml_dsa_44_signature_to_bytes signature + +let check ?watermark verification_key signature msg = + let msg = + Blake2B.to_bytes @@ Blake2B.hash_bytes + @@ match watermark with None -> [msg] | Some prefix -> [prefix; msg] + in + let signature = + match Octez_ml_dsa.octez_ml_dsa_44_signature_from_bytes signature with + | Error err -> Stdlib.failwith err + | Ok signature -> signature + in + match + Octez_ml_dsa.octez_ml_dsa_44_verify + verification_key + msg + Bytes.empty + signature + with + | Ok () -> true + | Error _ -> false + +let generate_key ?seed () = + let seed = match seed with Some seed -> seed | None -> Rand.generate 32 in + match Octez_ml_dsa.octez_ml_dsa_44_generate_key_pair seed with + | Error err -> Stdlib.failwith err + | Ok key_pair -> + let pk = + Octez_ml_dsa.octez_ml_dsa_44_key_pair_get_verification_key key_pair + in + let sk = Octez_ml_dsa.octez_ml_dsa_44_key_pair_get_signing_key key_pair in + (Public_key.hash pk, pk, (sk, pk)) + +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 = + Blake2B.to_bytes (Blake2B.hash_bytes [deterministic_nonce sk msg]) + +let pop_verify _ ?msg:_ _ = false + +include (Compare.Bytes : Compare.S with type t := t) diff --git a/src/lib_crypto/mldsa44.mli b/src/lib_crypto/mldsa44.mli new file mode 100644 index 000000000000..009bd92d08b2 --- /dev/null +++ b/src/lib_crypto/mldsa44.mli @@ -0,0 +1,12 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* SPDX-FileCopyrightText: 2025 Nomadic Labs *) +(* *) +(*****************************************************************************) + +(** Tezos - ML-DSA-44 cryptography *) + +include S.SIGNATURE with type watermark = Bytes.t + +include S.RAW_DATA with type t := t -- GitLab From 1900def7015fe17c0c549c5c7b37165b93ea5a70 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Mon, 15 Dec 2025 12:20:14 +0100 Subject: [PATCH 6/6] lib_crypto: signature v3 introduce mldsa44 --- .../yes_wallet/test/bench_signature_perf.ml | 6 + devtools/yes_wallet/yes_wallet_lib.ml | 4 + src/bin_signer/handler.ml | 2 +- src/lib_client_base/client_keys.ml | 18 ++ .../client_keys_commands.ml | 9 +- src/lib_crypto/signature.ml | 12 ++ src/lib_crypto/signature_v3.ml | 159 +++++++++++++++--- src/lib_crypto/signature_v3.mli | 13 +- src/lib_crypto/tezos_crypto.ml | 1 + .../encoding_benchmarks.ml | 1 + src/lib_signer_backends/encrypted.ml | 47 +++++- src/lib_signer_backends/unix/socket.ml | 1 + .../lib_client/injection.ml | 1 + .../lib_plugin/plugin_registerer.ml | 3 + .../lib_plugin/plugin_registerer.ml | 3 + .../dal.ml/DAL Node- P2P message encoding.out | 15 +- 16 files changed, 261 insertions(+), 34 deletions(-) diff --git a/devtools/yes_wallet/test/bench_signature_perf.ml b/devtools/yes_wallet/test/bench_signature_perf.ml index d1d354de60ee..c51ff7796e8a 100644 --- a/devtools/yes_wallet/test/bench_signature_perf.ml +++ b/devtools/yes_wallet/test/bench_signature_perf.ml @@ -33,22 +33,27 @@ let keys = let keys_e = Tezos_crypto.Signature.generate_key ~algo:Ed25519 () in let keys_s = Tezos_crypto.Signature.generate_key ~algo:Secp256k1 () in let keys_b = Tezos_crypto.Signature.generate_key ~algo:Bls () in + let keys_m = Tezos_crypto.Signature.generate_key ~algo:Mldsa44 () in function | Tezos_crypto.Signature.P256 -> keys_p | Ed25519 -> keys_e | Secp256k1 -> keys_s | Bls -> keys_b + | Mldsa44 -> keys_m let wrong_keys = let keys_p = Tezos_crypto.Signature.generate_key ~algo:P256 () in let keys_e = Tezos_crypto.Signature.generate_key ~algo:Ed25519 () in let keys_s = Tezos_crypto.Signature.generate_key ~algo:Secp256k1 () in let keys_b = Tezos_crypto.Signature.generate_key ~algo:Bls () in + let keys_m = Tezos_crypto.Signature.generate_key ~algo:Mldsa44 () in + function | Tezos_crypto.Signature.P256 -> keys_p | Ed25519 -> keys_e | Secp256k1 -> keys_s | Bls -> keys_b + | Mldsa44 -> keys_m let wrong_pk algo = let _, pk, _ = wrong_keys algo in @@ -107,6 +112,7 @@ let str_of_algo = function | Tezos_crypto.Signature.Secp256k1 -> "Secp256k1" | Tezos_crypto.Signature.P256 -> "P256" | Tezos_crypto.Signature.Bls -> "Bls" + | Tezos_crypto.Signature.Mldsa44 -> "Mldsa44" let time ~yes_crypto ~algo size datas = Format.eprintf "generating signatures...@?" ; diff --git a/devtools/yes_wallet/yes_wallet_lib.ml b/devtools/yes_wallet/yes_wallet_lib.ml index 74bc9c49db9a..5d7be9def11d 100644 --- a/devtools/yes_wallet/yes_wallet_lib.ml +++ b/devtools/yes_wallet/yes_wallet_lib.ml @@ -63,6 +63,10 @@ let fake_sk_of_pk (pk_s : string) : string = let sk_b = Bytes.sub pk_b 0 33 in (* Extracting fake secret key from the associated public key.*) Data_encoding.Binary.of_bytes_exn Secret_key.encoding sk_b + | Mldsa44 _ -> + let sk_b = Bytes.sub pk_b 0 1313 in + (* Extracting fake secret key from the associated public key.*) + Data_encoding.Binary.of_bytes_exn Secret_key.encoding sk_b | Bls _ -> (* For BLS we cannot easily encode secret key from public key bytes. It is simpler to generate a new random secret key with the public key as diff --git a/src/bin_signer/handler.ml b/src/bin_signer/handler.ml index 66f3027c4384..45912e0d4fca 100644 --- a/src/bin_signer/handler.ml +++ b/src/bin_signer/handler.ml @@ -78,7 +78,7 @@ module High_watermark = struct (* Slot is not part of the signed payload when signing with a tz4 address *) 1 + 4 + 32 + 1 - | Ed25519 _ | Secp256k1 _ | P256 _ -> 1 + 4 + 32 + 1 + 2 + | Ed25519 _ | Secp256k1 _ | P256 _ | Mldsa44 _ -> 1 + 4 + 32 + 1 + 2 in let level = Bytes.get_int32_be bytes level_offset in let round = Bytes.get_int32_be bytes (level_offset + 4) in diff --git a/src/lib_client_base/client_keys.ml b/src/lib_client_base/client_keys.ml index 1aab8f84e8a6..f57b208c3d21 100644 --- a/src/lib_client_base/client_keys.ml +++ b/src/lib_client_base/client_keys.ml @@ -976,6 +976,8 @@ module V0 = Make (struct function | Bls _ -> tzfail (Exn (Failure "BLS public key hash not supported by V0")) + | Mldsa44 _ -> + tzfail (Exn (Failure "MLDSA44 public key hash not supported by V0")) | Ed25519 k -> return (Ed25519 k : Public_key_hash.t) | Secp256k1 k -> return (Secp256k1 k : Public_key_hash.t) | P256 k -> return (P256 k : Public_key_hash.t) @@ -985,6 +987,8 @@ module V0 = Make (struct let open Result_syntax in function | Bls _ -> tzfail (Exn (Failure "BLS public key not supported by V0")) + | Mldsa44 _ -> + tzfail (Exn (Failure "MLDSA44 public key not supported by V0")) | Ed25519 k -> return (Ed25519 k : Public_key.t) | Secp256k1 k -> return (Secp256k1 k : Public_key.t) | P256 k -> return (P256 k : Public_key.t) @@ -993,6 +997,8 @@ module V0 = Make (struct let open Result_syntax in function | Bls _ -> tzfail (Exn (Failure "BLS signature not supported by V0")) + | Mldsa44 _ -> + tzfail (Exn (Failure "MLDSA44 signature not supported by V0")) | Ed25519 k -> return (Ed25519 k : t) | Secp256k1 k -> return (Secp256k1 k : t) | P256 k -> return (P256 k : t) @@ -1010,6 +1016,8 @@ module V1 = Make (struct Tezos_crypto.Signature.Public_key_hash.t -> Public_key_hash.t tzresult = let open Result_syntax in function + | Mldsa44 _ -> + tzfail (Exn (Failure "MLDSA44 public key hash not supported by V1")) | Bls k -> return (Bls k : Public_key_hash.t) | Ed25519 k -> return (Ed25519 k : Public_key_hash.t) | Secp256k1 k -> return (Secp256k1 k : Public_key_hash.t) @@ -1019,6 +1027,8 @@ module V1 = Make (struct Tezos_crypto.Signature.Public_key.t -> Public_key.t tzresult = let open Result_syntax in function + | Mldsa44 _ -> + tzfail (Exn (Failure "MLDSA44 public key not supported by V1")) | Bls k -> return (Bls k : Public_key.t) | Ed25519 k -> return (Ed25519 k : Public_key.t) | Secp256k1 k -> return (Secp256k1 k : Public_key.t) @@ -1027,6 +1037,8 @@ module V1 = Make (struct let signature : Tezos_crypto.Signature.t -> t tzresult = let open Result_syntax in function + | Mldsa44 _ -> + tzfail (Exn (Failure "MLDSA44 signature not supported by V1")) | Bls k -> return (Bls k : t) | Ed25519 k -> return (Ed25519 k : t) | Secp256k1 k -> return (Secp256k1 k : t) @@ -1045,6 +1057,8 @@ module V2 = Make (struct Tezos_crypto.Signature.Public_key_hash.t -> Public_key_hash.t tzresult = let open Result_syntax in function + | Mldsa44 _ -> + tzfail (Exn (Failure "MLDSA44 public key hash not supported by V2")) | Bls k -> return (Bls k : Public_key_hash.t) | Ed25519 k -> return (Ed25519 k : Public_key_hash.t) | Secp256k1 k -> return (Secp256k1 k : Public_key_hash.t) @@ -1054,6 +1068,8 @@ module V2 = Make (struct Tezos_crypto.Signature.Public_key.t -> Public_key.t tzresult = let open Result_syntax in function + | Mldsa44 _ -> + tzfail (Exn (Failure "MLDSA44 public key not supported by V2")) | Bls k -> return (Bls k : Public_key.t) | Ed25519 k -> return (Ed25519 k : Public_key.t) | Secp256k1 k -> return (Secp256k1 k : Public_key.t) @@ -1062,6 +1078,8 @@ module V2 = Make (struct let signature : Tezos_crypto.Signature.t -> t tzresult = let open Result_syntax in function + | Mldsa44 _ -> + tzfail (Exn (Failure "MLDSA44 signature not supported by V2")) | Bls k -> return (Bls k : t) | Ed25519 k -> return (Ed25519 k : t) | Secp256k1 k -> return (Secp256k1 k : t) diff --git a/src/lib_client_commands/client_keys_commands.ml b/src/lib_client_commands/client_keys_commands.ml index f8f2dce523cb..b61d618dca39 100644 --- a/src/lib_client_commands/client_keys_commands.ml +++ b/src/lib_client_commands/client_keys_commands.ml @@ -34,17 +34,19 @@ let group = let algo_param () = let open Lwt_result_syntax in Tezos_clic.parameter - ~autocomplete:(fun _ -> return ["ed25519"; "secp256k1"; "p256"; "bls"]) + ~autocomplete:(fun _ -> + return ["ed25519"; "secp256k1"; "p256"; "bls"; "mldsa44"]) (fun _ name -> match name with | "ed25519" -> return Signature.Ed25519 | "secp256k1" -> return Signature.Secp256k1 | "p256" -> return Signature.P256 | "bls" -> return Signature.Bls + | "mldsa44" -> return Signature.Mldsa44 | name -> failwith "Unknown signature algorithm (%s). Available: 'ed25519', \ - 'secp256k1','p256' or 'bls'" + 'secp256k1','p256', 'bls' or 'mldsa44'" name) let sig_algo_arg = @@ -52,7 +54,7 @@ let sig_algo_arg = ~doc:"use custom signature algorithm" ~long:"sig" ~short:'s' - ~placeholder:"ed25519|secp256k1|p256|bls" + ~placeholder:"ed25519|secp256k1|p256|bls|mldsa44" ~default:"ed25519" (algo_param ()) @@ -63,6 +65,7 @@ let algo_to_prefix algo = | Secp256k1 -> "tz2" | P256 -> "tz3" | Bls -> "tz4" + | Mldsa44 -> "tz5" let gen_keys_containing ?(encrypted = false) ?(prefix = false) ?(ignore_case = false) ?(force = false) ~algo ~containing ~name diff --git a/src/lib_crypto/signature.ml b/src/lib_crypto/signature.ml index 039fb6634e3b..b88d182761d4 100644 --- a/src/lib_crypto/signature.ml +++ b/src/lib_crypto/signature.ml @@ -127,18 +127,21 @@ module V0 = struct | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) | V_latest.Bls _ -> None + | V_latest.Mldsa44 _ -> None let public_key : V_latest.Public_key.t -> Public_key.t option = function | V_latest.Ed25519 k -> Some (Ed25519 k) | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) | V_latest.Bls _ -> None + | V_latest.Mldsa44 _ -> None let secret_key : V_latest.Secret_key.t -> Secret_key.t option = function | V_latest.Ed25519 k -> Some (Ed25519 k) | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) | V_latest.Bls _ -> None + | V_latest.Mldsa44 _ -> None let signature : V_latest.t -> t option = function | V_latest.Ed25519 k -> Some (Ed25519 k) @@ -146,6 +149,7 @@ module V0 = struct | V_latest.P256 k -> Some (P256 k) | V_latest.Unknown k -> Some (Unknown k) | V_latest.Bls _ -> None + | V_latest.Mldsa44 _ -> None let get_public_key pk = match public_key pk with @@ -227,18 +231,21 @@ module V1 = struct | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) | V_latest.Bls k -> Some (Bls k) + | V_latest.Mldsa44 _ -> None let public_key : V_latest.Public_key.t -> Public_key.t option = function | V_latest.Ed25519 k -> Some (Ed25519 k) | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) | V_latest.Bls k -> Some (Bls k) + | V_latest.Mldsa44 _ -> None let secret_key : V_latest.Secret_key.t -> Secret_key.t option = function | V_latest.Ed25519 k -> Some (Ed25519 k) | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) | V_latest.Bls k -> Some (Bls k) + | V_latest.Mldsa44 _ -> None let signature : V_latest.t -> t option = function | V_latest.Ed25519 k -> Some (Ed25519 k) @@ -246,6 +253,7 @@ module V1 = struct | V_latest.P256 k -> Some (P256 k) | V_latest.Unknown k -> Some (Unknown k) | V_latest.Bls k -> Some (Bls k) + | V_latest.Mldsa44 _ -> None let get_public_key pk = match public_key pk with @@ -327,18 +335,21 @@ module V2 = struct | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) | V_latest.Bls k -> Some (Bls k) + | V_latest.Mldsa44 _ -> None let public_key : V_latest.Public_key.t -> Public_key.t option = function | V_latest.Ed25519 k -> Some (Ed25519 k) | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) | V_latest.Bls k -> Some (Bls k) + | V_latest.Mldsa44 _ -> None let secret_key : V_latest.Secret_key.t -> Secret_key.t option = function | V_latest.Ed25519 k -> Some (Ed25519 k) | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) | V_latest.Bls k -> Some (Bls k) + | V_latest.Mldsa44 _ -> None let signature : V_latest.t -> t option = function | V_latest.Ed25519 k -> Some (Ed25519 k) @@ -346,6 +357,7 @@ module V2 = struct | V_latest.P256 k -> Some (P256 k) | V_latest.Unknown k -> Some (Unknown k) | V_latest.Bls k -> Some (Bls k) + | V_latest.Mldsa44 _ -> None let get_public_key pk = match public_key pk with diff --git a/src/lib_crypto/signature_v3.ml b/src/lib_crypto/signature_v3.ml index 54c5440d6553..953b71199e86 100644 --- a/src/lib_crypto/signature_v3.ml +++ b/src/lib_crypto/signature_v3.ml @@ -12,18 +12,21 @@ type public_key_hash = | Secp256k1 of Secp256k1.Public_key_hash.t | P256 of P256.Public_key_hash.t | Bls of Bls.Public_key_hash.t + | Mldsa44 of Mldsa44.Public_key_hash.t type public_key = | Ed25519 of Ed25519.Public_key.t | Secp256k1 of Secp256k1.Public_key.t | P256 of P256.Public_key.t | Bls of Bls.Public_key.t + | Mldsa44 of Mldsa44.Public_key.t type secret_key = | Ed25519 of Ed25519.Secret_key.t | Secp256k1 of Secp256k1.Secret_key.t | P256 of P256.Secret_key.t | Bls of Bls.Secret_key.t + | Mldsa44 of Mldsa44.Secret_key.t type watermark = Signature_v0.watermark = | Block_header of Chain_id.t @@ -37,14 +40,16 @@ module Public_key_hash = struct | Secp256k1 of Secp256k1.Public_key_hash.t | P256 of P256.Public_key_hash.t | Bls of Bls.Public_key_hash.t + | Mldsa44 of Mldsa44.Public_key_hash.t let name = "Signature.Public_key_hash" - let title = "A Ed25519, Secp256k1, P256, or BLS public key hash" + let title = "A Ed25519, Secp256k1, P256, BLS or Mldsa44 public key hash" let is_bls : t -> bool = function | Bls _ -> true | Ed25519 _ | Secp256k1 _ | P256 _ -> false + | Mldsa44 _ -> false type Base58.data += Data of t (* unused *) @@ -86,6 +91,12 @@ module Public_key_hash = struct Bls.Public_key_hash.encoding (function Bls x -> Some x | _ -> None) (function x -> Bls x); + case + (Tag 4) + ~title:"Mldsa44" + Mldsa44.Public_key_hash.encoding + (function Mldsa44 x -> Some x | _ -> None) + (function x -> Mldsa44 x); ] let to_bytes s = Data_encoding.Binary.to_bytes_exn raw_encoding s @@ -118,6 +129,7 @@ module Public_key_hash = struct | Some (Secp256k1.Public_key_hash.Data pkh) -> Some (Secp256k1 pkh) | Some (P256.Public_key_hash.Data pkh) -> Some (P256 pkh) | Some (Bls.Public_key_hash.Data pkh) -> Some (Bls pkh) + | Some (Mldsa44.Public_key_hash.Data pkh) -> Some (Mldsa44 pkh) | _ -> None let of_b58check_exn s = @@ -136,12 +148,14 @@ module Public_key_hash = struct | Secp256k1 pkh -> Secp256k1.Public_key_hash.to_b58check pkh | P256 pkh -> P256.Public_key_hash.to_b58check pkh | Bls pkh -> Bls.Public_key_hash.to_b58check pkh + | Mldsa44 pkh -> Mldsa44.Public_key_hash.to_b58check pkh let to_short_b58check = function | Ed25519 pkh -> Ed25519.Public_key_hash.to_short_b58check pkh | Secp256k1 pkh -> Secp256k1.Public_key_hash.to_short_b58check pkh | P256 pkh -> P256.Public_key_hash.to_short_b58check pkh | Bls pkh -> Bls.Public_key_hash.to_short_b58check pkh + | Mldsa44 pkh -> Mldsa44.Public_key_hash.to_short_b58check pkh let to_path key l = match key with @@ -149,6 +163,7 @@ module Public_key_hash = struct | Secp256k1 h -> "secp256k1" :: Secp256k1.Public_key_hash.to_path h l | P256 h -> "p256" :: P256.Public_key_hash.to_path h l | Bls h -> "bls" :: Bls.Public_key_hash.to_path h l + | Mldsa44 h -> "Mldsa44" :: Mldsa44.Public_key_hash.to_path h l let of_path = function | "ed25519" :: q -> ( @@ -167,6 +182,10 @@ module Public_key_hash = struct match Bls.Public_key_hash.of_path q with | Some pkh -> Some (Bls pkh) | None -> None) + | "Mldsa44" :: q -> ( + match Mldsa44.Public_key_hash.of_path q with + | Some pkh -> Some (Mldsa44 pkh) + | None -> None) | _ -> assert false (* FIXME classification des erreurs *) @@ -176,6 +195,7 @@ module Public_key_hash = struct | "secp256k1" :: q -> Secp256k1 (Secp256k1.Public_key_hash.of_path_exn q) | "p256" :: q -> P256 (P256.Public_key_hash.of_path_exn q) | "bls" :: q -> Bls (Bls.Public_key_hash.of_path_exn q) + | "Mldsa44" :: q -> Mldsa44 (Mldsa44.Public_key_hash.of_path_exn q) | _ -> assert false (* FIXME classification des erreurs *) @@ -184,10 +204,12 @@ module Public_key_hash = struct let l1 = Ed25519.Public_key_hash.path_length and l2 = Secp256k1.Public_key_hash.path_length and l3 = P256.Public_key_hash.path_length - and l4 = Bls.Public_key_hash.path_length in + and l4 = Bls.Public_key_hash.path_length + and l5 = Mldsa44.Public_key_hash.path_length in assert (Compare.Int.(l1 = l2)) ; assert (Compare.Int.(l1 = l3)) ; assert (Compare.Int.(l1 = l4)) ; + assert (Compare.Int.(l1 = l5)) ; 1 + l1 let prefix_path _ = assert false (* unused *) @@ -205,7 +227,13 @@ module Public_key_hash = struct | Secp256k1 x, Secp256k1 y -> Secp256k1.Public_key_hash.compare x y | P256 x, P256 y -> P256.Public_key_hash.compare x y | Bls x, Bls y -> Bls.Public_key_hash.compare x y - | _ -> Stdlib.compare a b + | Mldsa44 x, Mldsa44 y -> Mldsa44.Public_key_hash.compare x y + | Ed25519 _, (Secp256k1 _ | P256 _ | Bls _ | Mldsa44 _) + | Secp256k1 _, (Ed25519 _ | P256 _ | Bls _ | Mldsa44 _) + | P256 _, (Ed25519 _ | Secp256k1 _ | Bls _ | Mldsa44 _) + | Bls _, (Ed25519 _ | Secp256k1 _ | P256 _ | Mldsa44 _) + | Mldsa44 _, (Ed25519 _ | Secp256k1 _ | P256 _ | Bls _) -> + Stdlib.compare a b end) include Helpers.MakeEncoder (struct @@ -259,10 +287,11 @@ module Public_key = struct | Secp256k1 of Secp256k1.Public_key.t | P256 of P256.Public_key.t | Bls of Bls.Public_key.t + | Mldsa44 of Mldsa44.Public_key.t let name = "Signature.Public_key" - let title = "A Ed25519, Secp256k1, P256 or a BLS public key" + let title = "A Ed25519, Secp256k1, P256, BLS or Mldsa44 public key" let hash pk = match pk with @@ -270,6 +299,7 @@ module Public_key = struct | Secp256k1 pk -> Public_key_hash.Secp256k1 (Secp256k1.Public_key.hash pk) | P256 pk -> Public_key_hash.P256 (P256.Public_key.hash pk) | Bls pk -> Public_key_hash.Bls (Bls.Public_key.hash pk) + | Mldsa44 pk -> Public_key_hash.Mldsa44 (Mldsa44.Public_key.hash pk) include Compare.Make (struct type nonrec t = t @@ -280,9 +310,12 @@ module Public_key = struct | Secp256k1 x, Secp256k1 y -> Secp256k1.Public_key.compare x y | P256 x, P256 y -> P256.Public_key.compare x y | Bls x, Bls y -> Bls.Public_key.compare x y - | Ed25519 _, (Secp256k1 _ | P256 _ | Bls _) -> -1 - | Secp256k1 _, (P256 _ | Bls _) -> -1 - | P256 _, Bls _ -> -1 + | Mldsa44 x, Mldsa44 y -> Mldsa44.Public_key.compare x y + | Ed25519 _, (Secp256k1 _ | P256 _ | Bls _ | Mldsa44 _) -> -1 + | Secp256k1 _, (P256 _ | Bls _ | Mldsa44 _) -> -1 + | P256 _, (Bls _ | Mldsa44 _) -> -1 + | Bls _, Mldsa44 _ -> -1 + | Mldsa44 _, (Bls _ | P256 _ | Secp256k1 _ | Ed25519 _) -> 1 | Bls _, (P256 _ | Secp256k1 _ | Ed25519 _) -> 1 | P256 _, (Secp256k1 _ | Ed25519 _) -> 1 | Secp256k1 _, Ed25519 _ -> 1 @@ -305,6 +338,7 @@ module Public_key = struct | Some (Secp256k1.Public_key.Data public_key) -> Some (Secp256k1 public_key) | Some (P256.Public_key.Data public_key) -> Some (P256 public_key) | Some (Bls.Public_key.Data public_key) -> Some (Bls public_key) + | Some (Mldsa44.Public_key.Data public_key) -> Some (Mldsa44 public_key) | _ -> None let of_b58check_exn s = @@ -323,12 +357,14 @@ module Public_key = struct | Secp256k1 pk -> Secp256k1.Public_key.to_b58check pk | P256 pk -> P256.Public_key.to_b58check pk | Bls pk -> Bls.Public_key.to_b58check pk + | Mldsa44 pk -> Mldsa44.Public_key.to_b58check pk let to_short_b58check = function | Ed25519 pk -> Ed25519.Public_key.to_short_b58check pk | Secp256k1 pk -> Secp256k1.Public_key.to_short_b58check pk | P256 pk -> P256.Public_key.to_short_b58check pk | Bls pk -> Bls.Public_key.to_short_b58check pk + | Mldsa44 pk -> Mldsa44.Public_key.to_short_b58check pk let of_bytes_without_validation b = let tag = Bytes.(get_int8 b 0) in @@ -348,6 +384,10 @@ module Public_key = struct | 3 -> Option.bind (Bls.Public_key.of_bytes_without_validation b) (fun pk -> Some (Bls pk)) + | 4 -> + Option.bind + (Mldsa44.Public_key.of_bytes_without_validation b) + (fun pk -> Some (Mldsa44 pk)) | _ -> None include Helpers.MakeEncoder (struct @@ -386,6 +426,12 @@ module Public_key = struct Bls.Public_key.encoding (function Bls x -> Some x | _ -> None) (function x -> Bls x); + case + ~title:"Mldsa44" + (Tag 4) + Mldsa44.Public_key.encoding + (function Mldsa44 x -> Some x | _ -> None) + (function x -> Mldsa44 x); ] let of_b58check = of_b58check @@ -410,10 +456,11 @@ module Secret_key = struct | Secp256k1 of Secp256k1.Secret_key.t | P256 of P256.Secret_key.t | Bls of Bls.Secret_key.t + | Mldsa44 of Mldsa44.Secret_key.t let name = "Signature.Secret_key" - let title = "A Ed25519, Secp256k1, P256 or a BLS secret key" + let title = "A Ed25519, Secp256k1, P256, Bls or Mldsa44 secret key" let to_public_key = function | Ed25519 sk -> Public_key.Ed25519 (Ed25519.Secret_key.to_public_key sk) @@ -421,6 +468,7 @@ module Secret_key = struct Public_key.Secp256k1 (Secp256k1.Secret_key.to_public_key sk) | P256 sk -> Public_key.P256 (P256.Secret_key.to_public_key sk) | Bls sk -> Public_key.Bls (Bls.Secret_key.to_public_key sk) + | Mldsa44 sk -> Public_key.Mldsa44 (Mldsa44.Secret_key.to_public_key sk) include Compare.Make (struct type nonrec t = t @@ -431,7 +479,13 @@ module Secret_key = struct | Secp256k1 x, Secp256k1 y -> Secp256k1.Secret_key.compare x y | P256 x, P256 y -> P256.Secret_key.compare x y | Bls x, Bls y -> Bls.Secret_key.compare x y - | _ -> Stdlib.compare a b + | Mldsa44 x, Mldsa44 y -> Mldsa44.Secret_key.compare x y + | Ed25519 _, (Secp256k1 _ | P256 _ | Bls _ | Mldsa44 _) + | Secp256k1 _, (Ed25519 _ | P256 _ | Bls _ | Mldsa44 _) + | P256 _, (Ed25519 _ | Secp256k1 _ | Bls _ | Mldsa44 _) + | Bls _, (Ed25519 _ | Secp256k1 _ | P256 _ | Mldsa44 _) + | Mldsa44 _, (Ed25519 _ | Secp256k1 _ | P256 _ | Bls _) -> + Stdlib.compare a b end) type Base58.data += Data of t (* unused *) @@ -451,6 +505,7 @@ module Secret_key = struct | Some (Secp256k1.Secret_key.Data sk) -> Some (Secp256k1 sk) | Some (P256.Secret_key.Data sk) -> Some (P256 sk) | Some (Bls.Secret_key.Data sk) -> Some (Bls sk) + | Some (Mldsa44.Secret_key.Data sk) -> Some (Mldsa44 sk) | _ -> None let of_b58check_exn s = @@ -469,12 +524,14 @@ module Secret_key = struct | Secp256k1 sk -> Secp256k1.Secret_key.to_b58check sk | P256 sk -> P256.Secret_key.to_b58check sk | Bls sk -> Bls.Secret_key.to_b58check sk + | Mldsa44 sk -> Mldsa44.Secret_key.to_b58check sk let to_short_b58check = function | Ed25519 sk -> Ed25519.Secret_key.to_short_b58check sk | Secp256k1 sk -> Secp256k1.Secret_key.to_short_b58check sk | P256 sk -> P256.Secret_key.to_short_b58check sk | Bls sk -> Bls.Secret_key.to_short_b58check sk + | Mldsa44 sk -> Mldsa44.Secret_key.to_short_b58check sk include Helpers.MakeEncoder (struct type nonrec t = t @@ -512,6 +569,12 @@ module Secret_key = struct Bls.Secret_key.encoding (function Bls x -> Some x | _ -> None) (function x -> Bls x); + case + (Tag 4) + ~title:"Mldsa44" + Mldsa44.Secret_key.encoding + (function Mldsa44 x -> Some x | _ -> None) + (function x -> Mldsa44 x); ] let of_b58check = of_b58check @@ -533,6 +596,7 @@ type signature = | Secp256k1 of Secp256k1.t | P256 of P256.t | Bls of Bls.t + | Mldsa44 of Mldsa44.t | Unknown of Bytes.t type prefix = Bls_prefix of Bytes.t @@ -543,13 +607,14 @@ type t = signature let name = "Signature.V3" -let title = "A Ed25519, Secp256k1, P256 or BLS signature" +let title = "A Ed25519, Secp256k1, P256, BLS or Mldsa44 signature" let to_bytes = function | Ed25519 b -> Ed25519.to_bytes b | Secp256k1 b -> Secp256k1.to_bytes b | P256 b -> P256.to_bytes b | Bls b -> Bls.to_bytes b + | Mldsa44 b -> Mldsa44.to_bytes b | Unknown b -> b let of_bytes_opt s = @@ -562,7 +627,9 @@ let () = assert (Ed25519.size = 64) ; assert (Secp256k1.size = 64) ; assert (P256.size = 64) ; - assert (Bls.size = 96) + assert (Bls.size = 96) ; + assert (Mldsa44.size = 2420) +(* TODO: fix size *) type Base58.data += Data_unknown of Bytes.t @@ -605,6 +672,8 @@ let of_b58check_opt s = then Option.map (fun x -> P256 x) (P256.of_b58check_opt s) else if TzString.has_prefix ~prefix:Bls.b58check_encoding.encoded_prefix s then Option.map (fun x -> Bls x) (Bls.of_b58check_opt s) + else if TzString.has_prefix ~prefix:Mldsa44.b58check_encoding.encoded_prefix s + then Option.map (fun x -> Mldsa44 x) (Mldsa44.of_b58check_opt s) else Option.map (fun x -> Unknown x) @@ -625,6 +694,7 @@ let to_b58check = function | Secp256k1 b -> Secp256k1.to_b58check b | P256 b -> P256.to_b58check b | Bls b -> Bls.to_b58check b + | Mldsa44 b -> Mldsa44.to_b58check b | Unknown b -> Base58.simple_encode unknown_b58check_encoding b let to_short_b58check = function @@ -632,6 +702,7 @@ let to_short_b58check = function | Secp256k1 b -> Secp256k1.to_short_b58check b | P256 b -> P256.to_short_b58check b | Bls b -> Bls.to_short_b58check b + | Mldsa44 b -> Mldsa44.to_short_b58check b | Unknown b -> Base58.simple_encode unknown_b58check_encoding b let raw_encoding = @@ -700,6 +771,8 @@ let of_p256 s = P256 s let of_bls s = Bls s +let of_mldsa44 s = Mldsa44 s + let zero = of_ed25519 Ed25519.zero (* NOTE: At the moment, only BLS signatures can be encoded with a tag. We impose @@ -724,7 +797,7 @@ let prefix_encoding = ] let split_signature = function - | (Ed25519 _ | Secp256k1 _ | P256 _) as s -> + | (Ed25519 _ | Secp256k1 _ | P256 _ | Mldsa44 _) as s -> {prefix = None; suffix = to_bytes s} | Bls s -> let s = Bls.to_bytes s in @@ -771,6 +844,7 @@ let sign ?watermark secret_key message = | Secp256k1 sk -> of_secp256k1 (Secp256k1.sign ?watermark sk message) | P256 sk -> of_p256 (P256.sign ?watermark sk message) | Bls sk -> of_bls (Bls.sign ?watermark sk message) + | Mldsa44 sk -> of_mldsa44 (Mldsa44.sign ?watermark sk message) let check ?watermark public_key signature message = let watermark = Option.map bytes_of_watermark watermark in @@ -791,6 +865,10 @@ let check ?watermark public_key signature message = match Bls.of_bytes_opt signature with | Some s -> Bls.check ?watermark pk s message | None -> false) + | Public_key.Mldsa44 pk, Unknown signature -> ( + match Mldsa44.of_bytes_opt signature with + | Some s -> Mldsa44.check ?watermark pk s message + | None -> false) | Public_key.Ed25519 pk, Ed25519 signature -> Ed25519.check ?watermark pk signature message | Public_key.Secp256k1 pk, Secp256k1 signature -> @@ -799,12 +877,18 @@ let check ?watermark public_key signature message = P256.check ?watermark pk signature message | Public_key.Bls pk, Bls signature -> Bls.check ?watermark pk signature message - | _ -> false + | Public_key.Mldsa44 pk, Mldsa44 signature -> + Mldsa44.check ?watermark pk signature message + | Public_key.Ed25519 _, (Secp256k1 _ | P256 _ | Bls _ | Mldsa44 _) -> false + | Public_key.Secp256k1 _, (Ed25519 _ | P256 _ | Bls _ | Mldsa44 _) -> false + | Public_key.P256 _, (Ed25519 _ | Secp256k1 _ | Bls _ | Mldsa44 _) -> false + | Public_key.Bls _, (Ed25519 _ | Secp256k1 _ | P256 _ | Mldsa44 _) -> false + | Public_key.Mldsa44 _, (Ed25519 _ | Secp256k1 _ | P256 _ | Bls _) -> false -type algo = Ed25519 | Secp256k1 | P256 | Bls +type algo = Ed25519 | Secp256k1 | P256 | Bls | Mldsa44 let hardcoded_sk = - let ed, secp, p, bls = + let ed, secp, p, bls, mldsa44 = ( Secret_key.of_b58check_exn "edsk3gUfUPyBSfrS9CCgmCiQsTCHGkviBDusMxDJstFtojtc1zcpsh", Secret_key.of_b58check_exn @@ -812,31 +896,51 @@ let hardcoded_sk = Secret_key.of_b58check_exn "p2sk2k6YAkNJ8CySZCS3vGA5Ht6Lj6LXG3yb8UrHvMKZy7Ab8JUtWh", Secret_key.of_b58check_exn - "BLsk1hfuv6V8JJRaLDBJgPTRGLKusTZnTmWGrvSKYzUaMuzvPLmeGG" ) + "BLsk1hfuv6V8JJRaLDBJgPTRGLKusTZnTmWGrvSKYzUaMuzvPLmeGG", + Secret_key.of_b58check_exn + "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" + ) in - function Ed25519 -> ed | Secp256k1 -> secp | P256 -> p | Bls -> bls + function + | Ed25519 -> ed + | Secp256k1 -> secp + | P256 -> p + | Bls -> bls + | Mldsa44 -> mldsa44 let hardcoded_pk = (* precompute signatures *) - let ed, secp, p, bls = + let ed, secp, p, bls, mldsa44 = ( Secret_key.to_public_key (hardcoded_sk Ed25519), Secret_key.to_public_key (hardcoded_sk Secp256k1), Secret_key.to_public_key (hardcoded_sk P256), - Secret_key.to_public_key (hardcoded_sk Bls) ) + Secret_key.to_public_key (hardcoded_sk Bls), + Secret_key.to_public_key (hardcoded_sk Mldsa44) ) in - function Ed25519 -> ed | Secp256k1 -> secp | P256 -> p | Bls -> bls + function + | Ed25519 -> ed + | Secp256k1 -> secp + | P256 -> p + | Bls -> bls + | Mldsa44 -> mldsa44 let hardcoded_msg = Bytes.of_string "Cheers" let hardcoded_sig = (* precompute signatures *) - let ed, secp, p, bls = + let ed, secp, p, bls, mldsa44 = ( sign (hardcoded_sk Ed25519) hardcoded_msg, sign (hardcoded_sk Secp256k1) hardcoded_msg, sign (hardcoded_sk P256) hardcoded_msg, - sign (hardcoded_sk Bls) hardcoded_msg ) + sign (hardcoded_sk Bls) hardcoded_msg, + sign (hardcoded_sk Mldsa44) hardcoded_msg ) in - function Ed25519 -> ed | Secp256k1 -> secp | P256 -> p | Bls -> bls + function + | Ed25519 -> ed + | Secp256k1 -> secp + | P256 -> p + | Bls -> bls + | Mldsa44 -> mldsa44 let fast_fake_sign ?watermark:_ (sk : Secret_key.t) _msg = let algo = @@ -845,6 +949,7 @@ let fast_fake_sign ?watermark:_ (sk : Secret_key.t) _msg = | Secp256k1 _ -> Secp256k1 | P256 _ -> P256 | Bls _ -> Bls + | Mldsa44 _ -> Mldsa44 in hardcoded_sig algo @@ -859,6 +964,7 @@ let fake_check ?watermark:_ (pk : Public_key.t) _signature _msg = | Secp256k1 _ -> Secp256k1 | P256 _ -> P256 | Bls _ -> Bls + | Mldsa44 _ -> Mldsa44 in ignore (check (hardcoded_pk algo) (hardcoded_sig algo) hardcoded_msg) ; true @@ -884,7 +990,7 @@ let append ?watermark sk msg = Bytes.cat msg (to_bytes (sign ?watermark sk msg)) let concat msg signature = Bytes.cat msg (to_bytes signature) -let algos = [Ed25519; Secp256k1; P256; Bls] +let algos = [Ed25519; Secp256k1; P256; Bls; Mldsa44] let fake_generate_key (pkh, pk, _) = let sk_of_pk (pk : public_key) : secret_key = @@ -912,6 +1018,9 @@ let generate_key ?(algo = Ed25519) ?seed () = | Bls -> let pkh, pk, sk = Bls.generate_key ?seed () in (Public_key_hash.Bls pkh, Public_key.Bls pk, Secret_key.Bls sk) + | Mldsa44 -> + let pkh, pk, sk = Mldsa44.generate_key ?seed () in + (Public_key_hash.Mldsa44 pkh, Public_key.Mldsa44 pk, Secret_key.Mldsa44 sk) let fake_generate_key ?(algo = Ed25519) ?seed () = let true_keys = generate_key ~algo ?seed () in @@ -931,6 +1040,7 @@ let deterministic_nonce sk msg = | Secret_key.Secp256k1 sk -> Secp256k1.deterministic_nonce sk msg | Secret_key.P256 sk -> P256.deterministic_nonce sk msg | Secret_key.Bls sk -> Bls.deterministic_nonce sk msg + | Secret_key.Mldsa44 sk -> Mldsa44.deterministic_nonce sk msg let deterministic_nonce_hash sk msg = match sk with @@ -938,6 +1048,7 @@ let 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 | Secret_key.Bls sk -> Bls.deterministic_nonce_hash sk msg + | Secret_key.Mldsa44 sk -> Mldsa44.deterministic_nonce_hash sk msg let pop_verify pubkey ?msg proof = match pubkey with diff --git a/src/lib_crypto/signature_v3.mli b/src/lib_crypto/signature_v3.mli index 56a7d7df0a26..86bc1f5284fd 100644 --- a/src/lib_crypto/signature_v3.mli +++ b/src/lib_crypto/signature_v3.mli @@ -10,18 +10,21 @@ type public_key_hash = | Secp256k1 of Secp256k1.Public_key_hash.t | P256 of P256.Public_key_hash.t | Bls of Bls.Public_key_hash.t + | Mldsa44 of Mldsa44.Public_key_hash.t type public_key = | Ed25519 of Ed25519.Public_key.t | Secp256k1 of Secp256k1.Public_key.t | P256 of P256.Public_key.t | Bls of Bls.Public_key.t + | Mldsa44 of Mldsa44.Public_key.t type secret_key = | Ed25519 of Ed25519.Secret_key.t | Secp256k1 of Secp256k1.Secret_key.t | P256 of P256.Secret_key.t | Bls of Bls.Secret_key.t + | Mldsa44 of Mldsa44.Secret_key.t type watermark = Signature_v0.watermark = | Block_header of Chain_id.t @@ -38,6 +41,7 @@ type signature = | Secp256k1 of Secp256k1.t | P256 of P256.t | Bls of Bls.t + | Mldsa44 of Mldsa44.t | Unknown of Bytes.t (** A signature prefix holds data only for signature that are more than 64 bytes @@ -70,7 +74,8 @@ val concat : Bytes.t -> t -> Bytes.t include S.RAW_DATA with type t := t (** The size of the signature in bytes. Can be [64] for Ed25519, Secp256k1 and - P256 signatures or [96] for BLS signatures. *) + P256 signatures, [96] for BLS signatures and [TODO] for MLDSA44 + signatures. *) val size : t -> int (** [of_secp256k1 s] returns a wrapped version of the Secp256k1 signature [s] in @@ -87,8 +92,12 @@ val of_p256 : P256.t -> t (** [of_bls s] returns a wrapped version of the BLS signature [s] in {!t}. *) val of_bls : Bls.t -> t +(** [of_mldsa44 s] returns a wrapped version of the Mldsa44 signature [s] in + {!t}. *) +val of_mldsa44 : Mldsa44.t -> t + (** The type of signing algorithms. *) -type algo = Ed25519 | Secp256k1 | P256 | Bls +type algo = Ed25519 | Secp256k1 | P256 | Bls | Mldsa44 (** The list of signing algorithm supported, i.e. all constructors of type {!algo}. *) diff --git a/src/lib_crypto/tezos_crypto.ml b/src/lib_crypto/tezos_crypto.ml index 12b7bdce7d25..bf7c7eebad48 100644 --- a/src/lib_crypto/tezos_crypto.ml +++ b/src/lib_crypto/tezos_crypto.ml @@ -51,6 +51,7 @@ module Signature = struct module Ed25519 = Ed25519 module P256 = P256 module Secp256k1 = Secp256k1 + module Mldsa44 = Mldsa44 include Signature end diff --git a/src/lib_shell_benchmarks/encoding_benchmarks.ml b/src/lib_shell_benchmarks/encoding_benchmarks.ml index b96d81471940..54e0a7d47f1d 100644 --- a/src/lib_shell_benchmarks/encoding_benchmarks.ml +++ b/src/lib_shell_benchmarks/encoding_benchmarks.ml @@ -48,6 +48,7 @@ struct | Tezos_crypto.Signature.Secp256k1 -> "secp256k1" | Tezos_crypto.Signature.P256 -> "p256" | Tezos_crypto.Signature.Bls -> "bls" + | Tezos_crypto.Signature.Mldsa44 -> "Mldsa44" module Sampler = Crypto_samplers.Make_finite_key_pool (struct let size = 256 diff --git a/src/lib_signer_backends/encrypted.ml b/src/lib_signer_backends/encrypted.ml index e3a931718136..94aa958d3921 100644 --- a/src/lib_signer_backends/encrypted.ml +++ b/src/lib_signer_backends/encrypted.ml @@ -34,6 +34,8 @@ type Tezos_crypto.Base58.data += Encrypted_secp256k1_element of Bytes.t type Tezos_crypto.Base58.data += Encrypted_bls12_381 of Bytes.t +type Tezos_crypto.Base58.data += Encrypted_mldsa44 of Bytes.t + type encrypted_sk = Encrypted_sk of Signature.algo type decrypted_sk = Decrypted_sk of Signature.Secret_key.t @@ -58,8 +60,13 @@ module Raw = struct let nonce = Tezos_crypto.Crypto_box.zero_nonce (* Secret keys for Ed25519, secp256k1, P256 have the same size. *) - let encrypted_size = - Tezos_crypto.Crypto_box.tag_length + Tezos_crypto.Hacl.Ed25519.sk_size + let encrypted_size = function + | Encrypted_sk + ( Signature.Ed25519 | Signature.P256 | Signature.Secp256k1 + | Signature.Bls ) -> + Tezos_crypto.Crypto_box.tag_length + Tezos_crypto.Hacl.Ed25519.sk_size + | Encrypted_sk Signature.Mldsa44 -> + Tezos_crypto.Crypto_box.tag_length + 2560 + 1312 let pbkdf ~salt ~password = Pbkdf.SHA512.pbkdf2 ~count:32768 ~dk_len:32l ~salt ~password @@ -85,12 +92,17 @@ module Raw = struct sk | Decrypted_sk (Bls sk) -> Data_encoding.Binary.to_bytes_exn Signature.Bls.Secret_key.encoding sk + | Decrypted_sk (Mldsa44 sk) -> + Data_encoding.Binary.to_bytes_exn + Signature.Mldsa44.Secret_key.encoding + sk in Bytes.cat salt (Tezos_crypto.Crypto_box.Secretbox.secretbox key msg nonce) let decrypt algo ~password ~encrypted_sk = let open Lwt_result_syntax in let salt = Bytes.sub encrypted_sk 0 salt_len in + let encrypted_size = encrypted_size algo in let encrypted_sk = Bytes.sub encrypted_sk salt_len encrypted_size in let key = Tezos_crypto.Crypto_box.Secretbox.unsafe_of_bytes (pbkdf ~salt ~password) @@ -148,6 +160,19 @@ module Raw = struct failwith "Corrupted wallet, deciphered key is not a valid BLS12_381 \ secret key") + | Some bytes, Encrypted_sk Signature.Mldsa44 -> ( + match + Data_encoding.Binary.of_bytes_opt + Signature.Mldsa44.Secret_key.encoding + bytes + with + | Some sk -> + return_some + (Decrypted_sk (Mldsa44 sk : Tezos_crypto.Signature.Secret_key.t)) + | None -> + failwith + "Corrupted wallet, deciphered key is not a valid MLDSA44 secret \ + key") end module Encodings = struct @@ -214,12 +239,25 @@ module Encodings = struct if String.length buf <> length then None else Some (Bytes.of_string buf)) ~wrap:(fun sk -> Encrypted_secp256k1_element sk) + let mldsa44 = + let length = + 2560 + 1312 + Tezos_crypto.Crypto_box.tag_length + Raw.salt_len + in + Tezos_crypto.Base58.register_encoding + ~prefix:Tezos_crypto.Base58.Prefix.mldsa44_encrypted_seed + ~length + ~to_raw:(fun sk -> Bytes.to_string sk) + ~of_raw:(fun buf -> + if String.length buf <> length then None else Some (Bytes.of_string buf)) + ~wrap:(fun sk -> Encrypted_mldsa44 sk) + let () = Tezos_crypto.Base58.check_encoded_prefix ed25519 "edesk" 88 ; Tezos_crypto.Base58.check_encoded_prefix secp256k1 "spesk" 88 ; Tezos_crypto.Base58.check_encoded_prefix p256 "p2esk" 88 ; Tezos_crypto.Base58.check_encoded_prefix bls12_381 "BLesk" 88 ; - Tezos_crypto.Base58.check_encoded_prefix secp256k1_scalar "seesk" 93 + Tezos_crypto.Base58.check_encoded_prefix secp256k1_scalar "seesk" 93 ; + Tezos_crypto.Base58.check_encoded_prefix mldsa44 "mdesk" 5332 end (* we cache the password in this list to avoid @@ -304,6 +342,8 @@ let decrypt_payload cctxt ?name encrypted_sk = return (Encrypted_sk Signature.P256, encrypted_sk) | Some (Encrypted_bls12_381 encrypted_sk) -> return (Encrypted_sk Signature.Bls, encrypted_sk) + | Some (Encrypted_mldsa44 encrypted_sk) -> + return (Encrypted_sk Signature.Mldsa44, encrypted_sk) | _ -> failwith "Not a Base58Check-encoded encrypted key" in let* o = noninteractive_decrypt_loop algo ~encrypted_sk !passwords in @@ -370,6 +410,7 @@ let common_encrypt sk password = | Decrypted_sk (Secp256k1 _) -> Encodings.secp256k1 | Decrypted_sk (P256 _) -> Encodings.p256 | Decrypted_sk (Bls _) -> Encodings.bls12_381 + | Decrypted_sk (Mldsa44 _) -> Encodings.mldsa44 in Tezos_crypto.Base58.simple_encode encoding payload diff --git a/src/lib_signer_backends/unix/socket.ml b/src/lib_signer_backends/unix/socket.ml index ebadd6b7c81f..ec25ed02a143 100644 --- a/src/lib_signer_backends/unix/socket.ml +++ b/src/lib_signer_backends/unix/socket.ml @@ -54,6 +54,7 @@ struct | _, None -> Pkh pkh | (Ed25519 _ | Secp256k1 _ | P256 _), Some _ -> Pkh pkh | Bls _, Some version -> Pkh_with_version (pkh, version) + | Mldsa44 _, Some version -> Pkh_with_version (pkh, version) in Request.Sign {Sign.Request.pkh; data; signature} | Deterministic_nonce_request -> diff --git a/src/proto_016_PtMumbai/lib_client/injection.ml b/src/proto_016_PtMumbai/lib_client/injection.ml index 5ae1741f1176..1c0e501d2f0c 100644 --- a/src/proto_016_PtMumbai/lib_client/injection.ml +++ b/src/proto_016_PtMumbai/lib_client/injection.ml @@ -638,6 +638,7 @@ let signature_size_of_algo : Tezos_crypto.Signature.algo -> int = function (* BLS signatures in operations are encoded with 2 extra bytes: a [ff] prefix and a tag [03]. *) Tezos_crypto.Signature.Bls.size + 2 + | Mldsa44 -> Tezos_crypto.Signature.Mldsa44.size (* This value is used as a safety guard for gas limit. *) let safety_guard = Gas.Arith.(integral_of_int_exn 100) diff --git a/src/proto_021_PsQuebec/lib_plugin/plugin_registerer.ml b/src/proto_021_PsQuebec/lib_plugin/plugin_registerer.ml index 5164164d39f9..58ac836a2890 100644 --- a/src/proto_021_PsQuebec/lib_plugin/plugin_registerer.ml +++ b/src/proto_021_PsQuebec/lib_plugin/plugin_registerer.ml @@ -64,6 +64,9 @@ module Delegators_contribution_plugin = struct | Secp256k1 x -> Secp256k1 x | P256 x -> P256 x | Bls x -> Bls x + | Mldsa44 x -> + Tezos_crypto.Signature.V1.Of_V_latest.get_public_key_hash_exn + (Mldsa44 x) let delegated_breakdown_at_sampling context ~cycle ~delegate_pkh = let open Lwt_result_syntax in diff --git a/src/proto_022_PsRiotum/lib_plugin/plugin_registerer.ml b/src/proto_022_PsRiotum/lib_plugin/plugin_registerer.ml index 7aed0ba0e4ba..b964e5460b5c 100644 --- a/src/proto_022_PsRiotum/lib_plugin/plugin_registerer.ml +++ b/src/proto_022_PsRiotum/lib_plugin/plugin_registerer.ml @@ -48,6 +48,9 @@ module Delegators_contribution_plugin = struct | Secp256k1 x -> Secp256k1 x | P256 x -> P256 x | Bls x -> Bls x + | Mldsa44 x -> + Tezos_crypto.Signature.V1.Of_V_latest.get_public_key_hash_exn + (Mldsa44 x) let delegated_breakdown_at_sampling context ~cycle ~delegate_pkh = let open Lwt_result_syntax in diff --git a/tezt/tests/expected/dal.ml/DAL Node- P2P message encoding.out b/tezt/tests/expected/dal.ml/DAL Node- P2P message encoding.out index b3441110b14c..8b4846107cda 100644 --- a/tezt/tests/expected/dal.ml/DAL Node- P2P message encoding.out +++ b/tezt/tests/expected/dal.ml/DAL Node- P2P message encoding.out @@ -70,6 +70,18 @@ Bls (tag 3) +---------------------------+----------+------------------------+ +Mldsa44 (tag 4) +=============== + ++-------------------------+----------+------------------------+ +| Name | Size | Contents | ++=========================+==========+========================+ +| Tag | 1 byte | unsigned 8-bit integer | ++-------------------------+----------+------------------------+ +| MLDSA44.Public_key_hash | 20 bytes | bytes | ++-------------------------+----------+------------------------+ + + X_1 *** @@ -294,7 +306,8 @@ $DAL_commitment: /* Commitment representation for the DAL (Base58Check-encoded) */ $unistring $Signature.Public_key_hash: - /* A Ed25519, Secp256k1, P256, or BLS public key hash (Base58Check-encoded) */ + /* A Ed25519, Secp256k1, P256, BLS or Mldsa44 public key hash + (Base58Check-encoded) */ $unistring $p2p_point.id: /* Identifier for a peer point */ -- GitLab