From d92fc2bed63565984acda1505d10fd640b22576c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:58:18 +0000 Subject: [PATCH 01/86] build(deps): bump reviewdog/action-actionlint from 1.63.0 to 1.64.1 (#846) Bumps [reviewdog/action-actionlint](https://github.com/reviewdog/action-actionlint) from 1.63.0 to 1.64.1. - [Release notes](https://github.com/reviewdog/action-actionlint/releases) - [Commits](https://github.com/reviewdog/action-actionlint/compare/v1.63.0...v1.64.1) --- updated-dependencies: - dependency-name: reviewdog/action-actionlint dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aa5ed27..b422c09 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -200,7 +200,7 @@ jobs: continue-on-error: true steps: - uses: actions/checkout@v4.2.2 - - uses: reviewdog/action-actionlint@v1.63.0 + - uses: reviewdog/action-actionlint@v1.64.1 with: level: warning fail_on_error: false From 2a7f8aece2b3b71242c63d106c3193876a2ce596 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:58:21 +0000 Subject: [PATCH 02/86] build(deps): bump codecov/codecov-action from 5.1.2 to 5.2.0 (#848) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.1.2 to 5.2.0. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v5.1.2...v5.2.0) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/coverage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 78b16a2..9c98055 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -44,4 +44,4 @@ jobs: run: cargo llvm-cov report --lcov --ignore-filename-regex '.*(tests).*|benches.rs|gencode|helpers.rs|interoperability_tests.rs' --output-path lcov.info - name: Upload coverage report to Codecov - uses: codecov/codecov-action@v5.1.2 + uses: codecov/codecov-action@v5.2.0 From 1f8e0e90dc8eb35361c16f94f796c7ca55e742a0 Mon Sep 17 00:00:00 2001 From: Pili Guerra <1311133+mpguerra@users.noreply.github.com> Date: Tue, 28 Jan 2025 15:54:49 +0100 Subject: [PATCH 03/86] Update dependabot.yml (#856) Change to monthly schedule --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b21dd53..75dc882 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,12 +3,12 @@ updates: - package-ecosystem: github-actions directory: "/" schedule: - interval: daily + interval: monthly timezone: America/New_York open-pull-requests-limit: 10 - package-ecosystem: cargo directory: "/" schedule: - interval: daily + interval: monthly timezone: America/New_York open-pull-requests-limit: 10 From 121a11650b44a068b81d22db2592f0093e330fdb Mon Sep 17 00:00:00 2001 From: Skylar Ray <137945430+sky-coderay@users.noreply.github.com> Date: Thu, 30 Jan 2025 16:32:53 +0200 Subject: [PATCH 04/86] fix errors (#858) * Update release-checklist.md * Update dkg.rs --- book/src/dev/release-checklist.md | 2 +- frost-core/src/keys/dkg.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/dev/release-checklist.md b/book/src/dev/release-checklist.md index 7173eb8..d5893e5 100644 --- a/book/src/dev/release-checklist.md +++ b/book/src/dev/release-checklist.md @@ -33,7 +33,7 @@ ## Make changes 7. Bump the version of the crates in the root Cargo.toml file. (If they ever - get out of sync, you wil need to bump in each crate Cargo.toml file.) + get out of sync, you will need to bump in each crate Cargo.toml file.) 8. Bump the version used in the tutorial (importing.md) diff --git a/frost-core/src/keys/dkg.rs b/frost-core/src/keys/dkg.rs index 4373a91..541c053 100644 --- a/frost-core/src/keys/dkg.rs +++ b/frost-core/src/keys/dkg.rs @@ -17,7 +17,7 @@ //! //! As required for any multi-party protocol using Feldman's VSS, the key //! generation stage in FROST requires participants to maintain a consistent -//! view of the pubic commitments to the secret polynomial coefficients. This +//! view of the public commitments to the secret polynomial coefficients. This //! DKG protocol requires participants to broadcast the commitment values //! honestly (e.g., participants do not provide different commitment values to a //! subset of participants) over a _[secure broadcast channel]_. From 703559343f4c48befbf2b3a72d29fb0855a1f43b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 23:25:13 +0000 Subject: [PATCH 05/86] build(deps): bump codecov/codecov-action from 5.2.0 to 5.3.1 (#851) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.2.0 to 5.3.1. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v5.2.0...v5.3.1) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/coverage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 9c98055..dac117b 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -44,4 +44,4 @@ jobs: run: cargo llvm-cov report --lcov --ignore-filename-regex '.*(tests).*|benches.rs|gencode|helpers.rs|interoperability_tests.rs' --output-path lcov.info - name: Upload coverage report to Codecov - uses: codecov/codecov-action@v5.2.0 + uses: codecov/codecov-action@v5.3.1 From 1d41c21675f558439af9c826bf90e1199da806d5 Mon Sep 17 00:00:00 2001 From: Antoine Rondelet Date: Tue, 4 Feb 2025 09:36:25 +0000 Subject: [PATCH 06/86] Fixing a small typo (#863) --- book/src/zcash/technical-details.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/zcash/technical-details.md b/book/src/zcash/technical-details.md index 56ae655..e75adfe 100644 --- a/book/src/zcash/technical-details.md +++ b/book/src/zcash/technical-details.md @@ -90,14 +90,14 @@ when recovering a wallet. The biggest challenge in using FROST with Zcash is allowing participants to communicate securely with each other, which is required to run the FROST -protocol. Since wallets don't currently need to communication to each other, a +protocol. Since wallets don't currently need to communicate with each other, a whole new mechanism will need to be implemented. For this to happen, two things are required: - Allowing wallets to actually communicate with each other (regardless of security). This is challenging because users are usually behind NATs and - firewalls, so they can't simply open TCP connections with each other. So + firewalls, so they can't simply open TCP connections with each other. So, some kind of signaling server may be needed. - Making the communication secure. This is actually fairly solvable while not trivial and we're planning on working on a library to address it. It needs to From 602157a6fd552e1535c85ba136fb811780f43857 Mon Sep 17 00:00:00 2001 From: XxAlex74xX <30472093+XxAlex74xX@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:36:28 +0100 Subject: [PATCH 07/86] docs: correction comments (#860) * Update repairable.rs * Update refresh.rs * Update repairable.rs * Update lib.rs --- frost-core/src/keys/repairable.rs | 2 +- frost-core/src/lib.rs | 2 +- frost-core/src/tests/refresh.rs | 2 +- frost-core/src/tests/repairable.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frost-core/src/keys/repairable.rs b/frost-core/src/keys/repairable.rs index df0be45..24cd32b 100644 --- a/frost-core/src/keys/repairable.rs +++ b/frost-core/src/keys/repairable.rs @@ -2,7 +2,7 @@ //! //! Implements the Repairable Threshold Scheme (RTS) from . //! The RTS is used to help a signer (participant) repair their lost share. This is achieved -//! using a subset of the other signers know here as `helpers`. +//! using a subset of the other signers known here as `helpers`. use alloc::collections::{BTreeMap, BTreeSet}; diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index 76078d9..a290984 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -682,7 +682,7 @@ fn detect_cheater( /// for which the signature share was produced and with the group's /// `verifying_key`. /// -/// This is not required for regular FROST usage but might useful in certain +/// This is not required for regular FROST usage but might be useful in certain /// situations where it is desired to verify each individual signature share /// before aggregating the signature. pub fn verify_signature_share( diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index 384924e..0d7d617 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -18,7 +18,7 @@ use alloc::vec::Vec; use super::ciphersuite_generic::check_sign; -/// We want to test that recover share matches the original share +/// We want to test that recovered share matches the original share pub fn check_refresh_shares_with_dealer(mut rng: R) { // Compute shares diff --git a/frost-core/src/tests/repairable.rs b/frost-core/src/tests/repairable.rs index 532e1e0..a922e32 100644 --- a/frost-core/src/tests/repairable.rs +++ b/frost-core/src/tests/repairable.rs @@ -16,7 +16,7 @@ use crate::{ Ciphersuite, Error, Field, Group, Identifier, Scalar, }; -/// We want to test that recover share matches the original share +/// We want to test that recovered share matches the original share pub fn check_rts(mut rng: R) { // Compute shares From 464cc050001e70713df10f5baf1f8589971cb5a0 Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Tue, 4 Feb 2025 17:41:50 -0300 Subject: [PATCH 08/86] book: add FROST Server section (#811) * book: add FROST Server section * book: update Ywallet demo section * fill TODOs * Apply suggestions from code review Co-authored-by: natalie * Apply suggestions from code review --------- Co-authored-by: natalie --- book/src/SUMMARY.md | 1 + book/src/zcash/server.md | 363 +++++++++++++++++++++++++++++++++ book/src/zcash/ywallet-demo.md | 296 ++++++++++++++++++--------- 3 files changed, 563 insertions(+), 97 deletions(-) create mode 100644 book/src/zcash/server.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 3970f8e..004ef64 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -13,6 +13,7 @@ - [FROST with Zcash](zcash.md) - [Technical Details](zcash/technical-details.md) - [Ywallet Demo](zcash/ywallet-demo.md) + - [FROST Server](zcash/server.md) - [Terminology](terminology.md) - [Developer Documentation](dev.md) - [List of Dependencies for Audit](dev/frost-dependencies-for-audit.md) diff --git a/book/src/zcash/server.md b/book/src/zcash/server.md new file mode 100644 index 0000000..6350394 --- /dev/null +++ b/book/src/zcash/server.md @@ -0,0 +1,363 @@ +# ZF FROST Server (frostd) + +One challenge for using FROST is allowing participants to communicate securely +with one another. Devices are usually behind firewalls and NATs, which make +direct connections hard. + +To mitigate this issue and to make it easier to use FROST, the ZF FROST Server +(frostd) was created. It is a JSON-HTTP server with a small API to allow +participants to create signing sessions and to communicate with one another. + +It works like this: + +- Clients (coordinator or participants) authenticate to the server using a key + pair, which will likely be the same key pair they use to end-to-end encrypt + messages. +- The Coordinator creates a session, specifying the public keys of the + participants. +- Participants list sessions they're participating in, and choose the proceed + with the signing session. +- Coordinator and Participants run the FROST protocol, end-to-end encrypting + messages and sending them to the server. +- The Coordinator closes the session. + +Note that the server doesn't really care about the particular key pair being +used; it is only used to enforce who can send messages to who. + +## Compiling, Running and Deploying + +You will need to have [Rust and +Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) +installed. Run: + +``` +cargo install --git https://github.com/ZcashFoundation/frost-zcash-demo.git --locked frostd +``` + +The `frostd` binary will be installed [per `cargo` +config](https://doc.rust-lang.org/cargo/commands/cargo-install.html#description) +and it will likely be in your `$PATH`, so you can run by simply running +`frostd`. + +To deploy the FROST Server, **you need TLS/HTTPS certificates**. We strongly +recommend using a reverse proxy such as `nginx` to handle TLS and to also add +denial of service protections. In that case, use the `--no-tls-very-insecure` +flag in `frostd` and make `nginx` connect to it (see example config below). + +If you want to expose `frostd` directly, use the `--tls-cert` and +`--tls-key` to specify the paths of the PEM-encoded certificate and key. You can +use [Let's Encrypt](https://letsencrypt.org/) to get a free certificate. + + +### Local Testing + +For local testing, you can use the [`mkcert` +tool](https://github.com/FiloSottile/mkcert). Install it and run: + +``` +mkcert -install +mkcert localhost 127.0.0.1 ::1 +``` + +Then start the server with: + +``` +frostd --tls-cert localhost+2.pem --tls-key localhost+2-key.pem +``` + + +### Sample nginx Config + +This is a sample nginx config file tested in a Ubuntu deployment (i.e. it +assumes it's in a `http` block and it's included by `/etc/nginx/nginx.conf`); +copy it to `/etc/nginx/sites-enabled/frostd` and run `sudo service nginx +restart`. + +The config assumes the certificates were copied to `/etc/ssl`. + + +``` +limit_req_zone $binary_remote_addr zone=challenge:10m rate=30r/m; +limit_req_zone $binary_remote_addr zone=create:10m rate=10r/m; +limit_req_zone $binary_remote_addr zone=other:10m rate=240r/m; +limit_conn_zone $binary_remote_addr zone=addr:10m; + +server { + listen 443 ssl; + listen [::]:443 ssl; + ssl_certificate /etc/ssl/localhost+2.pem; + ssl_certificate_key /etc/ssl/localhost+2-key.pem; + ssl_protocols TLSv1.3; + ssl_ecdh_curve X25519:prime256v1:secp384r1; + ssl_prefer_server_ciphers off; + + server_name localhost; + + client_body_timeout 5s; + client_header_timeout 5s; + + location / { + proxy_pass http://127.0.0.1:2744; + limit_req zone=other burst=5; + limit_conn addr 10; + } + location /challenge { + proxy_pass http://127.0.0.1:2744/challenge; + limit_req zone=challenge burst=3; + limit_conn addr 10; + } + location /create_new_session { + proxy_pass http://127.0.0.1:2744/create_new_session; + limit_req zone=create burst=3; + limit_conn addr 10; + } +} +``` + +## API + +The API uses JSON/HTTP. All requests should have `Content-Type: +application/json`. Errors are returned with status code 500 and the content +body will have a JSON such as: + +``` +{ code: 1, msg: "error message" } +``` + +The +[codes](https://github.com/ZcashFoundation/frost-zcash-demo/blob/548a8a7329c6eed8180464662f430d12cd71dfcc/frostd/src/lib.rs#L95-L98) +are: + +``` +pub const INVALID_ARGUMENT: usize = 1; +pub const UNAUTHORIZED: usize = 2; +pub const SESSION_NOT_FOUND: usize = 3; +pub const NOT_COORDINATOR: usize = 4; +``` + + +### Usage flow + +For the Coordinator: + +- Log in with `/challenge` and `/login` +- Create a new signing session with `/create_new_session` +- Wait for round 1 messages by repeatedly polling `/receive` each 2 seconds or longer +- Send round 2 messages by using `/send` +- Wait for round 2 message by repeatedly polling `/receive` each 2 seconds or longer +- Close the session with `/close_session` + +For Participants: + +- Log in with `/challenge` and `/login` +- Wait for signing sessions with `/list_sessions`, either by the user's request or by repeatedly + polling each 10 seconds or longer +- Get the session information with `/get_session_info` +- Show the user the session information (who the participants are) to select which + session (if more than one) +- Send round 1 message by using `/send` +- Wait for round 2 message by repeatedly polling `/receive` each 2 seconds or longer +- Send round 2 message by using `/send` + +```admonish info +**Polling** is not optimal. The server will support a better mechanism in the +future. +``` + +```admonish info +Selecting sessions is tricky. Ideally, the user should select what session +to proceed by checking the message being signed; however, that is usually +sent in Round 2. There are multiple ways to handle this: + +- Simply show the users who are participants, hoping that is enough to + disambiguate (we assume that concurrent signing sessions won't be that common) +- Quietly proceed with all sessions, and only prompt the user after the message + is received. (It's harmless to do round 1 of FROST even if the user might + not have agreed to sign the message yet.) +- Change the application so that the message is sent to the participants first + (the server does not really care how the protocol is run). +``` + +```admonish critical +Always gather consent from the user by showing them the message before +signing it. +``` + +### `/challenge` + +Input: empty + +Sample output: + +``` +{"challenge":"2c5cdb6d-a7db-470e-9e6f-2a7062532825"} +``` + +Returns a challenge that the client will need to sign in order to authenticate. + +### `/login` + +To call `/login`, you will need to sign the challenge with XEdDSA, see +[example](https://github.com/ZcashFoundation/frost-zcash-demo/blob/548a8a7329c6eed8180464662f430d12cd71dfcc/frostd/tests/integration_tests.rs#L443-L476). +Sign the challenge UUID, converted to bytes. + + +Input sample: + +``` +{ + "challenge":"b771757e-085a-4a88-ab8f-28bd4ba67f3a", + "pubkey":"f5bf1b8194e20ebdd28e662b1efcf1c5cd2aaade5d5dd83cf89b246b5492726b", + "signature":"bba398d0963ab88e28134ad41c127eeee816a219838db01dd7bcd9d7fcd975f082330c134e6f7238580ba8434652aa116891495452d9048f5615e07f4ad6b204" +} +``` + +Output sample: + +``` +{"access_token":"061a18ba-2c3c-4685-a79e-2c0c93000af5"} +``` + +The returned access token must be included as a bearer token in an +`Authorization` header; e.g. `Authorization: Bearer +061a18ba-2c3c-4685-a79e-2c0c93000af5`. + +Access tokens are currently valid for 1 hour. It's recommended to login at the +beginning of each FROST session; log in again if it needs to take longer. + +### `/logout` + +Input: empty (it will logout the authenticated user) + +Output: empty + +Logs out, invalidating the access token. Note that access tokens expire after +1 hour anyway. + +### `/create_new_session` + +Input sample: + +``` +{ + "pubkeys": [ + "3c9f4a3b2ae28c8e11fbc90b693a9712c181275fb4b554a140c68dc13cdd9b4c", + "edbd661dec0a9d0468b4a166a4afa80560d769f6bcb152fb8f4224059329a518" + ], + message_count: 1, +} +``` + +Output sample: + +``` +{"session_id": "2c5cdb6d-a7db-470e-9e6f-2a7062532825"} +``` + +Creates a new session. The requesting user will be the Coordinator, and the +users with the hex-encoded public keys given in `pubkeys` will be the +participants (which might or might not include the Coordinator itself). + +The `message_count` parameter allows signing more than one message in the same +signing session, which will save roundtrips. This does not impacts the server +itself and is used to signal the participants (via `/get_session_info`). + +### `/list_sessions` + +Input: empty (it will list for the authenticated user) + +Output sample: + +``` +{"session_ids": ["2c5cdb6d-a7db-470e-9e6f-2a7062532825"]} +``` + +List the sessions IDs of the session a participant is in. + +### `/get_session_info` + +Input sample: + +```{"session_id": "2c5cdb6d-a7db-470e-9e6f-2a7062532825"}``` + +Output sample: + +``` +{ + "message_count": 1, + "pubkeys": [ + "3c9f4a3b2ae28c8e11fbc90b693a9712c181275fb4b554a140c68dc13cdd9b4c", + "edbd661dec0a9d0468b4a166a4afa80560d769f6bcb152fb8f4224059329a518" + ], + "coordinator_pubkey": "3c9f4a3b2ae28c8e11fbc90b693a9712c181275fb4b554a140c68dc13cdd9b4c", +} +``` + +Returns information about the given session. + +### `/send` + +Input sample: + +``` +{ + "session_id": "2c5cdb6d-a7db-470e-9e6f-2a7062532825", + "recipients": ["3c9f4a3b2ae28c8e11fbc90b693a9712c181275fb4b554a140c68dc13cdd9b4c"], + "msg": "000102", +} +``` + +Output: empty + +Sends a (hex-encoded) message to one or more participants. To send to the +Coordinator, pass an empty list in `recipients` (**do not** use the +Coordinator's public key, because that might be ambiguous if they're also a +Participant). + +```admonish critical +Messages **MUST** be end-to-end encrypted between recipients. The server can't +enforce this and if you fail to encrypt them then the server could read +all the messages. +``` + +### `/receive` + +Input sample: + +``` +{ + "session_id": "2c5cdb6d-a7db-470e-9e6f-2a7062532825", + "as_coordinator": false, +} +``` + +Output sample: + +``` +{ + "msgs":[ + { + "sender": "3c9f4a3b2ae28c8e11fbc90b693a9712c181275fb4b554a140c68dc13cdd9b4c", + "msg": "000102", + } + ] +} +``` + +Receives messages sent to the requesting user. Note that if a user is both a +Coordinator and a Participant, it is not possible to distinguish if a message +received from them was sent as Coordinator or as a Participant. This does not +matter in FROST since this ambiguity never arises (Participants always receive +messages from the Coordinator, and vice-versa, except during DKG where there is +no Coordinator anyway). + +### `/close_session` + +Input sample: + +```{"session_id": "2c5cdb6d-a7db-470e-9e6f-2a7062532825"}``` + +Output: empty + +Closes the given session. Only the Coordinator who created the session can close +it. Sessions also expire by default after 24 hours. diff --git a/book/src/zcash/ywallet-demo.md b/book/src/zcash/ywallet-demo.md index ea4f134..c36513e 100644 --- a/book/src/zcash/ywallet-demo.md +++ b/book/src/zcash/ywallet-demo.md @@ -1,7 +1,8 @@ # Ywallet Demo Tutorial This tutorial explaining how to run the FROST demo using Ywallet that was -[presented during Zcon4](https://www.youtube.com/watch?v=xvzESdDtczo). +[presented during Zcon4](https://www.youtube.com/watch?v=xvzESdDtczo) (though it +has been updated and it differs from what was presented). Ywallet supports [offline signing](https://ywallet.app/advanced/offline_signature/), which allows having a @@ -11,88 +12,215 @@ the transaction plan with a command line tool, using FROST. This tutorial assumes familiarity with the command line. + ## Setting up Install `cargo` and `git`. [Install Ywallet](https://ywallet.app/installation/). -Clone the repository: +Install the `frost-client` tool: + +``` +cargo install --git https://github.com/ZcashFoundation/frost-zcash-demo.git --locked frost-client +``` + +Install the `zcash-sign` tool: + +``` +cargo install --git https://github.com/ZcashFoundation/frost-zcash-demo.git --locked zcash-sign +``` + +Switch to an empty folder which will store the files generate in the demo. +For example: ``` -git clone https://github.com/ZcashFoundation/frost-zcash-demo.git +mkdir frost-demo +cd frost-demo/ +``` + + +### Running the server + +This demo uses the ZF FROST server (frostd) to help participants communicate. +While in practice users would use an existing online server, for the demo you +can run a local server by following [these instructions](./server.md) (the +"Compiling, Running and Deploying" and "Local Testing" sections). + +The rest of the tutorial assumes the server is up and running. + + +### Initializing the users + +Run the following command to initialize three users (in practice, each user +would run a similar command, but for demo purposes we're assuming +you will simulate all of them in the same machine, so run these +commands in your machine): + +``` +frost-client init -c alice.toml +frost-client init -c bob.toml +frost-client init -c eve.toml +``` + +This will create a config file for three users; Alice, Bob and Eve. + +```admonish note +If you really want to run the demo in separate machines, then you can omit the +`-c alice.toml` part of the command (i.e. run `frost-client init`); it will +save to a default location in the user's home directory. ``` + ## Generating FROST key shares First we will generate the FROST key shares. For simplicity we'll use trusted -dealer, DKG will be described later. +dealer; if you want to use Distributed Key Generation, skip to the next section. + +In a new terminal (in case the previous terminal is running the server), run the +following: + +``` +frost-client trusted-dealer -d "Alice, Bob and Eve's group" --names Alice,Bob,Eve -c alice.toml -c bob.toml -c eve.toml -C redpallas +``` + +This will by default generate a 2-of-3 key shares. The key shares will be +written into each participant's config file. You can change the threhsold, +number of shares and file names using the command line; append `-h` to the +commend above for the command line help. + + +## Generating FROST key shares using DKG + +For real-word usage we commend generating key shares using Distributed Key +Generation. If you did the previous section, skip to "Generating the Full +Viewing Key for the wallet". + + +```admonish note +This section assumes each participant is running the commands in their own +machine. If you want to simulate all of them in a single machine, +specify the config file for the user (e.g. `-c alice.toml`) accordingly. +``` + + +### Initializing config files + +If they haven't yet, each participant should run: + +``` +frost-client init +``` + + +### Sharing contacts + +Each participant must now generate a contact string that they will need to share +with the other participants. This contact string will include a name, which they +can choose when exporting and will be shown to whoever they send the contact to. + +Run the following, substituting the name accordingly: -Run the following (it will take a bit to compile): +``` +frost-client export --name 'Alice' +``` + +The command will print an encoded contact string such as +`zffrost1qyqq2stvd93k2g84hudcr98zp67a9rnx9v00euw9e5424hjathvre7ymy344fynjdvxmwxfg`. +Send it to the other participants using some trusted communication channel +(instant messaging, etc.). + +The other participants will send you their contacts. Import them by running the +following command for each contact (replace `` with the contact +string accordingly): ``` -cd frost-zcash-demo/ -cargo run --bin trusted-dealer -- -C redpallas +frost-client import ``` -This will by default generate a 2-of-3 key shares. The public key package -will be written to `public-key-package.json`, while key packages will be -written to `key-package-1.json` through `-3`. You can change the threshold, -number of shares and file names using the command line; append `-- -h` -to the command above for the command line help. -```admonish info -If you want to use DKG instead of Trusted Dealer, instead of the command above, - run this for each participant, in separate terminals for each: +### Generating shares + +Finally, to generate the shares, one of the participants will need to initiate +the process. They will need to public key of each participant, so they need to +first list them with the following command: + +``` +frost-client contacts +``` -`cargo run --bin dkg -- -C redpallas` +Then run the following command, replacing the `` and `` hex +strings with the public keys of the contacts which will participate (along with +the user running the command): -and follow the instructions. (There will be a considerable amount of -copy&pasting!) +``` +frost-client dkg -d 'Alice, Bob and Eve's group' -s localhost:2744 -S , -t 2 -C redpallas -c alice.toml ``` +The user should then notify the others that a signing session has started (e.g. +via instant messaging again), and also share the threshold number that was used. +They should then run the following, replacing the name of the group if they wish +and the threshold number with the one given by the first participant. + +``` +frost-client dkg -d 'Alice, Bob and Eve's group' -s localhost:2744 -t 2 -C redpallas +``` + +```admonish note +A future version might not require specifying the threshold and group name. +``` + + ## Generating the Full Viewing Key for the wallet -Get the `verifying_key` value that is listed inside the Public Key Package in -`public-key-package.json`. For example, in the following package +Next, we will need to generate a Zcash Full Viewing Key from the FROST group +material we have just generated; this address will then be imported into a wallet +so that we'll be able to create Zcash transactions for it. + +Run the following command: ``` -{"verifying_shares": ...snip... ,"verifying_key":"d2bf40ca860fb97e9d6d15d7d25e4f17d2e8ba5dd7069188cbf30b023910a71b","ciphersuite":"FROST(Pallas, BLAKE2b-512)"} +frost-client groups ``` -you would need to copy -`d2bf40ca860fb97e9d6d15d7d25e4f17d2e8ba5dd7069188cbf30b023910a71b`. +It will list all groups you're in - at this point it should list the only one +you have just created. Copy the Public Key it shows (it will look like e.g. +`79d6bcee79c88ad9ba259067772b97f5de12f1435b474d03bc98f255be08a610`) The run the following command, replacing `` with the value you copied. ``` -cd zcash-sign/ -cargo run --release -- generate --ak --danger-dummy-sapling +zcash-sign generate --ak --danger-dummy-sapling ``` It will print an Orchard address, and a Unified Full Viewing Key. Copy and paste both somewhere to use them later. + ## Importing the Full Viewing Key into Ywallet Open Ywallet and click "New account". Check "Restore an account" and paste the Unified Full Viewing Key created in the previous step. Click "Import". + ## Funding the wallet Now you will need to fund this wallet with some ZEC. Use the Orchard address printed by the signer (see warning below). Send ZEC to that address using another account (or try [ZecFaucet](https://zecfaucet.com/)). -```admonish warning +```admonish danger The address being show by Ywallet is a unified address that includes both an Orchard and Sapling address. For the demo to work, you need to receive funds in your Orchard address. Whether that will happen depends on multiple factors so it's probably easier to use just the Orchard-only address printed by the signer. -**If you send it to the Sapling address, the funds will be unspendable and lost!** +In Ywallet, you can also swipe right on the QR Code until it shows the "Orchard +Address". **IF YOU SEND IT TO THE SAPLING ADDRESS, THE FUNDS WILL BECOME +UNSPENDABLE AND WILL BE LOST!** ``` + ## Creating the transaction Now you will create the transaction that you wish to sign with FROST. Click @@ -102,112 +230,84 @@ click the arrow button. The wallet will show the transaction plan. Click the snowflake button. It will show a QR code, but we want that information as a file, so click the floppy disk -button and save the file somewhere (e.g. `tx.raw` as suggested by Ywallet). +button and save the file somewhere (e.g. `tx-plan.json`). + ## Signing the transaction +Now you will need to simulate two participants and a Coordinator to sign the +transaction, and you should still have the FROST server running which will +handle communications between them. It's probably easier to open three new +terminals. + Go back to the signer terminal and run the following, replacing `` with the path to the file you saved in the previous step, `` with the UFVK hex string printed previously, and `` with the path where you want to write the signed transaction (e.g. `tx-signed.raw`). ``` -cargo run --release -- sign --tx-plan --ufvk -o +zcash-sign sign --tx-plan --ufvk -o ``` -The program will print a SIGHASH and a Randomizer. - - -### Running the server - -Now you will need to simulate two participants and a Coordinator to sign the -transaction, and the FROST server that handles communications between them. -It's probably easier to open four terminals. +The program will print a SIGHASH and a Randomizer, and will prompt for a +signature. This is what you will get after running FROST, so let's do that; +leave the prompt there without typing anything. -In the first one, the server, run (in the same folder where key generation was -run): -``` -RUST_LOG=debug cargo run --bin server -``` - -### Registering users +### Coordinator -In order to interact with the server, you will need to register users. For this -guide we will need two. In a new terminal, run the following command for user -"alice" (replace the password if you want): +In the second terminal, the Coordinator, run (in the same folder where you +initialized the users and ran the key generation) the following: ``` -curl --data-binary '{"username": "alice", "password": "foobar10", "pubkey": ""}' -H 'Content-Type: application/json' http://127.0.0.1:2744/register +frost-client groups -c alice.toml ``` -It will output "null". (The "pubkey" parameter is not used currently and should -be empty.) Also register user "bob": +This will list the groups Alice is in; it should only list the one you created +earlier. You will need to copy some values in the command. Run the following, +replacing the value after `` with the "Public key" listed for the group; +replacing `` and `` with the public keys of Alice and Bob (the +hexadecimal values printed next to their names; Alice's name will be empty to +indicate it's her own). ``` -curl --data-binary '{"username": "bob", "password": "foobar10", "pubkey": ""}' -H 'Content-Type: application/json' http://127.0.0.1:2744/register +frost-client coordinator -c alice.toml --server-url localhost:2744 --group -S , -m - -r - ``` -You only need to do this once, even if you want to sign more than one -transaction. If for some reason you want to start over, close the server and -delete the `db.sqlite` file. +It will prompt you for a message. Paste the SIGHASH generated with the +`zcash-sign` tool and press enter. It will then prompt for a randomizer. Paste +the one generated with the `zcash-sign` tool and press enter. -Feel free to close this terminal, or reuse it for the next step. +The tool will connect to the server and wait for the other participants. ```admonish warning -Do not use passwords that you use in practice; use dummy ones instead. (You -shouldn't reuse passwords anyway!) For real world usage you would need to take -more care to not end up writing the password to your shell history. (In real -world usage we'd expect this to be done by applications anyway.) +If you prefer to pass the message (SIGHASH) or randomizer as files by using +the `-m` and `-r` arguments, you will need to convert them to binary format. ``` -### Coordinator -In the second terminal, the Coordinator, run (in the same folder where key -generation was run): +### Participant 1 (Alice) + +In the third terminal, Participant 1, run the following (replacing `` +with the same group public key used in the previous command): ``` -export PW=foobar10 -cargo run --bin coordinator -- -C redpallas --http -u alice -w PW -S alice,bob -r - +frost-client participant -c alice.toml --server-url localhost:2744 --group ``` -We will use "alice" as the Coordinator, so change the value next to `export PW=` -if you used another password when registering "alice". - -And then: - -- It should read the public key package from `public-key-package.json`. -- When prompted for the message to be signed, paste the SIGHASH printed by the - signer above (just the hex value, e.g. - ``4d065453cfa4cfb4f98dbc9cff60c4a3904ed91c523b8ef8d67d28bea7f12ea3``). -- When prompted for the randomizer, paste the randomizer printed by the signer - above (again just the hex value) +(We are using "Alice" again. There's nothing stopping a Coordinator from being a +Partcipant too!) -```admonish warning -If you prefer to pass the randomizer as a file by using the `--randomizer` -argument, you will need to convert it to binary format. -``` -### Participant 1 (alice) +### Participant 2 (Bob) -In the third terminal, Participant 1, run the following: +In the fourth terminal, for Participant 2, run the following (replacing `` +again): ``` -export PW=foobar10 -cargo run --bin participant -- -C redpallas --http --key-package key-package-1.json -u alice -w PW +frost-client participant -c bob.toml --server-url localhost:2744 --group ``` -(We are using "alice" again. There's nothing stopping a Coordinator from being a -Participant too!) - -### Participant 2 (bob) - -In the fourth terminal, for Participant 2, run the following: - -``` -export PW=foobar10 -cargo run --bin participant -- -C redpallas --http --key-package key-package-2.json -u bob -w PW -``` ### Coordinator @@ -218,10 +318,12 @@ successfully. It will print the final FROST-generated signature. Hurrah! Copy it Go back to the signer and paste the signature. It will write the raw signed transaction to the file you specified. + ## Broadcasting the transaction -Go back to Ywallet and return to its main screen. In the menu, select "Advanced" -and "Broadcast". Select the raw signed transaction file you have just generated -(`tx-signed.raw` if you followed the suggestion). +Go back to Ywallet and return to its main screen. In the menu, select "More" and +"Broadcast". Click the upper-right box-with-an-arrow icon and select the raw +signed transaction file you have just generated (`tx-signed.raw` if you followed +the suggestion). That's it! You just sent a FROST-signed Zcash transaction. From 4d3d8822f894890e11d51b5ae4aed55cd3e50394 Mon Sep 17 00:00:00 2001 From: shigeyuki azuchi Date: Fri, 7 Feb 2025 23:09:51 +0900 Subject: [PATCH 09/86] Fix typo (#866) --- frost-secp256k1-tr/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs index f566f72..eee93fd 100644 --- a/frost-secp256k1-tr/src/lib.rs +++ b/frost-secp256k1-tr/src/lib.rs @@ -600,7 +600,7 @@ pub mod keys { /// Trait for ensuring the group public key has an even Y coordinate. /// - /// In BIP-320, public keys are encoded with only the X coordinate, which + /// In BIP-340, public keys are encoded with only the X coordinate, which /// means that two Y coordinates are possible. The specification says that /// the coordinate which is even must be used. Alternatively, something /// equivalent can be accomplished by simply converting any existing From 543e6f446a663f46f9e5a0499833261d4f867ae9 Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Tue, 11 Feb 2025 13:07:35 -0300 Subject: [PATCH 10/86] all: use OsRng instead of thread_rng() (#861) --- frost-ed25519/README.md | 3 +- frost-ed25519/benches/bench.rs | 5 +- frost-ed25519/dkg.md | 3 +- frost-ed25519/src/keys/repairable.rs | 8 +-- frost-ed25519/src/tests/batch.rs | 8 ++- .../src/tests/coefficient_commitment.rs | 7 ++- frost-ed25519/src/tests/vss_commitment.rs | 9 ++-- frost-ed25519/tests/common_traits_tests.rs | 3 +- frost-ed25519/tests/integration_tests.rs | 51 +++++++++---------- frost-ed25519/tests/interoperability_tests.rs | 13 ++--- frost-ed25519/tests/rerandomized_tests.rs | 3 +- frost-ed448/README.md | 3 +- frost-ed448/benches/bench.rs | 5 +- frost-ed448/dkg.md | 3 +- frost-ed448/src/keys/repairable.rs | 8 +-- frost-ed448/src/tests/batch.rs | 8 ++- .../src/tests/coefficient_commitment.rs | 7 ++- frost-ed448/src/tests/vss_commitment.rs | 9 ++-- frost-ed448/tests/common_traits_tests.rs | 3 +- frost-ed448/tests/integration_tests.rs | 51 +++++++++---------- frost-ed448/tests/rerandomized_tests.rs | 3 +- frost-p256/README.md | 3 +- frost-p256/benches/bench.rs | 5 +- frost-p256/dkg.md | 3 +- frost-p256/src/keys/repairable.rs | 8 +-- frost-p256/src/tests/batch.rs | 8 ++- .../src/tests/coefficient_commitment.rs | 7 ++- frost-p256/src/tests/vss_commitment.rs | 9 ++-- frost-p256/tests/common_traits_tests.rs | 3 +- frost-p256/tests/integration_tests.rs | 51 +++++++++---------- frost-p256/tests/rerandomized_tests.rs | 3 +- frost-ristretto255/README.md | 3 +- frost-ristretto255/benches/bench.rs | 5 +- frost-ristretto255/dkg.md | 3 +- frost-ristretto255/src/keys/repairable.rs | 8 +-- frost-ristretto255/src/tests/batch.rs | 8 ++- .../src/tests/coefficient_commitment.rs | 7 ++- .../src/tests/vss_commitment.rs | 9 ++-- .../tests/common_traits_tests.rs | 3 +- frost-ristretto255/tests/integration_tests.rs | 51 +++++++++---------- .../tests/rerandomized_tests.rs | 3 +- frost-secp256k1-tr/README.md | 3 +- frost-secp256k1-tr/benches/bench.rs | 5 +- frost-secp256k1-tr/dkg.md | 3 +- frost-secp256k1-tr/src/keys/repairable.rs | 8 +-- frost-secp256k1-tr/src/tests/batch.rs | 8 ++- .../src/tests/coefficient_commitment.rs | 7 ++- .../src/tests/vss_commitment.rs | 9 ++-- .../tests/common_traits_tests.rs | 3 +- frost-secp256k1-tr/tests/integration_tests.rs | 51 +++++++++---------- .../tests/interoperability_tests.rs | 13 +++-- .../tests/rerandomized_tests.rs | 3 +- frost-secp256k1-tr/tests/tweaking_tests.rs | 6 +-- frost-secp256k1/README.md | 3 +- frost-secp256k1/benches/bench.rs | 5 +- frost-secp256k1/dkg.md | 3 +- frost-secp256k1/src/keys/repairable.rs | 8 +-- frost-secp256k1/src/tests/batch.rs | 8 ++- .../src/tests/coefficient_commitment.rs | 7 ++- frost-secp256k1/src/tests/vss_commitment.rs | 9 ++-- frost-secp256k1/tests/common_traits_tests.rs | 3 +- frost-secp256k1/tests/integration_tests.rs | 51 +++++++++---------- frost-secp256k1/tests/rerandomized_tests.rs | 3 +- 63 files changed, 283 insertions(+), 349 deletions(-) diff --git a/frost-ed25519/README.md b/frost-ed25519/README.md index f9f267c..0b9a2c9 100644 --- a/frost-ed25519/README.md +++ b/frost-ed25519/README.md @@ -11,10 +11,9 @@ scenario in a single thread and it abstracts away any communication between peer ```rust # // ANCHOR: tkg_gen use frost_ed25519 as frost; -use rand::thread_rng; use std::collections::BTreeMap; -let mut rng = thread_rng(); +let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; let (shares, pubkey_package) = frost::keys::generate_with_dealer( diff --git a/frost-ed25519/benches/bench.rs b/frost-ed25519/benches/bench.rs index 04cfbfb..4317e05 100644 --- a/frost-ed25519/benches/bench.rs +++ b/frost-ed25519/benches/bench.rs @@ -1,16 +1,15 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use rand::thread_rng; use frost_ed25519::*; fn bench_ed25519_batch_verify(c: &mut Criterion) { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; frost_core::benches::bench_batch_verify::(c, "ed25519", &mut rng); } fn bench_ed25519_sign(c: &mut Criterion) { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; frost_core::benches::bench_sign::(c, "ed25519", &mut rng); } diff --git a/frost-ed25519/dkg.md b/frost-ed25519/dkg.md index 19e2433..de3f6ae 100644 --- a/frost-ed25519/dkg.md +++ b/frost-ed25519/dkg.md @@ -26,12 +26,11 @@ they can proceed to sign messages with FROST. ```rust # // ANCHOR: dkg_import -use rand::thread_rng; use std::collections::BTreeMap; use frost_ed25519 as frost; -let mut rng = thread_rng(); +let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; diff --git a/frost-ed25519/src/keys/repairable.rs b/frost-ed25519/src/keys/repairable.rs index 5e875a7..f0df91b 100644 --- a/frost-ed25519/src/keys/repairable.rs +++ b/frost-ed25519/src/keys/repairable.rs @@ -58,7 +58,7 @@ pub fn repair_share_step_3( mod tests { use lazy_static::lazy_static; - use rand::thread_rng; + use serde_json::Value; use crate::Ed25519Sha512; @@ -71,7 +71,7 @@ mod tests { #[test] fn check_repair_share_step_1() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_1::(rng); } @@ -83,7 +83,7 @@ mod tests { #[test] fn check_repair_share_step_3() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_3::( rng, &REPAIR_SHARE, @@ -92,7 +92,7 @@ mod tests { #[test] fn check_repair_share_step_1_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< Ed25519Sha512, _, diff --git a/frost-ed25519/src/tests/batch.rs b/frost-ed25519/src/tests/batch.rs index 2649793..a57301f 100644 --- a/frost-ed25519/src/tests/batch.rs +++ b/frost-ed25519/src/tests/batch.rs @@ -1,24 +1,22 @@ -use rand::thread_rng; - use crate::*; #[test] fn check_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::batch_verify::(rng); } #[test] fn check_bad_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::bad_batch_verify::(rng); } #[test] fn empty_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::empty_batch_verify::(rng); } diff --git a/frost-ed25519/src/tests/coefficient_commitment.rs b/frost-ed25519/src/tests/coefficient_commitment.rs index 113eb1c..d45994e 100644 --- a/frost-ed25519/src/tests/coefficient_commitment.rs +++ b/frost-ed25519/src/tests/coefficient_commitment.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; use crate::*; @@ -13,7 +12,7 @@ lazy_static! { #[test] fn check_serialization_of_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_serialization_of_coefficient_commitment::< Ed25519Sha512, _, @@ -22,7 +21,7 @@ fn check_serialization_of_coefficient_commitment() { #[test] fn check_create_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_create_coefficient_commitment::< Ed25519Sha512, _, @@ -37,7 +36,7 @@ fn check_create_coefficient_commitment_error() { #[test] fn check_get_value_of_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_get_value_of_coefficient_commitment::< Ed25519Sha512, diff --git a/frost-ed25519/src/tests/vss_commitment.rs b/frost-ed25519/src/tests/vss_commitment.rs index 2bd1bae..c1c128f 100644 --- a/frost-ed25519/src/tests/vss_commitment.rs +++ b/frost-ed25519/src/tests/vss_commitment.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; use crate::*; @@ -13,19 +12,19 @@ lazy_static! { #[test] fn check_serialize_vss_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); } #[test] fn check_deserialize_vss_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment::(rng); } #[test] fn check_deserialize_vss_commitment_error() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment_error::( rng, &ELEMENTS, ); @@ -33,6 +32,6 @@ fn check_deserialize_vss_commitment_error() { #[test] fn check_compute_public_key_package() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_compute_public_key_package::(rng); } diff --git a/frost-ed25519/tests/common_traits_tests.rs b/frost-ed25519/tests/common_traits_tests.rs index 9336302..db17392 100644 --- a/frost-ed25519/tests/common_traits_tests.rs +++ b/frost-ed25519/tests/common_traits_tests.rs @@ -4,7 +4,6 @@ mod helpers; use frost_ed25519::SigningKey; use helpers::samples; -use rand::thread_rng; #[allow(clippy::unnecessary_literal_unwrap)] fn check_common_traits_for_type(v: T) { @@ -20,7 +19,7 @@ fn check_common_traits_for_type(v: #[test] fn check_signing_key_common_traits() { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; let signing_key = SigningKey::new(&mut rng); check_common_traits_for_type(signing_key); } diff --git a/frost-ed25519/tests/integration_tests.rs b/frost-ed25519/tests/integration_tests.rs index c8c27fc..d7b5d16 100644 --- a/frost-ed25519/tests/integration_tests.rs +++ b/frost-ed25519/tests/integration_tests.rs @@ -1,6 +1,5 @@ use frost_ed25519::*; use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; #[test] @@ -10,14 +9,14 @@ fn check_zero_key_fails() { #[test] fn check_sign_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dkg::(rng); } #[test] fn check_dkg_part1_fails_with_invalid_signers_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 1; let max_signers = 3; @@ -31,7 +30,7 @@ fn check_dkg_part1_fails_with_invalid_signers_min_signers() { #[test] fn check_dkg_part1_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -45,7 +44,7 @@ fn check_dkg_part1_fails_with_min_signers_greater_than_max() { #[test] fn check_dkg_part1_fails_with_invalid_signers_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 1; @@ -59,21 +58,21 @@ fn check_dkg_part1_fails_with_invalid_signers_max_signers() { #[test] fn check_rts() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_rts::(rng); } #[test] fn check_refresh_shares_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); } #[test] fn check_refresh_shares_with_dealer_serialisation() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::( rng, @@ -82,7 +81,7 @@ fn check_refresh_shares_with_dealer_serialisation() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_public_key_package::< Ed25519Sha512, @@ -92,7 +91,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -111,7 +110,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { #[test] fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -130,7 +129,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s #[test] fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -149,7 +148,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; let min_signers = 3; let max_signers = 1; @@ -163,7 +162,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(8).unwrap(), Identifier::try_from(3).unwrap(), @@ -182,21 +181,21 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { #[test] fn check_refresh_shares_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dkg::(rng); } #[test] fn check_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dealer::(rng); } #[test] fn check_sign_with_dealer_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 1; let max_signers = 3; @@ -210,7 +209,7 @@ fn check_sign_with_dealer_fails_with_invalid_min_signers() { #[test] fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -224,7 +223,7 @@ fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { #[test] fn check_sign_with_dealer_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 1; @@ -240,13 +239,13 @@ fn check_sign_with_dealer_fails_with_invalid_max_signers() { /// value is working. #[test] fn check_share_generation_ed25519_sha512() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_share_generation::(rng); } #[test] fn check_share_generation_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 0; let max_signers = 3; @@ -260,7 +259,7 @@ fn check_share_generation_fails_with_invalid_min_signers() { #[test] fn check_share_generation_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -274,7 +273,7 @@ fn check_share_generation_fails_with_min_signers_greater_than_max() { #[test] fn check_share_generation_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 0; @@ -338,7 +337,7 @@ fn check_identifier_generation() -> Result<(), Error> { #[test] fn check_sign_with_dealer_and_identifiers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::< Ed25519Sha512, @@ -348,7 +347,7 @@ fn check_sign_with_dealer_and_identifiers() { #[test] fn check_sign_with_missing_identifier() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_missing_identifier::( rng, ); @@ -356,7 +355,7 @@ fn check_sign_with_missing_identifier() { #[test] fn check_sign_with_incorrect_commitments() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_incorrect_commitments::( rng, ); diff --git a/frost-ed25519/tests/interoperability_tests.rs b/frost-ed25519/tests/interoperability_tests.rs index 9c27193..e758ee2 100644 --- a/frost-ed25519/tests/interoperability_tests.rs +++ b/frost-ed25519/tests/interoperability_tests.rs @@ -1,21 +1,18 @@ use crate::Ed25519Sha512; use frost_ed25519::*; -use rand::thread_rng; mod helpers; #[test] fn check_interoperability_in_sign_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; // Test with multiple keys/signatures to better exercise the key generation // and the interoperability check. A smaller number of iterations is used // because DKG takes longer and otherwise the test would be too slow. for _ in 0..32 { let (msg, group_signature, group_pubkey) = - frost_core::tests::ciphersuite_generic::check_sign_with_dkg::( - rng.clone(), - ); + frost_core::tests::ciphersuite_generic::check_sign_with_dkg::(rng); helpers::verify_signature(&msg, group_signature, group_pubkey); } @@ -23,15 +20,13 @@ fn check_interoperability_in_sign_with_dkg() { #[test] fn check_interoperability_in_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; // Test with multiple keys/signatures to better exercise the key generation // and the interoperability check. for _ in 0..256 { let (msg, group_signature, group_pubkey) = - frost_core::tests::ciphersuite_generic::check_sign_with_dealer::( - rng.clone(), - ); + frost_core::tests::ciphersuite_generic::check_sign_with_dealer::(rng); // Check that the threshold signature can be verified by the `ed25519_dalek` crate // public key (interoperability test) diff --git a/frost-ed25519/tests/rerandomized_tests.rs b/frost-ed25519/tests/rerandomized_tests.rs index e6981bd..1217387 100644 --- a/frost-ed25519/tests/rerandomized_tests.rs +++ b/frost-ed25519/tests/rerandomized_tests.rs @@ -1,9 +1,8 @@ use frost_ed25519::Ed25519Sha512; -use rand::thread_rng; #[test] fn check_randomized_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let (_msg, _group_signature, _group_pubkey) = frost_rerandomized::tests::check_randomized_sign_with_dealer::(rng); diff --git a/frost-ed448/README.md b/frost-ed448/README.md index 7cf30ff..34d4e53 100644 --- a/frost-ed448/README.md +++ b/frost-ed448/README.md @@ -11,10 +11,9 @@ scenario in a single thread and it abstracts away any communication between peer ```rust # // ANCHOR: tkg_gen use frost_ed448 as frost; -use rand::thread_rng; use std::collections::BTreeMap; -let mut rng = thread_rng(); +let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; let (shares, pubkey_package) = frost::keys::generate_with_dealer( diff --git a/frost-ed448/benches/bench.rs b/frost-ed448/benches/bench.rs index 343f72f..ba40feb 100644 --- a/frost-ed448/benches/bench.rs +++ b/frost-ed448/benches/bench.rs @@ -1,18 +1,17 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use rand::thread_rng; use frost_ed448::*; // bench_ed448_batch_verify not included until batch verification is fixed for Ed448 #[allow(unused)] fn bench_ed448_batch_verify(c: &mut Criterion) { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; frost_core::benches::bench_batch_verify::(c, "ed448", &mut rng); } fn bench_ed448_sign(c: &mut Criterion) { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; frost_core::benches::bench_sign::(c, "ed448", &mut rng); } diff --git a/frost-ed448/dkg.md b/frost-ed448/dkg.md index 6471d76..0399378 100644 --- a/frost-ed448/dkg.md +++ b/frost-ed448/dkg.md @@ -26,12 +26,11 @@ they can proceed to sign messages with FROST. ```rust # // ANCHOR: dkg_import -use rand::thread_rng; use std::collections::BTreeMap; use frost_ed448 as frost; -let mut rng = thread_rng(); +let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; diff --git a/frost-ed448/src/keys/repairable.rs b/frost-ed448/src/keys/repairable.rs index 97771f3..7bbdeca 100644 --- a/frost-ed448/src/keys/repairable.rs +++ b/frost-ed448/src/keys/repairable.rs @@ -58,7 +58,7 @@ pub fn repair_share_step_3( mod tests { use lazy_static::lazy_static; - use rand::thread_rng; + use serde_json::Value; use crate::Ed448Shake256; @@ -71,7 +71,7 @@ mod tests { #[test] fn check_repair_share_step_1() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_1::(rng); } @@ -83,7 +83,7 @@ mod tests { #[test] fn check_repair_share_step_3() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_3::( rng, &REPAIR_SHARE, @@ -92,7 +92,7 @@ mod tests { #[test] fn check_repair_share_step_1_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< Ed448Shake256, _, diff --git a/frost-ed448/src/tests/batch.rs b/frost-ed448/src/tests/batch.rs index 85b6b1a..5c84b5e 100644 --- a/frost-ed448/src/tests/batch.rs +++ b/frost-ed448/src/tests/batch.rs @@ -1,24 +1,22 @@ -use rand::thread_rng; - use crate::*; #[test] fn check_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::batch_verify::(rng); } #[test] fn check_bad_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::bad_batch_verify::(rng); } #[test] fn empty_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::empty_batch_verify::(rng); } diff --git a/frost-ed448/src/tests/coefficient_commitment.rs b/frost-ed448/src/tests/coefficient_commitment.rs index d088ad1..0d2091a 100644 --- a/frost-ed448/src/tests/coefficient_commitment.rs +++ b/frost-ed448/src/tests/coefficient_commitment.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; use crate::*; @@ -13,7 +12,7 @@ lazy_static! { #[test] fn check_serialization_of_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_serialization_of_coefficient_commitment::< Ed448Shake256, _, @@ -22,7 +21,7 @@ fn check_serialization_of_coefficient_commitment() { #[test] fn check_create_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_create_coefficient_commitment::< Ed448Shake256, _, @@ -37,7 +36,7 @@ fn check_create_coefficient_commitment_error() { #[test] fn check_get_value_of_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_get_value_of_coefficient_commitment::< Ed448Shake256, diff --git a/frost-ed448/src/tests/vss_commitment.rs b/frost-ed448/src/tests/vss_commitment.rs index 98810b3..c5beea6 100644 --- a/frost-ed448/src/tests/vss_commitment.rs +++ b/frost-ed448/src/tests/vss_commitment.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; use crate::*; @@ -13,19 +12,19 @@ lazy_static! { #[test] fn check_serialize_vss_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); } #[test] fn check_deserialize_vss_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment::(rng); } #[test] fn check_deserialize_vss_commitment_error() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment_error::( rng, &ELEMENTS, ); @@ -33,6 +32,6 @@ fn check_deserialize_vss_commitment_error() { #[test] fn check_compute_public_key_package() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_compute_public_key_package::(rng); } diff --git a/frost-ed448/tests/common_traits_tests.rs b/frost-ed448/tests/common_traits_tests.rs index 44f389a..749cc70 100644 --- a/frost-ed448/tests/common_traits_tests.rs +++ b/frost-ed448/tests/common_traits_tests.rs @@ -4,7 +4,6 @@ mod helpers; use frost_ed448::SigningKey; use helpers::samples; -use rand::thread_rng; #[allow(clippy::unnecessary_literal_unwrap)] fn check_common_traits_for_type(v: T) { @@ -20,7 +19,7 @@ fn check_common_traits_for_type(v: #[test] fn check_signing_key_common_traits() { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; let signing_key = SigningKey::new(&mut rng); check_common_traits_for_type(signing_key); } diff --git a/frost-ed448/tests/integration_tests.rs b/frost-ed448/tests/integration_tests.rs index b96d832..872051a 100644 --- a/frost-ed448/tests/integration_tests.rs +++ b/frost-ed448/tests/integration_tests.rs @@ -1,6 +1,5 @@ use frost_ed448::*; use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; #[test] @@ -10,14 +9,14 @@ fn check_zero_key_fails() { #[test] fn check_sign_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dkg::(rng); } #[test] fn check_dkg_part1_fails_with_invalid_signers_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 1; let max_signers = 3; @@ -31,7 +30,7 @@ fn check_dkg_part1_fails_with_invalid_signers_min_signers() { #[test] fn check_dkg_part1_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -45,7 +44,7 @@ fn check_dkg_part1_fails_with_min_signers_greater_than_max() { #[test] fn check_dkg_part1_fails_with_invalid_signers_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 1; @@ -59,21 +58,21 @@ fn check_dkg_part1_fails_with_invalid_signers_max_signers() { #[test] fn check_rts() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_rts::(rng); } #[test] fn check_refresh_shares_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); } #[test] fn check_refresh_shares_with_dealer_serialisation() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::( rng, @@ -82,7 +81,7 @@ fn check_refresh_shares_with_dealer_serialisation() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_public_key_package::< Ed448Shake256, @@ -92,7 +91,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -111,7 +110,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { #[test] fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -130,7 +129,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s #[test] fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -149,7 +148,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; let min_signers = 3; let max_signers = 1; @@ -163,7 +162,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(8).unwrap(), Identifier::try_from(3).unwrap(), @@ -182,21 +181,21 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { #[test] fn check_refresh_shares_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dkg::(rng); } #[test] fn check_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dealer::(rng); } #[test] fn check_sign_with_dealer_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 1; let max_signers = 3; @@ -210,7 +209,7 @@ fn check_sign_with_dealer_fails_with_invalid_min_signers() { #[test] fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -224,7 +223,7 @@ fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { #[test] fn check_sign_with_dealer_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 1; @@ -240,13 +239,13 @@ fn check_sign_with_dealer_fails_with_invalid_max_signers() { /// value is working. #[test] fn check_share_generation_ed448_shake256() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_share_generation::(rng); } #[test] fn check_share_generation_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 0; let max_signers = 3; @@ -260,7 +259,7 @@ fn check_share_generation_fails_with_invalid_min_signers() { #[test] fn check_share_generation_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -274,7 +273,7 @@ fn check_share_generation_fails_with_min_signers_greater_than_max() { #[test] fn check_share_generation_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 0; @@ -338,7 +337,7 @@ fn check_identifier_generation() -> Result<(), Error> { #[test] fn check_sign_with_dealer_and_identifiers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::< Ed448Shake256, @@ -348,7 +347,7 @@ fn check_sign_with_dealer_and_identifiers() { #[test] fn check_sign_with_missing_identifier() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_missing_identifier::( rng, ); @@ -356,7 +355,7 @@ fn check_sign_with_missing_identifier() { #[test] fn check_sign_with_incorrect_commitments() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_incorrect_commitments::( rng, ); diff --git a/frost-ed448/tests/rerandomized_tests.rs b/frost-ed448/tests/rerandomized_tests.rs index 16e31f2..e16d906 100644 --- a/frost-ed448/tests/rerandomized_tests.rs +++ b/frost-ed448/tests/rerandomized_tests.rs @@ -1,9 +1,8 @@ use frost_ed448::Ed448Shake256; -use rand::thread_rng; #[test] fn check_randomized_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let (_msg, _group_signature, _group_pubkey) = frost_rerandomized::tests::check_randomized_sign_with_dealer::(rng); diff --git a/frost-p256/README.md b/frost-p256/README.md index df0cc4a..ce0aa3d 100644 --- a/frost-p256/README.md +++ b/frost-p256/README.md @@ -11,10 +11,9 @@ scenario in a single thread and it abstracts away any communication between peer ```rust # // ANCHOR: tkg_gen use frost_p256 as frost; -use rand::thread_rng; use std::collections::BTreeMap; -let mut rng = thread_rng(); +let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; let (shares, pubkey_package) = frost::keys::generate_with_dealer( diff --git a/frost-p256/benches/bench.rs b/frost-p256/benches/bench.rs index 1a4d835..8ae524f 100644 --- a/frost-p256/benches/bench.rs +++ b/frost-p256/benches/bench.rs @@ -1,16 +1,15 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use rand::thread_rng; use frost_p256::*; fn bench_p256_batch_verify(c: &mut Criterion) { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; frost_core::benches::bench_batch_verify::(c, "p256", &mut rng); } fn bench_p256_sign(c: &mut Criterion) { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; frost_core::benches::bench_sign::(c, "p256", &mut rng); } diff --git a/frost-p256/dkg.md b/frost-p256/dkg.md index f3dbb23..afb4bd4 100644 --- a/frost-p256/dkg.md +++ b/frost-p256/dkg.md @@ -26,12 +26,11 @@ they can proceed to sign messages with FROST. ```rust # // ANCHOR: dkg_import -use rand::thread_rng; use std::collections::BTreeMap; use frost_p256 as frost; -let mut rng = thread_rng(); +let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; diff --git a/frost-p256/src/keys/repairable.rs b/frost-p256/src/keys/repairable.rs index 2337574..484a9fc 100644 --- a/frost-p256/src/keys/repairable.rs +++ b/frost-p256/src/keys/repairable.rs @@ -58,7 +58,7 @@ pub fn repair_share_step_3( mod tests { use lazy_static::lazy_static; - use rand::thread_rng; + use serde_json::Value; use crate::P256Sha256; @@ -71,7 +71,7 @@ mod tests { #[test] fn check_repair_share_step_1() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_1::(rng); } @@ -83,7 +83,7 @@ mod tests { #[test] fn check_repair_share_step_3() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_3::( rng, &REPAIR_SHARE, @@ -92,7 +92,7 @@ mod tests { #[test] fn check_repair_share_step_1_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< P256Sha256, _, diff --git a/frost-p256/src/tests/batch.rs b/frost-p256/src/tests/batch.rs index 1d4cff1..3a46bfd 100644 --- a/frost-p256/src/tests/batch.rs +++ b/frost-p256/src/tests/batch.rs @@ -1,24 +1,22 @@ -use rand::thread_rng; - use crate::*; #[test] fn check_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::batch_verify::(rng); } #[test] fn check_bad_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::bad_batch_verify::(rng); } #[test] fn empty_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::empty_batch_verify::(rng); } diff --git a/frost-p256/src/tests/coefficient_commitment.rs b/frost-p256/src/tests/coefficient_commitment.rs index 8083aea..e52f839 100644 --- a/frost-p256/src/tests/coefficient_commitment.rs +++ b/frost-p256/src/tests/coefficient_commitment.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; use crate::*; @@ -13,7 +12,7 @@ lazy_static! { #[test] fn check_serialization_of_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_serialization_of_coefficient_commitment::< P256Sha256, _, @@ -22,7 +21,7 @@ fn check_serialization_of_coefficient_commitment() { #[test] fn check_create_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_create_coefficient_commitment::( rng, ); @@ -36,7 +35,7 @@ fn check_create_coefficient_commitment_error() { #[test] fn check_get_value_of_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_get_value_of_coefficient_commitment::< P256Sha256, diff --git a/frost-p256/src/tests/vss_commitment.rs b/frost-p256/src/tests/vss_commitment.rs index a30d3f6..2445791 100644 --- a/frost-p256/src/tests/vss_commitment.rs +++ b/frost-p256/src/tests/vss_commitment.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; use crate::*; @@ -13,19 +12,19 @@ lazy_static! { #[test] fn check_serialize_vss_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); } #[test] fn check_deserialize_vss_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment::(rng); } #[test] fn check_deserialize_vss_commitment_error() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment_error::( rng, &ELEMENTS, ); @@ -33,6 +32,6 @@ fn check_deserialize_vss_commitment_error() { #[test] fn check_compute_public_key_package() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_compute_public_key_package::(rng); } diff --git a/frost-p256/tests/common_traits_tests.rs b/frost-p256/tests/common_traits_tests.rs index 16f52d5..8dc2d73 100644 --- a/frost-p256/tests/common_traits_tests.rs +++ b/frost-p256/tests/common_traits_tests.rs @@ -4,7 +4,6 @@ mod helpers; use frost_p256::SigningKey; use helpers::samples; -use rand::thread_rng; #[allow(clippy::unnecessary_literal_unwrap)] fn check_common_traits_for_type(v: T) { @@ -20,7 +19,7 @@ fn check_common_traits_for_type(v: #[test] fn check_signing_key_common_traits() { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; let signing_key = SigningKey::new(&mut rng); check_common_traits_for_type(signing_key); } diff --git a/frost-p256/tests/integration_tests.rs b/frost-p256/tests/integration_tests.rs index acd3f81..33e4c20 100644 --- a/frost-p256/tests/integration_tests.rs +++ b/frost-p256/tests/integration_tests.rs @@ -1,6 +1,5 @@ use frost_p256::*; use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; #[test] @@ -10,14 +9,14 @@ fn check_zero_key_fails() { #[test] fn check_sign_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dkg::(rng); } #[test] fn check_dkg_part1_fails_with_invalid_signers_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 1; let max_signers = 3; @@ -31,7 +30,7 @@ fn check_dkg_part1_fails_with_invalid_signers_min_signers() { #[test] fn check_dkg_part1_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -45,7 +44,7 @@ fn check_dkg_part1_fails_with_min_signers_greater_than_max() { #[test] fn check_dkg_part1_fails_with_invalid_signers_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 1; @@ -59,21 +58,21 @@ fn check_dkg_part1_fails_with_invalid_signers_max_signers() { #[test] fn check_rts() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_rts::(rng); } #[test] fn check_refresh_shares_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); } #[test] fn check_refresh_shares_with_dealer_serialisation() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::( rng, @@ -82,7 +81,7 @@ fn check_refresh_shares_with_dealer_serialisation() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_public_key_package::< P256Sha256, @@ -92,7 +91,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -111,7 +110,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { #[test] fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -130,7 +129,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s #[test] fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -149,7 +148,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; let min_signers = 3; let max_signers = 1; @@ -163,7 +162,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(8).unwrap(), Identifier::try_from(3).unwrap(), @@ -182,21 +181,21 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { #[test] fn check_refresh_shares_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dkg::(rng); } #[test] fn check_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dealer::(rng); } #[test] fn check_sign_with_dealer_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 1; let max_signers = 3; @@ -210,7 +209,7 @@ fn check_sign_with_dealer_fails_with_invalid_min_signers() { #[test] fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -224,7 +223,7 @@ fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { #[test] fn check_sign_with_dealer_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 1; @@ -240,13 +239,13 @@ fn check_sign_with_dealer_fails_with_invalid_max_signers() { /// value is working. #[test] fn check_share_generation_p256_sha256() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_share_generation::(rng); } #[test] fn check_share_generation_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 0; let max_signers = 3; @@ -260,7 +259,7 @@ fn check_share_generation_fails_with_invalid_min_signers() { #[test] fn check_share_generation_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -274,7 +273,7 @@ fn check_share_generation_fails_with_min_signers_greater_than_max() { #[test] fn check_share_generation_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 0; @@ -336,7 +335,7 @@ fn check_identifier_generation() -> Result<(), Error> { #[test] fn check_sign_with_dealer_and_identifiers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::( rng, @@ -345,7 +344,7 @@ fn check_sign_with_dealer_and_identifiers() { #[test] fn check_sign_with_missing_identifier() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_missing_identifier::( rng, ); @@ -353,7 +352,7 @@ fn check_sign_with_missing_identifier() { #[test] fn check_sign_with_incorrect_commitments() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_incorrect_commitments::( rng, ); diff --git a/frost-p256/tests/rerandomized_tests.rs b/frost-p256/tests/rerandomized_tests.rs index d16a0c3..6dc482c 100644 --- a/frost-p256/tests/rerandomized_tests.rs +++ b/frost-p256/tests/rerandomized_tests.rs @@ -1,9 +1,8 @@ use frost_p256::P256Sha256; -use rand::thread_rng; #[test] fn check_randomized_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let (_msg, _group_signature, _group_pubkey) = frost_rerandomized::tests::check_randomized_sign_with_dealer::(rng); diff --git a/frost-ristretto255/README.md b/frost-ristretto255/README.md index a2d2842..4ba16fe 100644 --- a/frost-ristretto255/README.md +++ b/frost-ristretto255/README.md @@ -11,10 +11,9 @@ scenario in a single thread and it abstracts away any communication between peer ```rust # // ANCHOR: tkg_gen use frost_ristretto255 as frost; -use rand::thread_rng; use std::collections::BTreeMap; -let mut rng = thread_rng(); +let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; let (shares, pubkey_package) = frost::keys::generate_with_dealer( diff --git a/frost-ristretto255/benches/bench.rs b/frost-ristretto255/benches/bench.rs index b53e560..b7e9af3 100644 --- a/frost-ristretto255/benches/bench.rs +++ b/frost-ristretto255/benches/bench.rs @@ -1,16 +1,15 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use rand::thread_rng; use frost_ristretto255::*; fn bench_ristretto255_batch_verify(c: &mut Criterion) { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; frost_core::benches::bench_batch_verify::(c, "ristretto255", &mut rng); } fn bench_ristretto255_sign(c: &mut Criterion) { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; frost_core::benches::bench_sign::(c, "ristretto255", &mut rng); } diff --git a/frost-ristretto255/dkg.md b/frost-ristretto255/dkg.md index 18954d4..86995d7 100644 --- a/frost-ristretto255/dkg.md +++ b/frost-ristretto255/dkg.md @@ -26,12 +26,11 @@ they can proceed to sign messages with FROST. ```rust # // ANCHOR: dkg_import -use rand::thread_rng; use std::collections::BTreeMap; use frost_ristretto255 as frost; -let mut rng = thread_rng(); +let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; diff --git a/frost-ristretto255/src/keys/repairable.rs b/frost-ristretto255/src/keys/repairable.rs index a935eb8..e3d20a7 100644 --- a/frost-ristretto255/src/keys/repairable.rs +++ b/frost-ristretto255/src/keys/repairable.rs @@ -58,7 +58,7 @@ pub fn repair_share_step_3( mod tests { use lazy_static::lazy_static; - use rand::thread_rng; + use serde_json::Value; use crate::Ristretto255Sha512; @@ -71,7 +71,7 @@ mod tests { #[test] fn check_repair_share_step_1() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_1::(rng); } @@ -85,7 +85,7 @@ mod tests { #[test] fn check_repair_share_step_3() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_3::( rng, &REPAIR_SHARE, @@ -94,7 +94,7 @@ mod tests { #[test] fn check_repair_share_step_1_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< Ristretto255Sha512, _, diff --git a/frost-ristretto255/src/tests/batch.rs b/frost-ristretto255/src/tests/batch.rs index 9c08b77..b26d033 100644 --- a/frost-ristretto255/src/tests/batch.rs +++ b/frost-ristretto255/src/tests/batch.rs @@ -1,24 +1,22 @@ -use rand::thread_rng; - use crate::*; #[test] fn check_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::batch_verify::(rng); } #[test] fn check_bad_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::bad_batch_verify::(rng); } #[test] fn empty_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::empty_batch_verify::(rng); } diff --git a/frost-ristretto255/src/tests/coefficient_commitment.rs b/frost-ristretto255/src/tests/coefficient_commitment.rs index e5df351..a031d4c 100644 --- a/frost-ristretto255/src/tests/coefficient_commitment.rs +++ b/frost-ristretto255/src/tests/coefficient_commitment.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; use crate::*; @@ -13,7 +12,7 @@ lazy_static! { #[test] fn check_serialization_of_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_serialization_of_coefficient_commitment::< Ristretto255Sha512, _, @@ -22,7 +21,7 @@ fn check_serialization_of_coefficient_commitment() { #[test] fn check_create_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_create_coefficient_commitment::< Ristretto255Sha512, _, @@ -37,7 +36,7 @@ fn check_create_coefficient_commitment_error() { #[test] fn check_get_value_of_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_get_value_of_coefficient_commitment::< Ristretto255Sha512, diff --git a/frost-ristretto255/src/tests/vss_commitment.rs b/frost-ristretto255/src/tests/vss_commitment.rs index d7acaaa..66c6d37 100644 --- a/frost-ristretto255/src/tests/vss_commitment.rs +++ b/frost-ristretto255/src/tests/vss_commitment.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; use crate::*; @@ -13,13 +12,13 @@ lazy_static! { #[test] fn check_serialize_vss_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); } #[test] fn check_deserialize_vss_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment::( rng, ); @@ -27,7 +26,7 @@ fn check_deserialize_vss_commitment() { #[test] fn check_deserialize_vss_commitment_error() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment_error::< Ristretto255Sha512, _, @@ -36,7 +35,7 @@ fn check_deserialize_vss_commitment_error() { #[test] fn check_compute_public_key_package() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_compute_public_key_package::( rng, ); diff --git a/frost-ristretto255/tests/common_traits_tests.rs b/frost-ristretto255/tests/common_traits_tests.rs index a985d0a..8d1fcf2 100644 --- a/frost-ristretto255/tests/common_traits_tests.rs +++ b/frost-ristretto255/tests/common_traits_tests.rs @@ -4,7 +4,6 @@ mod helpers; use frost_ristretto255::SigningKey; use helpers::samples; -use rand::thread_rng; #[allow(clippy::unnecessary_literal_unwrap)] fn check_common_traits_for_type(v: T) { @@ -20,7 +19,7 @@ fn check_common_traits_for_type(v: #[test] fn check_signing_key_common_traits() { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; let signing_key = SigningKey::new(&mut rng); check_common_traits_for_type(signing_key); } diff --git a/frost-ristretto255/tests/integration_tests.rs b/frost-ristretto255/tests/integration_tests.rs index 7436202..93a5574 100644 --- a/frost-ristretto255/tests/integration_tests.rs +++ b/frost-ristretto255/tests/integration_tests.rs @@ -1,6 +1,5 @@ use frost_ristretto255::*; use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; #[test] @@ -10,14 +9,14 @@ fn check_zero_key_fails() { #[test] fn check_sign_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dkg::(rng); } #[test] fn check_dkg_part1_fails_with_invalid_signers_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 1; let max_signers = 3; @@ -31,7 +30,7 @@ fn check_dkg_part1_fails_with_invalid_signers_min_signers() { #[test] fn check_dkg_part1_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -45,7 +44,7 @@ fn check_dkg_part1_fails_with_min_signers_greater_than_max() { #[test] fn check_dkg_part1_fails_with_invalid_signers_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 1; @@ -59,21 +58,21 @@ fn check_dkg_part1_fails_with_invalid_signers_max_signers() { #[test] fn check_rts() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_rts::(rng); } #[test] fn check_refresh_shares_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); } #[test] fn check_refresh_shares_with_dealer_serialisation() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::< Ristretto255Sha512, @@ -83,7 +82,7 @@ fn check_refresh_shares_with_dealer_serialisation() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_public_key_package::< Ristretto255Sha512, @@ -93,7 +92,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -112,7 +111,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { #[test] fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -131,7 +130,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s #[test] fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -150,7 +149,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; let min_signers = 3; let max_signers = 1; @@ -164,7 +163,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(8).unwrap(), Identifier::try_from(3).unwrap(), @@ -183,21 +182,21 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { #[test] fn check_refresh_shares_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dkg::(rng); } #[test] fn check_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dealer::(rng); } #[test] fn check_sign_with_dealer_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 1; let max_signers = 3; @@ -211,7 +210,7 @@ fn check_sign_with_dealer_fails_with_invalid_min_signers() { #[test] fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -225,7 +224,7 @@ fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { #[test] fn check_sign_with_dealer_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 1; @@ -241,13 +240,13 @@ fn check_sign_with_dealer_fails_with_invalid_max_signers() { /// value is working. #[test] fn check_share_generation_ristretto255_sha512() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_share_generation::(rng); } #[test] fn check_share_generation_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 0; let max_signers = 3; @@ -261,7 +260,7 @@ fn check_share_generation_fails_with_invalid_min_signers() { #[test] fn check_share_generation_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -275,7 +274,7 @@ fn check_share_generation_fails_with_min_signers_greater_than_max() { #[test] fn check_share_generation_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 0; @@ -339,7 +338,7 @@ fn check_identifier_generation() -> Result<(), Error> { #[test] fn check_sign_with_dealer_and_identifiers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::< Ristretto255Sha512, @@ -349,7 +348,7 @@ fn check_sign_with_dealer_and_identifiers() { #[test] fn check_sign_with_missing_identifier() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_missing_identifier::< Ristretto255Sha512, _, @@ -358,7 +357,7 @@ fn check_sign_with_missing_identifier() { #[test] fn check_sign_with_incorrect_commitments() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_incorrect_commitments::< Ristretto255Sha512, _, diff --git a/frost-ristretto255/tests/rerandomized_tests.rs b/frost-ristretto255/tests/rerandomized_tests.rs index a7a884c..23277d0 100644 --- a/frost-ristretto255/tests/rerandomized_tests.rs +++ b/frost-ristretto255/tests/rerandomized_tests.rs @@ -1,9 +1,8 @@ use frost_ristretto255::Ristretto255Sha512; -use rand::thread_rng; #[test] fn check_randomized_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let (_msg, _group_signature, _group_pubkey) = frost_rerandomized::tests::check_randomized_sign_with_dealer::(rng); diff --git a/frost-secp256k1-tr/README.md b/frost-secp256k1-tr/README.md index f4d2205..26e2870 100644 --- a/frost-secp256k1-tr/README.md +++ b/frost-secp256k1-tr/README.md @@ -11,10 +11,9 @@ scenario in a single thread and it abstracts away any communication between peer ```rust # // ANCHOR: tkg_gen use frost_secp256k1_tr as frost; -use rand::thread_rng; use std::collections::BTreeMap; -let mut rng = thread_rng(); +let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; let (shares, pubkey_package) = frost::keys::generate_with_dealer( diff --git a/frost-secp256k1-tr/benches/bench.rs b/frost-secp256k1-tr/benches/bench.rs index e9097bd..d2ce56f 100644 --- a/frost-secp256k1-tr/benches/bench.rs +++ b/frost-secp256k1-tr/benches/bench.rs @@ -1,16 +1,15 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use rand::thread_rng; use frost_secp256k1_tr::*; fn bench_secp256k1_batch_verify(c: &mut Criterion) { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; frost_core::benches::bench_batch_verify::(c, "secp256k1", &mut rng); } fn bench_secp256k1_sign(c: &mut Criterion) { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; frost_core::benches::bench_sign::(c, "secp256k1", &mut rng); } diff --git a/frost-secp256k1-tr/dkg.md b/frost-secp256k1-tr/dkg.md index 9643d37..31a96ac 100644 --- a/frost-secp256k1-tr/dkg.md +++ b/frost-secp256k1-tr/dkg.md @@ -26,12 +26,11 @@ they can proceed to sign messages with FROST. ```rust # // ANCHOR: dkg_import -use rand::thread_rng; use std::collections::BTreeMap; use frost_secp256k1_tr as frost; -let mut rng = thread_rng(); +let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; diff --git a/frost-secp256k1-tr/src/keys/repairable.rs b/frost-secp256k1-tr/src/keys/repairable.rs index 9b53803..4454335 100644 --- a/frost-secp256k1-tr/src/keys/repairable.rs +++ b/frost-secp256k1-tr/src/keys/repairable.rs @@ -58,7 +58,7 @@ pub fn repair_share_step_3( mod tests { use lazy_static::lazy_static; - use rand::thread_rng; + use serde_json::Value; use crate::Secp256K1Sha256TR; @@ -71,7 +71,7 @@ mod tests { #[test] fn check_repair_share_step_1() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_1::(rng); } @@ -85,7 +85,7 @@ mod tests { #[test] fn check_repair_share_step_3() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_3::( rng, &REPAIR_SHARE, @@ -94,7 +94,7 @@ mod tests { #[test] fn check_repair_share_step_1_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< Secp256K1Sha256TR, _, diff --git a/frost-secp256k1-tr/src/tests/batch.rs b/frost-secp256k1-tr/src/tests/batch.rs index f88793a..d22efdf 100644 --- a/frost-secp256k1-tr/src/tests/batch.rs +++ b/frost-secp256k1-tr/src/tests/batch.rs @@ -1,24 +1,22 @@ -use rand::thread_rng; - use crate::*; #[test] fn check_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::batch_verify::(rng); } #[test] fn check_bad_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::bad_batch_verify::(rng); } #[test] fn empty_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::empty_batch_verify::(rng); } diff --git a/frost-secp256k1-tr/src/tests/coefficient_commitment.rs b/frost-secp256k1-tr/src/tests/coefficient_commitment.rs index a63259c..71706ea 100644 --- a/frost-secp256k1-tr/src/tests/coefficient_commitment.rs +++ b/frost-secp256k1-tr/src/tests/coefficient_commitment.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; use crate::*; @@ -13,7 +12,7 @@ lazy_static! { #[test] fn check_serialization_of_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_serialization_of_coefficient_commitment::< Secp256K1Sha256TR, _, @@ -22,7 +21,7 @@ fn check_serialization_of_coefficient_commitment() { #[test] fn check_create_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_create_coefficient_commitment::< Secp256K1Sha256TR, _, @@ -37,7 +36,7 @@ fn check_create_coefficient_commitment_error() { #[test] fn check_get_value_of_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_get_value_of_coefficient_commitment::< Secp256K1Sha256TR, diff --git a/frost-secp256k1-tr/src/tests/vss_commitment.rs b/frost-secp256k1-tr/src/tests/vss_commitment.rs index 80fb1ca..0a09068 100644 --- a/frost-secp256k1-tr/src/tests/vss_commitment.rs +++ b/frost-secp256k1-tr/src/tests/vss_commitment.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; use crate::*; @@ -13,13 +12,13 @@ lazy_static! { #[test] fn check_serialize_vss_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); } #[test] fn check_deserialize_vss_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment::( rng, ); @@ -27,7 +26,7 @@ fn check_deserialize_vss_commitment() { #[test] fn check_deserialize_vss_commitment_error() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment_error::( rng, &ELEMENTS, ); @@ -35,7 +34,7 @@ fn check_deserialize_vss_commitment_error() { #[test] fn check_compute_public_key_package() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_compute_public_key_package::( rng, ); diff --git a/frost-secp256k1-tr/tests/common_traits_tests.rs b/frost-secp256k1-tr/tests/common_traits_tests.rs index 81b97a9..93265b7 100644 --- a/frost-secp256k1-tr/tests/common_traits_tests.rs +++ b/frost-secp256k1-tr/tests/common_traits_tests.rs @@ -4,7 +4,6 @@ mod helpers; use frost_secp256k1_tr::SigningKey; use helpers::samples; -use rand::thread_rng; #[allow(clippy::unnecessary_literal_unwrap)] fn check_common_traits_for_type(v: T) { @@ -20,7 +19,7 @@ fn check_common_traits_for_type(v: #[test] fn check_signing_key_common_traits() { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; let signing_key = SigningKey::new(&mut rng); check_common_traits_for_type(signing_key); } diff --git a/frost-secp256k1-tr/tests/integration_tests.rs b/frost-secp256k1-tr/tests/integration_tests.rs index 176aad6..2d5db02 100644 --- a/frost-secp256k1-tr/tests/integration_tests.rs +++ b/frost-secp256k1-tr/tests/integration_tests.rs @@ -1,6 +1,5 @@ use frost_secp256k1_tr::*; use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; #[test] @@ -10,14 +9,14 @@ fn check_zero_key_fails() { #[test] fn check_sign_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dkg::(rng); } #[test] fn check_dkg_part1_fails_with_invalid_signers_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 1; let max_signers = 3; @@ -31,7 +30,7 @@ fn check_dkg_part1_fails_with_invalid_signers_min_signers() { #[test] fn check_dkg_part1_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -45,7 +44,7 @@ fn check_dkg_part1_fails_with_min_signers_greater_than_max() { #[test] fn check_dkg_part1_fails_with_invalid_signers_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 1; @@ -59,21 +58,21 @@ fn check_dkg_part1_fails_with_invalid_signers_max_signers() { #[test] fn check_rts() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_rts::(rng); } #[test] fn check_refresh_shares_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); } #[test] fn check_refresh_shares_with_dealer_serialisation() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::< Secp256K1Sha256TR, @@ -83,7 +82,7 @@ fn check_refresh_shares_with_dealer_serialisation() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_public_key_package::< Secp256K1Sha256TR, @@ -93,7 +92,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -112,7 +111,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { #[test] fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -131,7 +130,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s #[test] fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -150,7 +149,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; let min_signers = 3; let max_signers = 1; @@ -164,7 +163,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(8).unwrap(), Identifier::try_from(3).unwrap(), @@ -183,21 +182,21 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { #[test] fn check_refresh_shares_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dkg::(rng); } #[test] fn check_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dealer::(rng); } #[test] fn check_sign_with_dealer_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 1; let max_signers = 3; @@ -211,7 +210,7 @@ fn check_sign_with_dealer_fails_with_invalid_min_signers() { #[test] fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -225,7 +224,7 @@ fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { #[test] fn check_sign_with_dealer_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 1; @@ -241,13 +240,13 @@ fn check_sign_with_dealer_fails_with_invalid_max_signers() { /// value is working. #[test] fn check_share_generation_secp256k1_tr_sha256() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_share_generation::(rng); } #[test] fn check_share_generation_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 0; let max_signers = 3; @@ -261,7 +260,7 @@ fn check_share_generation_fails_with_invalid_min_signers() { #[test] fn check_share_generation_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -275,7 +274,7 @@ fn check_share_generation_fails_with_min_signers_greater_than_max() { #[test] fn check_share_generation_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 0; @@ -339,7 +338,7 @@ fn check_identifier_generation() -> Result<(), Error> { #[test] fn check_sign_with_dealer_and_identifiers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::< Secp256K1Sha256TR, @@ -349,7 +348,7 @@ fn check_sign_with_dealer_and_identifiers() { #[test] fn check_sign_with_missing_identifier() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_missing_identifier::< Secp256K1Sha256TR, _, @@ -358,7 +357,7 @@ fn check_sign_with_missing_identifier() { #[test] fn check_sign_with_incorrect_commitments() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_incorrect_commitments::< Secp256K1Sha256TR, _, diff --git a/frost-secp256k1-tr/tests/interoperability_tests.rs b/frost-secp256k1-tr/tests/interoperability_tests.rs index b2e3f9a..5be7f62 100644 --- a/frost-secp256k1-tr/tests/interoperability_tests.rs +++ b/frost-secp256k1-tr/tests/interoperability_tests.rs @@ -1,25 +1,24 @@ use frost_secp256k1_tr::*; use crate::Secp256K1Sha256TR; -use rand::thread_rng; mod helpers; #[test] fn check_interoperability_in_regular_sign() { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; for _ in 0..256 { let signing_key = SigningKey::new(&mut rng); let verifying_key = signing_key.into(); - let signature = signing_key.sign(&mut rng, b"message"); + let signature = signing_key.sign(rng, b"message"); helpers::verify_signature(b"message", &signature, &verifying_key); } } #[test] fn check_interoperability_in_sign_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; // Test with multiple keys/signatures to better exercise the key generation // and the interoperability check. A smaller number of iterations is used @@ -27,7 +26,7 @@ fn check_interoperability_in_sign_with_dkg() { for _ in 0..32 { let (message, group_signature, group_pubkey) = frost_core::tests::ciphersuite_generic::check_sign_with_dkg::( - rng.clone(), + rng, ); helpers::verify_signature(&message, &group_signature, &group_pubkey); @@ -36,14 +35,14 @@ fn check_interoperability_in_sign_with_dkg() { #[test] fn check_interoperability_in_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; // Test with multiple keys/signatures to better exercise the key generation // and the interoperability check. for _ in 0..256 { let (message, group_signature, group_pubkey) = frost_core::tests::ciphersuite_generic::check_sign_with_dealer::( - rng.clone(), + rng, ); // Check that the threshold signature can be verified by the `ed25519_dalek` crate diff --git a/frost-secp256k1-tr/tests/rerandomized_tests.rs b/frost-secp256k1-tr/tests/rerandomized_tests.rs index 67e1431..7b4144c 100644 --- a/frost-secp256k1-tr/tests/rerandomized_tests.rs +++ b/frost-secp256k1-tr/tests/rerandomized_tests.rs @@ -1,9 +1,8 @@ use frost_secp256k1_tr::Secp256K1Sha256TR; -use rand::thread_rng; #[test] fn check_randomized_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let (_msg, _group_signature, _group_pubkey) = frost_rerandomized::tests::check_randomized_sign_with_dealer::(rng); diff --git a/frost-secp256k1-tr/tests/tweaking_tests.rs b/frost-secp256k1-tr/tests/tweaking_tests.rs index 3fc74ae..dddbed1 100644 --- a/frost-secp256k1-tr/tests/tweaking_tests.rs +++ b/frost-secp256k1-tr/tests/tweaking_tests.rs @@ -12,19 +12,19 @@ mod helpers; #[test] fn check_tweaked_sign_with_dealer() -> Result<(), Box> { use frost_secp256k1_tr as frost; - use rand::thread_rng; + use std::collections::BTreeMap; let merkle_root: Vec = vec![12; 32]; - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; let (shares, pubkey_package) = frost::keys::generate_with_dealer( max_signers, min_signers, frost::keys::IdentifierList::Default, - &mut rng, + rng, )?; let mut key_packages: BTreeMap<_, _> = BTreeMap::new(); for (identifier, secret_share) in shares { diff --git a/frost-secp256k1/README.md b/frost-secp256k1/README.md index 9e4928b..bdec1ac 100644 --- a/frost-secp256k1/README.md +++ b/frost-secp256k1/README.md @@ -11,10 +11,9 @@ scenario in a single thread and it abstracts away any communication between peer ```rust # // ANCHOR: tkg_gen use frost_secp256k1 as frost; -use rand::thread_rng; use std::collections::BTreeMap; -let mut rng = thread_rng(); +let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; let (shares, pubkey_package) = frost::keys::generate_with_dealer( diff --git a/frost-secp256k1/benches/bench.rs b/frost-secp256k1/benches/bench.rs index c577363..cd89e8e 100644 --- a/frost-secp256k1/benches/bench.rs +++ b/frost-secp256k1/benches/bench.rs @@ -1,16 +1,15 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use rand::thread_rng; use frost_secp256k1::*; fn bench_secp256k1_batch_verify(c: &mut Criterion) { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; frost_core::benches::bench_batch_verify::(c, "secp256k1", &mut rng); } fn bench_secp256k1_sign(c: &mut Criterion) { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; frost_core::benches::bench_sign::(c, "secp256k1", &mut rng); } diff --git a/frost-secp256k1/dkg.md b/frost-secp256k1/dkg.md index 5d62857..24c6a53 100644 --- a/frost-secp256k1/dkg.md +++ b/frost-secp256k1/dkg.md @@ -26,12 +26,11 @@ they can proceed to sign messages with FROST. ```rust # // ANCHOR: dkg_import -use rand::thread_rng; use std::collections::BTreeMap; use frost_secp256k1 as frost; -let mut rng = thread_rng(); +let mut rng = rand::rngs::OsRng; let max_signers = 5; let min_signers = 3; diff --git a/frost-secp256k1/src/keys/repairable.rs b/frost-secp256k1/src/keys/repairable.rs index 88bce01..98a2e7c 100644 --- a/frost-secp256k1/src/keys/repairable.rs +++ b/frost-secp256k1/src/keys/repairable.rs @@ -58,7 +58,7 @@ pub fn repair_share_step_3( mod tests { use lazy_static::lazy_static; - use rand::thread_rng; + use serde_json::Value; use crate::Secp256K1Sha256; @@ -71,7 +71,7 @@ mod tests { #[test] fn check_repair_share_step_1() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_1::(rng); } @@ -83,7 +83,7 @@ mod tests { #[test] fn check_repair_share_step_3() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_3::( rng, &REPAIR_SHARE, @@ -92,7 +92,7 @@ mod tests { #[test] fn check_repair_share_step_1_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< Secp256K1Sha256, _, diff --git a/frost-secp256k1/src/tests/batch.rs b/frost-secp256k1/src/tests/batch.rs index b87d22a..d3b1c68 100644 --- a/frost-secp256k1/src/tests/batch.rs +++ b/frost-secp256k1/src/tests/batch.rs @@ -1,24 +1,22 @@ -use rand::thread_rng; - use crate::*; #[test] fn check_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::batch_verify::(rng); } #[test] fn check_bad_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::bad_batch_verify::(rng); } #[test] fn empty_batch_verify() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::batch::empty_batch_verify::(rng); } diff --git a/frost-secp256k1/src/tests/coefficient_commitment.rs b/frost-secp256k1/src/tests/coefficient_commitment.rs index d1b6c22..7be35ea 100644 --- a/frost-secp256k1/src/tests/coefficient_commitment.rs +++ b/frost-secp256k1/src/tests/coefficient_commitment.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; use crate::*; @@ -13,7 +12,7 @@ lazy_static! { #[test] fn check_serialization_of_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_serialization_of_coefficient_commitment::< Secp256K1Sha256, _, @@ -22,7 +21,7 @@ fn check_serialization_of_coefficient_commitment() { #[test] fn check_create_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_create_coefficient_commitment::< Secp256K1Sha256, _, @@ -37,7 +36,7 @@ fn check_create_coefficient_commitment_error() { #[test] fn check_get_value_of_coefficient_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::coefficient_commitment::check_get_value_of_coefficient_commitment::< Secp256K1Sha256, diff --git a/frost-secp256k1/src/tests/vss_commitment.rs b/frost-secp256k1/src/tests/vss_commitment.rs index 1a09195..5574f59 100644 --- a/frost-secp256k1/src/tests/vss_commitment.rs +++ b/frost-secp256k1/src/tests/vss_commitment.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; use crate::*; @@ -13,19 +12,19 @@ lazy_static! { #[test] fn check_serialize_vss_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); } #[test] fn check_deserialize_vss_commitment() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment::(rng); } #[test] fn check_deserialize_vss_commitment_error() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment_error::( rng, &ELEMENTS, ); @@ -33,6 +32,6 @@ fn check_deserialize_vss_commitment_error() { #[test] fn check_compute_public_key_package() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_compute_public_key_package::(rng); } diff --git a/frost-secp256k1/tests/common_traits_tests.rs b/frost-secp256k1/tests/common_traits_tests.rs index 6048b48..e9f788e 100644 --- a/frost-secp256k1/tests/common_traits_tests.rs +++ b/frost-secp256k1/tests/common_traits_tests.rs @@ -4,7 +4,6 @@ mod helpers; use frost_secp256k1::SigningKey; use helpers::samples; -use rand::thread_rng; #[allow(clippy::unnecessary_literal_unwrap)] fn check_common_traits_for_type(v: T) { @@ -20,7 +19,7 @@ fn check_common_traits_for_type(v: #[test] fn check_signing_key_common_traits() { - let mut rng = thread_rng(); + let mut rng = rand::rngs::OsRng; let signing_key = SigningKey::new(&mut rng); check_common_traits_for_type(signing_key); } diff --git a/frost-secp256k1/tests/integration_tests.rs b/frost-secp256k1/tests/integration_tests.rs index d098f26..0676f61 100644 --- a/frost-secp256k1/tests/integration_tests.rs +++ b/frost-secp256k1/tests/integration_tests.rs @@ -1,6 +1,5 @@ use frost_secp256k1::*; use lazy_static::lazy_static; -use rand::thread_rng; use serde_json::Value; #[test] @@ -10,14 +9,14 @@ fn check_zero_key_fails() { #[test] fn check_sign_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dkg::(rng); } #[test] fn check_dkg_part1_fails_with_invalid_signers_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 1; let max_signers = 3; @@ -31,7 +30,7 @@ fn check_dkg_part1_fails_with_invalid_signers_min_signers() { #[test] fn check_dkg_part1_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -45,7 +44,7 @@ fn check_dkg_part1_fails_with_min_signers_greater_than_max() { #[test] fn check_dkg_part1_fails_with_invalid_signers_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 1; @@ -59,21 +58,21 @@ fn check_dkg_part1_fails_with_invalid_signers_max_signers() { #[test] fn check_rts() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::repairable::check_rts::(rng); } #[test] fn check_refresh_shares_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); } #[test] fn check_refresh_shares_with_dealer_serialisation() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::( rng, @@ -82,7 +81,7 @@ fn check_refresh_shares_with_dealer_serialisation() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_public_key_package::< Secp256K1Sha256, @@ -92,7 +91,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -111,7 +110,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { #[test] fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -130,7 +129,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s #[test] fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -149,7 +148,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; let min_signers = 3; let max_signers = 1; @@ -163,7 +162,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let identifiers = vec![ Identifier::try_from(8).unwrap(), Identifier::try_from(3).unwrap(), @@ -182,21 +181,21 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { #[test] fn check_refresh_shares_with_dkg() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::refresh::check_refresh_shares_with_dkg::(rng); } #[test] fn check_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dealer::(rng); } #[test] fn check_sign_with_dealer_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 1; let max_signers = 3; @@ -210,7 +209,7 @@ fn check_sign_with_dealer_fails_with_invalid_min_signers() { #[test] fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -224,7 +223,7 @@ fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { #[test] fn check_sign_with_dealer_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 1; @@ -240,13 +239,13 @@ fn check_sign_with_dealer_fails_with_invalid_max_signers() { /// value is working. #[test] fn check_share_generation_secp256k1_sha256() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_share_generation::(rng); } #[test] fn check_share_generation_fails_with_invalid_min_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 0; let max_signers = 3; @@ -260,7 +259,7 @@ fn check_share_generation_fails_with_invalid_min_signers() { #[test] fn check_share_generation_fails_with_min_signers_greater_than_max() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 2; @@ -274,7 +273,7 @@ fn check_share_generation_fails_with_min_signers_greater_than_max() { #[test] fn check_share_generation_fails_with_invalid_max_signers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let min_signers = 3; let max_signers = 0; @@ -338,7 +337,7 @@ fn check_identifier_generation() -> Result<(), Error> { #[test] fn check_sign_with_dealer_and_identifiers() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::< Secp256K1Sha256, @@ -348,7 +347,7 @@ fn check_sign_with_dealer_and_identifiers() { #[test] fn check_sign_with_missing_identifier() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_missing_identifier::( rng, ); @@ -356,7 +355,7 @@ fn check_sign_with_missing_identifier() { #[test] fn check_sign_with_incorrect_commitments() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; frost_core::tests::ciphersuite_generic::check_sign_with_incorrect_commitments::< Secp256K1Sha256, _, diff --git a/frost-secp256k1/tests/rerandomized_tests.rs b/frost-secp256k1/tests/rerandomized_tests.rs index 65cf0df..c7845f1 100644 --- a/frost-secp256k1/tests/rerandomized_tests.rs +++ b/frost-secp256k1/tests/rerandomized_tests.rs @@ -1,9 +1,8 @@ use frost_secp256k1::Secp256K1Sha256; -use rand::thread_rng; #[test] fn check_randomized_sign_with_dealer() { - let rng = thread_rng(); + let rng = rand::rngs::OsRng; let (_msg, _group_signature, _group_pubkey) = frost_rerandomized::tests::check_randomized_sign_with_dealer::(rng); From f862ef18f1147232df44f9e0c3f693b103f93b6a Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Tue, 11 Feb 2025 14:26:10 -0300 Subject: [PATCH 11/86] update release checklist; misc cleanups (#843) * update release checklist; misc cleanups * remove links to versions * add semver-checks; change numbered items to nonnumbered --- book/src/dev/release-checklist.md | 111 ++++++++++++++++-------------- frost-core/CHANGELOG.md | 4 +- frost-core/src/keys/refresh.rs | 6 +- 3 files changed, 63 insertions(+), 58 deletions(-) diff --git a/book/src/dev/release-checklist.md b/book/src/dev/release-checklist.md index d5893e5..5cfb4b4 100644 --- a/book/src/dev/release-checklist.md +++ b/book/src/dev/release-checklist.md @@ -1,110 +1,115 @@ # Release Checklist +## One-time `gh` setup + +Install the [GitHub command line +tool](https://github.com/cli/cli?tab=readme-ov-file#installation) to make +releases easier. ## One-time crates.io setup -1. Follow the steps in (you can create a token scoped to `publish-update`). -2. To get permissions to publish you’ll need to be in the [owners](https://github.com/orgs/ZcashFoundation/teams/owners) group. If you aren’t in there, ask someone in that group to add you +- Follow the steps in (you can create a token scoped to `publish-update`). +- To get permissions to publish you’ll need to be in the [owners](https://github.com/orgs/ZcashFoundation/teams/owners) group. If you aren’t in there, ask someone in that group to add you ## Communication -3. Post in #frost slack channel and tag the Frost team that you’re going to be doing a release to freeze PR approvals until it’s done. E.g “@frost-core I’m doing a release of \ of Frost. Please do not merge any more PRs until I’m finished. Thanks.” +- Post in #frost slack channel and tag the Frost team that you’re going to be doing a release to freeze PR approvals until it’s done. E.g “@frost-core I’m doing a release of \ of Frost. Please do not merge any more PRs until I’m finished. Thanks.” ## Checks -4. Check current version for each crate. This is in the Cargo.toml file for frost-core, frost-ed448 etc. - - 1. [Frost core version number](https://github.com/ZcashFoundation/frost/blob/main/frost-core/Cargo.toml#L7) - 2. [Frost ed25519 version number](https://github.com/ZcashFoundation/frost/blob/main/frost-ed25519/Cargo.toml#L8) - 3. [Frost ed448 version number](https://github.com/ZcashFoundation/frost/blob/main/frost-ed448/Cargo.toml#L7) - 4. [Frost p256 version number](https://github.com/ZcashFoundation/frost/blob/main/frost-p256/Cargo.toml#L8) - 5. [Frost re randomized version number](https://github.com/ZcashFoundation/frost/blob/main/frost-rerandomized/Cargo.toml#L8) - 6. [Frost ristretto255 version number](https://github.com/ZcashFoundation/frost/blob/main/frost-ristretto255/Cargo.toml#L8) - 7. [Frost secp256k1 version number](https://github.com/ZcashFoundation/frost/blob/main/frost-secp256k1/Cargo.toml#L7) - 8. [Frost secp256k1 tr version number](https://github.com/ZcashFoundation/frost/blob/main/frost-secp256k1-tr/Cargo.toml#L7) +- Currently all crates share the same version number, in the root Cargo.toml + file. Take note of that version. (If we ever decide to have specific + versions, update those separately as required.) -5. Decide which version to tag the release with (e.g. v0.3.0). Currently we always use the same release number for all crates, but it's possible for them to get out of sync in the future. +- Decide which version to tag the release with (e.g. v0.3.0), considering + [SemVer](https://doc.rust-lang.org/cargo/reference/semver.html). -6. Create new issue. E.g. [Release v0.4.0](https://github.com/ZcashFoundation/frost/issues/377) +- Create new issue. E.g. [Release v0.4.0](https://github.com/ZcashFoundation/frost/issues/377) ## Make changes -7. Bump the version of the crates in the root Cargo.toml file. (If they ever +- Bump the version of the crates in the root Cargo.toml file. (If they ever get out of sync, you will need to bump in each crate Cargo.toml file.) -8. Bump the version used in the tutorial (importing.md) +- Run `cargo semver-checks` to check if there are no API changes that break + SemVer compatibility. ([Installation + instructions](https://crates.io/crates/cargo-semver-checks)) Fix issues if + any (i.e. change the version, or revert/adapt the API change). + +- Bump the version used in the tutorial (importing.md) -9. Check if the [changelog](https://github.com/ZcashFoundation/frost/blob/main/frost-core/CHANGELOG.md) is up to date and update if required (we’re only keeping the one in frost-core for now). Double check using [FROST releases](https://github.com/ZcashFoundation/frost/releases) which will have a list of all the PRs that have been closed since the last release. Things to include in the changelog will be anything that impacts production code and big documentation changes. I.e. script and test changes should not be included. NOTE: Please add to the changelog whenever you make changes to the library as this will make things simpler for the person in charge of the release. +- Check if the [changelog](https://github.com/ZcashFoundation/frost/blob/main/frost-core/CHANGELOG.md) is up to date and update if required (we’re only keeping the one in frost-core for now). Double check using [FROST releases](https://github.com/ZcashFoundation/frost/releases) which will have a list of all the PRs that have been closed since the last release. Things to include in the changelog will be anything that impacts production code and big documentation changes. I.e. script and test changes should not be included. NOTE: Please add to the changelog whenever you make changes to the library as this will make things simpler for the person in charge of the release. - 1. Move version in changelog to Released - 2. Create a new version in “unreleased” in changelog + - Move version in changelog to Released + - Create a new version in “unreleased” in changelog -10. Update the version number for frost-core and frost-rerandomized in the Ciphersuite crates, e.g. in `frost-core = { path = "../frost-core", version = "0.4.0", features = ["test-impl"] }`. You'll need to do this for dependencies and dev-dependencies +- Update the version number for frost-core and frost-rerandomized in the Ciphersuite crates, e.g. in `frost-core = { path = "../frost-core", version = "0.4.0", features = ["test-impl"] }`. You'll need to do this for dependencies and dev-dependencies -11. Create a PR with subject `Release \` containing all these changes +- Create a PR with subject `Release \` containing all these changes -12. You’ll need someone to review and approve it +- You’ll need someone to review and approve it -13. Wait for it to pass CI checks +- Wait for it to pass CI checks ## Publish -14. Checkout main branch, **in the commit of the previously merged PR** (in case other stuff got merged after that) +- Checkout main branch, **in the commit of the previously merged PR** (in case other stuff got merged after that) -15. Run `cargo publish -p frost-core --dry-run` to check if it’s ready to publish. Fix issues if any. +- Run `cargo publish -p frost-core --dry-run` to check if it’s ready to publish. Fix issues if any. -16. [Draft and publish a new release](https://github.com/ZcashFoundation/frost/releases/new) for frost-core. +- [Draft and publish a new release](https://github.com/ZcashFoundation/frost/releases/new) for frost-core. - 1. In “Choose a tag” type `/` e.g. “frost-core/v0.2.0” and click “Create new tag” - 2. In “Target” select “main” as long as other PRs haven’t been merged after the version bump PR. Otherwise, **select the commit matching the PR that was merged above**. - 3. In “Release title” use ` ` e.g. “frost-core v0.2.0” - 4. Paste the (raw Markdown) changelog for this version into the description box. - 5. Leave “Set as pre-release” **unchecked** (we should have checked it in earlier versions but the ship has sailed. It doesn’t matter much) - 6. **Check** “Set as the latest release” + - In “Choose a tag” type `/` e.g. “frost-core/v0.2.0” and click “Create new tag” + - In “Target” select “main” as long as other PRs haven’t been merged after the version bump PR. Otherwise, **select the commit matching the PR that was merged above**. + - In “Release title” use ` ` e.g. “frost-core v0.2.0” + - Paste the (raw Markdown) changelog for this version into the description box. + - Leave “Set as pre-release” **unchecked** (we should have checked it in earlier versions but the ship has sailed. It doesn’t matter much) + - **Check** “Set as the latest release” -17. Publish it with `cargo publish -p frost-core` +- Publish it with `cargo publish -p frost-core` -18. Check if frost-rerandomized is ready to be published: `cargo publish -p frost-rerandomized --dry-run`. Fix any errors if needed. +- Check if frost-rerandomized is ready to be published: `cargo publish -p frost-rerandomized --dry-run`. Fix any errors if needed. -19. Draft and publish a frost-rerandomized release +- Draft and publish a frost-rerandomized release: - 1. Use the same process as described for frost-core above, but you can leave the changelog empty and **uncheck** “Set as the latest release” + - Run `gh release create "frost-rerandomized/v2.1.0" -n '' -t "frost-rerandomized v2.1.0" --latest=false` + (replace both instances of the version) -20. Publish it with `cargo publish -p frost-rerandomized` +- Publish it with `cargo publish -p frost-rerandomized` -21. Check if other crates are ready to be published: `for cs in ristretto255 ed25519 secp256k1 secp256k1-tr p256 ed448; do cargo publish -p frost-$cs --dry-run; done`. Fix any issues if needed. +- Check if other crates are ready to be published: `for cs in ristretto255 ed25519 secp256k1 secp256k1-tr p256 ed448; do cargo publish -p frost-$cs --dry-run; done`. Fix any issues if needed. - 1. If you get an error like this: + - If you get an error like this: “error: failed to verify package tarball Caused by: failed to select a version for the requirement `frost-core = "^0.3.0"` candidate versions found which didn't match: 0.2.0, 0.1.0 location searched: crates.io index required by package `frost-ed25519 v0.3.0 (frost/target/package/frost-ed25519-0.3.0)`” This is because the ciphersuite crates aren’t pointing at the new frost-core package. This is because you need to publish frost-core before you can publish the others otherwise they will not have the expected version to point to. -22. Draft and publish releases for each of those crates (sorry, that will be boring) +- Draft and publish releases for each of those crates: - 1. Use the same process as described for frost-core above (actions 1 - 3), but you can leave the changelog empty and **uncheck** “Set as the latest release” + - Run `for cs in ristretto255 ed25519 secp256k1 secp256k1-tr p256 ed448; do gh release create "frost-$cs/v2.1.0" -n '' -t "frost-$cs v2.1.0" --latest=false; done` (replace both instances of the version) -23. Publish those crates: `for cs in ristretto255 ed25519 secp256k1 secp256k1-tr p256 ed448; do cargo publish -p frost-$cs; done` +- Publish those crates: `for cs in ristretto255 ed25519 secp256k1 secp256k1-tr p256 ed448; do cargo publish -p frost-$cs; done` ## Confirm -24. Check versions in the crates to confirm everything worked: +- Check versions in the crates to confirm everything worked: - 1. [Frost core](https://crates.io/crates/frost-core/versions) - 2. [Frost ed25519](https://crates.io/crates/frost-ed25519/versions) - 3. [Frost ed448](https://crates.io/crates/frost-ed448/versions) - 4. [Frost p256](https://crates.io/crates/frost-p256/versions) - 5. [Frost ristretto255](https://crates.io/crates/frost-ristretto255/versions) - 6. [Frost secp256k1](https://crates.io/crates/frost-secp256k1/versions) - 7. [Frost secp256k1 tr](https://crates.io/crates/frost-secp256k1-tr/versions) - 8. [Frost rerandomized](https://crates.io/crates/frost-rerandomized/versions) + - [Frost core](https://crates.io/crates/frost-core/versions) + - [Frost ed25519](https://crates.io/crates/frost-ed25519/versions) + - [Frost ed448](https://crates.io/crates/frost-ed448/versions) + - [Frost p256](https://crates.io/crates/frost-p256/versions) + - [Frost ristretto255](https://crates.io/crates/frost-ristretto255/versions) + - [Frost secp256k1](https://crates.io/crates/frost-secp256k1/versions) + - [Frost secp256k1 tr](https://crates.io/crates/frost-secp256k1-tr/versions) + - [Frost rerandomized](https://crates.io/crates/frost-rerandomized/versions) -25. Let the team know in the #frost slack channel that the release is complete and successful +- Let the team know in the #frost slack channel that the release is complete and successful ## In the case of an unsuccessful release diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index 2d4016d..46eaca6 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -8,9 +8,9 @@ Entries are listed in reverse chronological order. * It is now possible to identify the culprit in `frost_core::keys::dkg::part3()` if an invalid secret share was sent by one of the participants (by calling - frost_core::Error::culprit()`) (#728) + `frost_core::Error::culprit()`) (#728) * Added frost-secp256k1-tr crate, allowing to generate Bitcoin Taproot - (BIP340/BIP341) compatible signatures(#730). + (BIP340/BIP341) compatible signatures (#730). * Support refreshing shares using the DKG approach using the `frost_core::keys::refresh::refresh_dkg_{part1,part2,shares}()` functions (#766). diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index ef57c60..b12c01e 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -1,8 +1,8 @@ //! Refresh Shares //! -//! Implements the functionality to refresh a share. This requires the participation -//! of all the remaining signers. This can be done using a Trusted Dealer or -//! DKG (not yet implemented) +//! Implements the functionality to refresh a share. This requires the +//! participation of all the remaining signers. This can be done using a Trusted +//! Dealer or DKG. use alloc::collections::BTreeMap; use alloc::vec::Vec; From 64a4be6c6dbc10760af7c43add263ee019cd250b Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Mon, 17 Feb 2025 16:49:09 +0300 Subject: [PATCH 12/86] chore(ci): update some github action dependencies (#873) --- .github/workflows/coverage.yaml | 7 ++-- .github/workflows/docs.yml | 6 +--- .github/workflows/main.yml | 61 +++++++-------------------------- book/src/dev/developer-guide.md | 2 +- 4 files changed, 17 insertions(+), 59 deletions(-) diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index dac117b..dece196 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -27,12 +27,9 @@ jobs: with: persist-credentials: false - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - override: true - profile: minimal - components: llvm-tools-preview + components: llvm-tools - name: Install cargo-llvm-cov cargo command run: cargo install cargo-llvm-cov diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index aebf2fa..d587aa2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -41,11 +41,7 @@ jobs: persist-credentials: false - name: Install latest beta - uses: actions-rs/toolchain@v1 - with: - toolchain: beta - components: rust-docs - override: true + uses: dtolnay/rust-toolchain@beta - uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b422c09..7c6d0e4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,20 +9,14 @@ on: - main jobs: - build_default: name: build with default features runs-on: ubuntu-latest steps: - uses: actions/checkout@v4.2.2 - - uses: actions-rs/toolchain@v1.0.7 - with: - toolchain: beta - override: true - - uses: actions-rs/cargo@v1.0.3 - with: - command: build + - uses: dtolnay/rust-toolchain@beta + - run: cargo build build_msrv: name: build with MSRV (1.66.1) @@ -85,14 +79,8 @@ jobs: steps: - uses: actions/checkout@v4.2.2 - - uses: actions-rs/toolchain@v1.0.7 - with: - toolchain: beta - override: true - - uses: actions-rs/cargo@v1.0.3 - with: - command: test - args: --release --all-features + - uses: dtolnay/rust-toolchain@beta + - run: cargo test --release --all-features clippy: name: Clippy @@ -103,10 +91,7 @@ jobs: with: persist-credentials: false - - uses: actions-rs/toolchain@v1.0.7 - with: - toolchain: stable - override: true + - uses: dtolnay/rust-toolchain@stable - name: Check workflow permissions id: check_permissions @@ -117,12 +102,9 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Run clippy action to produce annotations - uses: actions-rs/clippy-check@v1.0.7 + uses: clechasseur/rs-clippy-check@v4 if: ${{ steps.check_permissions.outputs.has-permission }} with: - # GitHub displays the clippy job and its results as separate entries - name: Clippy (stable) Results - token: ${{ secrets.GITHUB_TOKEN }} args: --all-features --all-targets -- -D warnings - name: Run clippy manually without annotations @@ -138,18 +120,13 @@ jobs: with: persist-credentials: false - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: - toolchain: stable components: rustfmt - override: true - uses: Swatinem/rust-cache@v2 - - uses: actions-rs/cargo@v1.0.3 - with: - command: fmt - args: --all -- --check + - run: cargo fmt --all -- --check gencode: name: Check if automatically generated code is up to date @@ -160,18 +137,13 @@ jobs: with: persist-credentials: false - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: - toolchain: stable components: rustfmt - override: true - uses: Swatinem/rust-cache@v2 - - uses: actions-rs/cargo@v1.0.3 - with: - command: run - args: --bin gencode -- --check + - run: cargo run --bin gencode -- --check docs: name: Check Rust doc @@ -184,16 +156,9 @@ jobs: with: persist-credentials: false - - uses: actions-rs/toolchain@v1.0.7 - with: - toolchain: stable - profile: minimal - override: true + - uses: dtolnay/rust-toolchain@stable - - uses: actions-rs/cargo@v1.0.3 - with: - command: doc - args: --no-deps --document-private-items --all-features + - run: cargo doc --no-deps --document-private-items --all-features actionlint: runs-on: ubuntu-latest @@ -203,4 +168,4 @@ jobs: - uses: reviewdog/action-actionlint@v1.64.1 with: level: warning - fail_on_error: false + fail_level: none diff --git a/book/src/dev/developer-guide.md b/book/src/dev/developer-guide.md index 27ffb58..7c31eec 100644 --- a/book/src/dev/developer-guide.md +++ b/book/src/dev/developer-guide.md @@ -11,4 +11,4 @@ Test coverage checks are performed in the pipeline. This is configured here: `.github/workflows/coverage.yaml` To run these locally: 1. Install coverage tool by running `cargo install cargo-llvm-cov` -2. Run `cargo llvm-cov --ignore-filename-regex '.*(tests).*|benches.rs|gencode|helpers.rs'` (you may be asked if you want to install `llvm-tools-preview`, if so type `Y`) +2. Run `cargo llvm-cov --ignore-filename-regex '.*(tests).*|benches.rs|gencode|helpers.rs'` (you may be asked if you want to install `llvm-tools`, if so type `Y`) From fc87f59cc08e8721e9c543fbd90aed7da4bf0d67 Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Mon, 17 Feb 2025 17:10:41 +0300 Subject: [PATCH 13/86] chore(deps): improve organization of workspace (root Cargo.toml) (#874) --- Cargo.toml | 6 ++++-- book/src/dev/release-checklist.md | 2 +- frost-core/Cargo.toml | 4 ++-- frost-ed25519/Cargo.toml | 9 ++++----- frost-ed448/Cargo.toml | 8 ++++---- frost-p256/Cargo.toml | 10 +++++----- frost-rerandomized/Cargo.toml | 4 +--- frost-ristretto255/Cargo.toml | 10 +++++----- frost-secp256k1-tr/Cargo.toml | 10 +++++----- frost-secp256k1/Cargo.toml | 10 +++++----- gencode/Cargo.toml | 2 -- 11 files changed, 36 insertions(+), 39 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d2615d4..c940a76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ members = [ "gencode" ] - [workspace.package] edition = "2021" version = "2.1.0" @@ -38,5 +37,8 @@ rand_chacha = "0.3" rand_core = "0.6" serde_json = "1.0" +frost-core = { path = "frost-core", version = "2.1.0", default-features = false } +frost-rerandomized = { path = "frost-rerandomized", version = "2.1.0", default-features = false } + [profile.test.package."*"] -opt-level = 3 \ No newline at end of file +opt-level = 3 diff --git a/book/src/dev/release-checklist.md b/book/src/dev/release-checklist.md index 5cfb4b4..79aac86 100644 --- a/book/src/dev/release-checklist.md +++ b/book/src/dev/release-checklist.md @@ -46,7 +46,7 @@ releases easier. - Move version in changelog to Released - Create a new version in “unreleased” in changelog -- Update the version number for frost-core and frost-rerandomized in the Ciphersuite crates, e.g. in `frost-core = { path = "../frost-core", version = "0.4.0", features = ["test-impl"] }`. You'll need to do this for dependencies and dev-dependencies +- Update the version number for frost-core and frost-rerandomized in the root Cargo.toml file, e.g. in `frost-core = { path = "frost-core", version = "0.4.0", default-features = false }` - Create a PR with subject `Release \` containing all these changes diff --git a/frost-core/Cargo.toml b/frost-core/Cargo.toml index a013a9c..0907e8b 100644 --- a/frost-core/Cargo.toml +++ b/frost-core/Cargo.toml @@ -34,10 +34,10 @@ itertools = { version = "0.14.0", default-features = false } # Test dependencies used with the test-impl feature proptest = { version = "1.0", optional = true } serde_json = { version = "1.0", optional = true } -criterion = { version = "0.5", optional = true } +criterion = { workspace = true, optional = true } [dev-dependencies] -criterion = { version = "0.5" } +criterion.workspace = true lazy_static.workspace = true proptest.workspace = true rand.workspace = true diff --git a/frost-ed25519/Cargo.toml b/frost-ed25519/Cargo.toml index b6d26d5..36523fd 100644 --- a/frost-ed25519/Cargo.toml +++ b/frost-ed25519/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "frost-ed25519" edition.workspace = true - version.workspace = true authors.workspace = true readme = "README.md" @@ -18,15 +17,15 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] curve25519-dalek = { version = "=4.1.3", features = ["rand_core"] } document-features.workspace = true -frost-core = { path = "../frost-core", version = "2.1.0", default-features = false } -frost-rerandomized = { path = "../frost-rerandomized", version = "2.1.0", default-features = false } +frost-core.workspace = true +frost-rerandomized.workspace = true rand_core.workspace = true sha2 = { version = "0.10.2", default-features = false } [dev-dependencies] criterion.workspace = true -frost-core = { path = "../frost-core", version = "2.1.0", features = ["test-impl"] } -frost-rerandomized = { path = "../frost-rerandomized", version = "2.1.0", features = ["test-impl"] } +frost-core = { workspace = true, features = ["std", "test-impl"] } +frost-rerandomized = { workspace = true, features = ["std", "test-impl"] } ed25519-dalek = "2.1.0" insta.workspace = true hex.workspace = true diff --git a/frost-ed448/Cargo.toml b/frost-ed448/Cargo.toml index 16282a9..bc7d85b 100644 --- a/frost-ed448/Cargo.toml +++ b/frost-ed448/Cargo.toml @@ -17,15 +17,15 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] document-features.workspace = true ed448-goldilocks = { version = "0.9.0" } -frost-core = { path = "../frost-core", version = "2.1.0", default-features = false } -frost-rerandomized = { path = "../frost-rerandomized", version = "2.1.0", default-features = false } +frost-core.workspace = true +frost-rerandomized.workspace = true rand_core.workspace = true sha3 = { version = "0.10.6", default-features = false } [dev-dependencies] criterion.workspace = true -frost-core = { path = "../frost-core", version = "2.1.0", features = ["test-impl"] } -frost-rerandomized = { path = "../frost-rerandomized", version = "2.1.0", features = ["test-impl"] } +frost-core = { workspace = true, features = ["std", "test-impl"] } +frost-rerandomized = { workspace = true, features = ["std", "test-impl"] } lazy_static.workspace = true insta.workspace = true hex.workspace = true diff --git a/frost-p256/Cargo.toml b/frost-p256/Cargo.toml index dc5e3ed..1376e4d 100644 --- a/frost-p256/Cargo.toml +++ b/frost-p256/Cargo.toml @@ -7,7 +7,7 @@ readme = "README.md" license.workspace = true repository.workspace = true categories.workspace = true -keywords = ["cryptography", "crypto", "threshold", "signature"] +keywords = ["cryptography", "crypto", "p256", "threshold", "signature"] description = "A Schnorr signature scheme over the NIST P-256 curve that supports FROST." [package.metadata.docs.rs] @@ -17,15 +17,15 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] document-features.workspace = true p256 = { version = "0.13.0", features = ["hash2curve"], default-features = false } -frost-core = { path = "../frost-core", version = "2.1.0", default-features = false } -frost-rerandomized = { path = "../frost-rerandomized", version = "2.1.0", default-features = false } +frost-core.workspace = true +frost-rerandomized.workspace = true rand_core.workspace = true sha2 = { version = "0.10.2", default-features = false } [dev-dependencies] criterion.workspace = true -frost-core = { path = "../frost-core", version = "2.1.0", features = ["test-impl"] } -frost-rerandomized = { path = "../frost-rerandomized", version = "2.1.0", features = ["test-impl"] } +frost-core = { workspace = true, features = ["std", "test-impl"] } +frost-rerandomized = { workspace = true, features = ["std", "test-impl"] } insta.workspace = true hex.workspace = true lazy_static.workspace = true diff --git a/frost-rerandomized/Cargo.toml b/frost-rerandomized/Cargo.toml index 878f710..cc3a1d3 100644 --- a/frost-rerandomized/Cargo.toml +++ b/frost-rerandomized/Cargo.toml @@ -17,9 +17,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] derive-getters = "0.5.0" document-features.workspace = true -frost-core = { path = "../frost-core", version = "2.1.0", features = [ - "internals" -], default-features = false } +frost-core = { workspace = true, features = ["internals"] } hex.workspace = true rand_core.workspace = true diff --git a/frost-ristretto255/Cargo.toml b/frost-ristretto255/Cargo.toml index ff67d89..06e8803 100644 --- a/frost-ristretto255/Cargo.toml +++ b/frost-ristretto255/Cargo.toml @@ -17,15 +17,15 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] curve25519-dalek = { version = "=4.1.3", features = ["rand_core"] } document-features.workspace = true -frost-core = { path = "../frost-core", version = "2.1.0", default-features = false } -frost-rerandomized = { path = "../frost-rerandomized", version = "2.1.0", default-features = false } +frost-core.workspace = true +frost-rerandomized.workspace = true rand_core.workspace = true sha2 = { version = "0.10.2", default-features = false } [dev-dependencies] -criterion = { version = "0.5", features = ["html_reports"] } -frost-core = { path = "../frost-core", version = "2.1.0", features = ["test-impl"] } -frost-rerandomized = { path = "../frost-rerandomized", version = "2.1.0", features = ["test-impl"] } +criterion = { workspace = true, features = ["html_reports"] } +frost-core = { workspace = true, features = ["std", "test-impl"] } +frost-rerandomized = { workspace = true, features = ["std", "test-impl"] } insta.workspace = true hex.workspace = true lazy_static.workspace = true diff --git a/frost-secp256k1-tr/Cargo.toml b/frost-secp256k1-tr/Cargo.toml index 3a2292c..1f9d16e 100644 --- a/frost-secp256k1-tr/Cargo.toml +++ b/frost-secp256k1-tr/Cargo.toml @@ -7,7 +7,7 @@ readme = "README.md" license.workspace = true repository.workspace = true categories.workspace = true -keywords = ["cryptography", "crypto", "threshold", "signature"] +keywords = ["cryptography", "crypto", "secp256k1", "threshold", "signature"] description = "A Schnorr signature scheme over the secp256k1 curve that supports FROST and Taproot." [package.metadata.docs.rs] @@ -16,16 +16,16 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] document-features.workspace = true -frost-core = { path = "../frost-core", version = "2.1.0", default-features = false } -frost-rerandomized = { path = "../frost-rerandomized", version = "2.1.0", default-features = false } +frost-core.workspace = true +frost-rerandomized.workspace = true k256 = { version = "0.13.0", features = ["arithmetic", "expose-field", "hash2curve"], default-features = false } rand_core.workspace = true sha2 = { version = "0.10.2", default-features = false } [dev-dependencies] criterion.workspace = true -frost-core = { path = "../frost-core", version = "2.1.0", features = ["test-impl"] } -frost-rerandomized = { path = "../frost-rerandomized", version = "2.1.0", features = ["test-impl"] } +frost-core = { workspace = true, features = ["std", "test-impl"] } +frost-rerandomized = { workspace = true, features = ["std", "test-impl"] } insta.workspace = true hex.workspace = true lazy_static.workspace = true diff --git a/frost-secp256k1/Cargo.toml b/frost-secp256k1/Cargo.toml index 4a6ac8e..c6d6935 100644 --- a/frost-secp256k1/Cargo.toml +++ b/frost-secp256k1/Cargo.toml @@ -7,7 +7,7 @@ readme = "README.md" license.workspace = true repository.workspace = true categories.workspace = true -keywords = ["cryptography", "crypto", "threshold", "signature"] +keywords = ["cryptography", "crypto", "secp256k1", "threshold", "signature"] description = "A Schnorr signature scheme over the secp256k1 curve that supports FROST." [package.metadata.docs.rs] @@ -16,16 +16,16 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] document-features.workspace = true -frost-core = { path = "../frost-core", version = "2.1.0", default-features = false } -frost-rerandomized = { path = "../frost-rerandomized", version = "2.1.0", default-features = false } +frost-core.workspace = true +frost-rerandomized.workspace = true k256 = { version = "0.13.0", features = ["arithmetic", "expose-field", "hash2curve"], default-features = false } rand_core.workspace = true sha2 = { version = "0.10.2", default-features = false } [dev-dependencies] criterion.workspace = true -frost-core = { path = "../frost-core", version = "2.1.0", features = ["test-impl"] } -frost-rerandomized = { path = "../frost-rerandomized", version = "2.1.0", features = ["test-impl"] } +frost-core = { workspace = true, features = ["std", "test-impl"] } +frost-rerandomized = { workspace = true, features = ["std", "test-impl"] } insta.workspace = true hex.workspace = true lazy_static.workspace = true diff --git a/gencode/Cargo.toml b/gencode/Cargo.toml index 8487d9e..44dead9 100644 --- a/gencode/Cargo.toml +++ b/gencode/Cargo.toml @@ -4,8 +4,6 @@ version = "0.1.0" edition.workspace = true publish = false -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] regex = "1.6.0" serde_json.workspace = true From aed1ea885c29efe38f5df27fd0f8cd7b2ac380a5 Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Mon, 24 Feb 2025 16:57:22 +0300 Subject: [PATCH 14/86] feat(frost-core): add (de)serialization for `VerifiableSecretSharingSharingSharingCommitment` (#878) * feat(frost-core): add (de)serialization for `VerifiableSecretSharingSharingSharingCommitment` * Update frost-core/src/keys.rs Co-authored-by: Conrado Gouvea * Update frost-core/src/keys.rs Co-authored-by: Conrado Gouvea * add tests * fix doc --------- Co-authored-by: Conrado Gouvea --- frost-core/src/keys.rs | 24 ++++ frost-core/src/tests/vss_commitment.rs | 124 +++++++++++++++++- frost-ed25519/src/tests/vss_commitment.rs | 25 ++++ frost-ed448/src/tests/vss_commitment.rs | 25 ++++ frost-p256/src/tests/vss_commitment.rs | 20 +++ .../src/tests/vss_commitment.rs | 26 ++++ .../src/tests/vss_commitment.rs | 25 ++++ frost-secp256k1/src/tests/vss_commitment.rs | 25 ++++ 8 files changed, 293 insertions(+), 1 deletion(-) diff --git a/frost-core/src/keys.rs b/frost-core/src/keys.rs index d146115..e9c8f4b 100644 --- a/frost-core/src/keys.rs +++ b/frost-core/src/keys.rs @@ -327,6 +327,11 @@ where .collect::>>() } + /// Serialize the whole commitment vector as a single byte vector. + pub fn serialize_whole(&self) -> Result, Error> { + self.serialize().map(|v| v.concat()) + } + /// Returns VerifiableSecretSharingCommitment from an iterator of serialized /// CoefficientCommitments (e.g. a [`Vec>`]). pub fn deserialize(serialized_coefficient_commitments: I) -> Result> @@ -342,6 +347,25 @@ where Ok(Self::new(coefficient_commitments)) } + /// Deserialize a whole commitment vector from a single byte vector as returned by + /// [`VerifiableSecretSharingCommitment::serialize_whole()`]. + pub fn deserialize_whole(bytes: &[u8]) -> Result> { + // Get size from the size of the generator + let generator = ::generator(); + let len = ::serialize(&generator) + .expect("serializing the generator always works") + .as_ref() + .len(); + + let serialized_coefficient_commitments = bytes.chunks_exact(len); + + if !serialized_coefficient_commitments.remainder().is_empty() { + return Err(Error::InvalidCoefficient); + } + + Self::deserialize(serialized_coefficient_commitments) + } + /// Get the VerifyingKey matching this commitment vector (which is the first /// element in the vector), or an error if the vector is empty. pub(crate) fn verifying_key(&self) -> Result, Error> { diff --git a/frost-core/src/tests/vss_commitment.rs b/frost-core/src/tests/vss_commitment.rs index 1046522..25fe7a2 100644 --- a/frost-core/src/tests/vss_commitment.rs +++ b/frost-core/src/tests/vss_commitment.rs @@ -3,7 +3,7 @@ use crate::{ keys::{CoefficientCommitment, VerifiableSecretSharingCommitment}, tests::helpers::generate_element, - Group, + Error, Group, }; use debugless_unwrap::DebuglessUnwrap; use rand_core::{CryptoRng, RngCore}; @@ -46,6 +46,40 @@ pub fn check_serialize_vss_commitment(mu .all(|(e, c)| e.as_ref() == c)); } +/// Test serialize_whole VerifiableSecretSharingCommitment +pub fn check_serialize_whole_vss_commitment(mut rng: R) { + // Generate test CoefficientCommitments + + // --- + let input_1 = generate_element::(&mut rng); + let input_2 = generate_element::(&mut rng); + let input_3 = generate_element::(&mut rng); + + let coeff_comms = vec![ + CoefficientCommitment::::new(input_1), + CoefficientCommitment::new(input_2), + CoefficientCommitment::new(input_3), + ]; + + // --- + + let expected = [ + ::serialize(&input_1).unwrap(), + ::serialize(&input_2).unwrap(), + ::serialize(&input_3).unwrap(), + ] + .into_iter() + .map(|element| element.as_ref().to_vec()) + .collect::>() + .concat(); + + let vss_commitment = VerifiableSecretSharingCommitment(coeff_comms) + .serialize_whole() + .unwrap(); + + assert!(expected == vss_commitment); +} + /// Test deserialize VerifiableSecretSharingCommitment pub fn check_deserialize_vss_commitment(mut rng: R) { // Generate test CoefficientCommitments @@ -76,6 +110,40 @@ pub fn check_deserialize_vss_commitment( assert!(expected == vss_value.unwrap()); } +/// Test deserialize_whole VerifiableSecretSharingCommitment +pub fn check_deserialize_whole_vss_commitment(mut rng: R) { + // Generate test CoefficientCommitments + + // --- + let input_1 = generate_element::(&mut rng); + let input_2 = generate_element::(&mut rng); + let input_3 = generate_element::(&mut rng); + + let coeff_comms = vec![ + CoefficientCommitment::::new(input_1), + CoefficientCommitment::new(input_2), + CoefficientCommitment::new(input_3), + ]; + // --- + + let expected = VerifiableSecretSharingCommitment(coeff_comms); + + let data = vec![ + ::serialize(&input_1).unwrap(), + ::serialize(&input_2).unwrap(), + ::serialize(&input_3).unwrap(), + ] + .into_iter() + .map(|element| element.as_ref().to_vec()) + .collect::>() + .concat(); + + let vss_value = VerifiableSecretSharingCommitment::deserialize_whole(&data); + + assert!(vss_value.is_ok()); + assert!(expected == vss_value.unwrap()); +} + /// Test deserialize VerifiableSecretSharingCommitment error pub fn check_deserialize_vss_commitment_error( mut rng: R, @@ -109,6 +177,60 @@ pub fn check_deserialize_vss_commitment_error( + mut rng: R, + commitment_helpers: &Value, +) { + // Generate test CoefficientCommitments + + // --- + let values = &commitment_helpers["elements"]; + + let input_1 = generate_element::(&mut rng); + let input_2 = generate_element::(&mut rng); + let input_3 = generate_element::(&mut rng); + + let serialized: ::Serialization = + ::Serialization::try_from( + hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(), + ) + .debugless_unwrap(); + // --- + + let data = vec![ + ::serialize(&input_1).unwrap(), + ::serialize(&input_2).unwrap(), + ::serialize(&input_3).unwrap(), + serialized, + ] + .into_iter() + .map(|element| element.as_ref().to_vec()) + .collect::>() + .concat(); + + let vss_value = VerifiableSecretSharingCommitment::::deserialize_whole(&data); + + assert!(vss_value.is_err()); + + // Generate test CoefficientCommitments with invalid length + + let mut data = vec![ + ::serialize(&input_1).unwrap(), + ::serialize(&input_2).unwrap(), + ::serialize(&input_3).unwrap(), + ] + .into_iter() + .map(|element| element.as_ref().to_vec()) + .collect::>() + .concat(); + data.append(&mut vec![0x00]); + + let vss_value = VerifiableSecretSharingCommitment::::deserialize_whole(&data); + + assert_eq!(vss_value, Err(Error::InvalidCoefficient)); +} + /// Test computing the public key package from a list of commitments. pub fn check_compute_public_key_package(mut rng: R) { let max_signers = 3; diff --git a/frost-ed25519/src/tests/vss_commitment.rs b/frost-ed25519/src/tests/vss_commitment.rs index c1c128f..3e0fcd2 100644 --- a/frost-ed25519/src/tests/vss_commitment.rs +++ b/frost-ed25519/src/tests/vss_commitment.rs @@ -16,12 +16,28 @@ fn check_serialize_vss_commitment() { frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); } +#[test] +fn check_serialize_whole_vss_commitment() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_serialize_whole_vss_commitment::( + rng, + ); +} + #[test] fn check_deserialize_vss_commitment() { let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment::(rng); } +#[test] +fn check_deserialize_whole_vss_commitment() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment::( + rng, + ); +} + #[test] fn check_deserialize_vss_commitment_error() { let rng = rand::rngs::OsRng; @@ -30,6 +46,15 @@ fn check_deserialize_vss_commitment_error() { ); } +#[test] +fn check_deserialize_whole_vss_commitment_error() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment_error::< + Ed25519Sha512, + _, + >(rng, &ELEMENTS); +} + #[test] fn check_compute_public_key_package() { let rng = rand::rngs::OsRng; diff --git a/frost-ed448/src/tests/vss_commitment.rs b/frost-ed448/src/tests/vss_commitment.rs index c5beea6..7e1f834 100644 --- a/frost-ed448/src/tests/vss_commitment.rs +++ b/frost-ed448/src/tests/vss_commitment.rs @@ -16,12 +16,28 @@ fn check_serialize_vss_commitment() { frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); } +#[test] +fn check_serialize_whole_vss_commitment() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_serialize_whole_vss_commitment::( + rng, + ); +} + #[test] fn check_deserialize_vss_commitment() { let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment::(rng); } +#[test] +fn check_deserialize_whole_vss_commitment() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment::( + rng, + ); +} + #[test] fn check_deserialize_vss_commitment_error() { let rng = rand::rngs::OsRng; @@ -30,6 +46,15 @@ fn check_deserialize_vss_commitment_error() { ); } +#[test] +fn check_deserialize_whole_vss_commitment_error() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment_error::< + Ed448Shake256, + _, + >(rng, &ELEMENTS); +} + #[test] fn check_compute_public_key_package() { let rng = rand::rngs::OsRng; diff --git a/frost-p256/src/tests/vss_commitment.rs b/frost-p256/src/tests/vss_commitment.rs index 2445791..44c08c5 100644 --- a/frost-p256/src/tests/vss_commitment.rs +++ b/frost-p256/src/tests/vss_commitment.rs @@ -16,12 +16,24 @@ fn check_serialize_vss_commitment() { frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); } +#[test] +fn check_serialize_whole_vss_commitment() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_serialize_whole_vss_commitment::(rng); +} + #[test] fn check_deserialize_vss_commitment() { let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment::(rng); } +#[test] +fn check_deserialize_whole_vss_commitment() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment::(rng); +} + #[test] fn check_deserialize_vss_commitment_error() { let rng = rand::rngs::OsRng; @@ -30,6 +42,14 @@ fn check_deserialize_vss_commitment_error() { ); } +#[test] +fn check_deserialize_whole_vss_commitment_error() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment_error::( + rng, &ELEMENTS, + ); +} + #[test] fn check_compute_public_key_package() { let rng = rand::rngs::OsRng; diff --git a/frost-ristretto255/src/tests/vss_commitment.rs b/frost-ristretto255/src/tests/vss_commitment.rs index 66c6d37..06b16e4 100644 --- a/frost-ristretto255/src/tests/vss_commitment.rs +++ b/frost-ristretto255/src/tests/vss_commitment.rs @@ -16,6 +16,14 @@ fn check_serialize_vss_commitment() { frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); } +#[test] +fn check_serialize_whole_vss_commitment() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_serialize_whole_vss_commitment::( + rng, + ); +} + #[test] fn check_deserialize_vss_commitment() { let rng = rand::rngs::OsRng; @@ -24,6 +32,15 @@ fn check_deserialize_vss_commitment() { ); } +#[test] +fn check_deserialize_whole_vss_commitment() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment::< + Ristretto255Sha512, + _, + >(rng); +} + #[test] fn check_deserialize_vss_commitment_error() { let rng = rand::rngs::OsRng; @@ -33,6 +50,15 @@ fn check_deserialize_vss_commitment_error() { >(rng, &ELEMENTS); } +#[test] +fn check_deserialize_whole_vss_commitment_error() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment_error::< + Ristretto255Sha512, + _, + >(rng, &ELEMENTS); +} + #[test] fn check_compute_public_key_package() { let rng = rand::rngs::OsRng; diff --git a/frost-secp256k1-tr/src/tests/vss_commitment.rs b/frost-secp256k1-tr/src/tests/vss_commitment.rs index 0a09068..f264c33 100644 --- a/frost-secp256k1-tr/src/tests/vss_commitment.rs +++ b/frost-secp256k1-tr/src/tests/vss_commitment.rs @@ -16,6 +16,14 @@ fn check_serialize_vss_commitment() { frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); } +#[test] +fn check_serialize_whole_vss_commitment() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_serialize_whole_vss_commitment::( + rng, + ); +} + #[test] fn check_deserialize_vss_commitment() { let rng = rand::rngs::OsRng; @@ -24,6 +32,14 @@ fn check_deserialize_vss_commitment() { ); } +#[test] +fn check_deserialize_whole_vss_commitment() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment::( + rng, + ); +} + #[test] fn check_deserialize_vss_commitment_error() { let rng = rand::rngs::OsRng; @@ -32,6 +48,15 @@ fn check_deserialize_vss_commitment_error() { ); } +#[test] +fn check_deserialize_whole_vss_commitment_error() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment_error::< + Secp256K1Sha256TR, + _, + >(rng, &ELEMENTS); +} + #[test] fn check_compute_public_key_package() { let rng = rand::rngs::OsRng; diff --git a/frost-secp256k1/src/tests/vss_commitment.rs b/frost-secp256k1/src/tests/vss_commitment.rs index 5574f59..79aa89a 100644 --- a/frost-secp256k1/src/tests/vss_commitment.rs +++ b/frost-secp256k1/src/tests/vss_commitment.rs @@ -16,12 +16,28 @@ fn check_serialize_vss_commitment() { frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); } +#[test] +fn check_serialize_whole_vss_commitment() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_serialize_whole_vss_commitment::( + rng, + ); +} + #[test] fn check_deserialize_vss_commitment() { let rng = rand::rngs::OsRng; frost_core::tests::vss_commitment::check_deserialize_vss_commitment::(rng); } +#[test] +fn check_deserialize_whole_vss_commitment() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment::( + rng, + ); +} + #[test] fn check_deserialize_vss_commitment_error() { let rng = rand::rngs::OsRng; @@ -30,6 +46,15 @@ fn check_deserialize_vss_commitment_error() { ); } +#[test] +fn check_deserialize_whole_vss_commitment_error() { + let rng = rand::rngs::OsRng; + frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment_error::< + Secp256K1Sha256, + _, + >(rng, &ELEMENTS); +} + #[test] fn check_compute_public_key_package() { let rng = rand::rngs::OsRng; From 2a6673fdf576a65f7e24407ba3e8486e56cb585d Mon Sep 17 00:00:00 2001 From: crStiv Date: Thu, 10 Apr 2025 16:33:37 +0300 Subject: [PATCH 15/86] docs: Add network topologies section with diagrams (#883) * Update frost.md * Update SUMMARY.md * Update frost.md --- book/src/SUMMARY.md | 1 + book/src/frost.md | 75 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 004ef64..4d75f2d 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -2,6 +2,7 @@ [ZF FROST](index.md) - [Understanding FROST](frost.md) + - [Network Topologies](frost.md#network-topologies) - [Tutorial](tutorial.md) - [Importing and General Information](tutorial/importing.md) - [Trusted Dealer Key Generation](tutorial/trusted-dealer.md) diff --git a/book/src/frost.md b/book/src/frost.md index 7c34073..9bf5668 100644 --- a/book/src/frost.md +++ b/book/src/frost.md @@ -78,6 +78,81 @@ be able to produce the final signature. Of course, the Coordinator is still free to start the process with only 2 participants if they wish. ``` +## Network Topologies + +FROST supports different network topologies for both signing and DKG (Distributed Key Generation) processes. Understanding these topologies is crucial for implementing FROST in a way that best suits your application's needs. + +### Signing Topologies + +#### 1. Centralized Coordinator + +```ascii + Coordinator + / | \ + / | \ + / | \ + Signer1 Signer2 Signer3 +``` + +This is the default topology where: +- A single coordinator (which may or may not be a signer) manages the signing process +- Signers only communicate with the coordinator +- Pros: Simple to implement, clear communication flow +- Cons: Single point of failure, potential bottleneck + +#### 2. Distributed Coordination + +```ascii + Signer1 -------- Signer2 + \ / + \ / + \ / + Signer3 +``` + +In this topology: +- Each signer acts as their own coordinator +- All signers communicate directly with each other +- Pros: No single point of failure +- Cons: More complex implementation, requires full mesh networking + +### DKG Topologies + +#### 1. Full Mesh (Recommended) + +```ascii + Node1 --------- Node2 + | \ / | + | \ / | + | \ / | + | \ / | + | \ / | + Node4 --- Node3 +``` + +For DKG: +- All participants need to communicate directly with each other +- Requires authenticated and confidential channels between all pairs +- Requires a broadcast channel for public values +- Most secure but requires more complex networking setup + +#### 2. Star with Broadcast Hub + +```ascii + Hub + / | \ + / | \ + Node1 | Node3 + | + Node2 +``` + +Alternative DKG setup: +- A central hub relays messages between participants +- Simpler networking requirements +- Hub must be trusted for message delivery (but cannot learn secrets) +- May be suitable for controlled environments + ## Verifying Signatures Signature verification is carried out as normal with single-party signatures, From ca1c98c99bfd903ac0b34f543ad91475fe6488c3 Mon Sep 17 00:00:00 2001 From: shigeyuki azuchi Date: Sat, 12 Apr 2025 00:23:03 +0900 Subject: [PATCH 16/86] Add tests for all test vectors in DKG (#875) * Modify DKG key generation test to include additional testing of all participant data * Fix wrong signing share * Fix wrong proof of knowledge and add missing data for participant 2,3 * Fix format --- frost-core/src/tests/vectors_dkg.rs | 211 ++++++++++-------- frost-ed25519/tests/helpers/vectors_dkg.json | 4 +- frost-ed448/tests/helpers/vectors_dkg.json | 2 +- .../tests/helpers/vectors_dkg.json | 16 +- 4 files changed, 129 insertions(+), 104 deletions(-) diff --git a/frost-core/src/tests/vectors_dkg.rs b/frost-core/src/tests/vectors_dkg.rs index 894eda2..ddd10f2 100644 --- a/frost-core/src/tests/vectors_dkg.rs +++ b/frost-core/src/tests/vectors_dkg.rs @@ -1,6 +1,5 @@ //! Helper function for testing with test vectors. use alloc::{collections::BTreeMap, string::ToString, vec::Vec}; - use debugless_unwrap::DebuglessUnwrap; use hex::{self}; use serde_json::Value; @@ -44,76 +43,92 @@ fn json_to_element(vector: &Value) -> ::Seria /// Parse test vectors for a given ciphersuite. #[allow(clippy::type_complexity)] -pub fn parse_test_vectors_dkg(json_vectors: &Value) -> DKGTestVectors { +pub fn parse_test_vectors_dkg(json_vectors: &Value) -> Vec> { + let mut vectors: Vec> = Vec::new(); let inputs = &json_vectors["inputs"]; - let participant = &inputs["1"]; - - let participant_1_id: Identifier = (participant["identifier"].as_u64().unwrap() as u16) - .try_into() - .unwrap(); - let participant_2_id: Identifier = (inputs["2"]["identifier"].as_u64().unwrap() as u16) - .try_into() - .unwrap(); - let participant_3_id: Identifier = (inputs["3"]["identifier"].as_u64().unwrap() as u16) - .try_into() - .unwrap(); - - let mut round1_packages = BTreeMap::new(); - round1_packages.insert(participant_2_id, build_round_1_package(json_vectors, 2)); - round1_packages.insert(participant_3_id, build_round_1_package(json_vectors, 3)); - - let mut round2_packages = BTreeMap::new(); - round2_packages.insert(participant_2_id, build_round_2_package(json_vectors, 2)); - round2_packages.insert(participant_3_id, build_round_2_package(json_vectors, 3)); + let max_participants = json_vectors["config"]["MAX_PARTICIPANTS"].as_u64().unwrap() as u16; + let min_signers = json_vectors["config"]["MIN_PARTICIPANTS"].as_u64().unwrap() as u16; - let secret = - SigningKey::deserialize(json_to_scalar::(&participant["signing_key"]).as_ref()).unwrap(); + for i in 1..=max_participants { + let participant_id_str = &i.to_string(); + let participant_data = &inputs[participant_id_str]; + let participant_id: Identifier = (participant_data["identifier"].as_u64().unwrap() + as u16) + .try_into() + .unwrap(); - let coefficient = <::Field as Field>::deserialize(&json_to_scalar::( - &participant["coefficient"], - )) - .unwrap(); + let mut round1_packages = BTreeMap::new(); + let mut round2_packages = BTreeMap::new(); + for (other_participant_id_str, other_participant_data) in inputs.as_object().unwrap() { + if participant_id_str == other_participant_id_str { + continue; + } + match other_participant_id_str.parse::() { + Ok(id) => id, + Err(_) => continue, + }; + let other_participant_id: Identifier = + (other_participant_data["identifier"].as_u64().unwrap() as u16) + .try_into() + .unwrap(); + round1_packages.insert( + other_participant_id, + build_round_1_package(other_participant_data), + ); + round2_packages.insert( + other_participant_id, + build_round_2_package(participant_data, other_participant_id_str), + ); + } + + let secret = + SigningKey::deserialize(json_to_scalar::(&participant_data["signing_key"]).as_ref()) + .unwrap(); + + let coefficient = <::Field as Field>::deserialize(&json_to_scalar::( + &participant_data["coefficient"], + )) + .unwrap(); - let public_key_package = build_public_key_package(json_vectors); + let public_key_package = build_public_key_package(json_vectors); - let verifying_share = - VerifyingShare::deserialize(json_to_element::(&participant["verifying_share"]).as_ref()) - .unwrap(); + let verifying_share = VerifyingShare::deserialize( + json_to_element::(&participant_data["verifying_share"]).as_ref(), + ) + .unwrap(); - let verifying_key = - VerifyingKey::deserialize(json_to_element::(&inputs["verifying_key"]).as_ref()).unwrap(); + let verifying_key = + VerifyingKey::deserialize(json_to_element::(&inputs["verifying_key"]).as_ref()) + .unwrap(); - let signing_share = - SigningShare::deserialize(json_to_scalar::(&participant["signing_share"]).as_ref()) - .unwrap(); + let signing_share = SigningShare::deserialize( + json_to_scalar::(&participant_data["signing_share"]).as_ref(), + ) + .unwrap(); - let key_package = KeyPackage { - header: Header::default(), - identifier: participant_1_id, - signing_share, - verifying_share, - verifying_key, - min_signers: 2, - }; - - DKGTestVectors { - secret, - coefficient, - round1_packages, - round2_packages, - public_key_package, - key_package, - participant_id: participant_1_id, + let key_package = KeyPackage { + header: Header::default(), + identifier: participant_id, + signing_share, + verifying_share, + verifying_key, + min_signers, + }; + vectors.push(DKGTestVectors { + secret, + coefficient, + round1_packages, + round2_packages, + public_key_package, + key_package, + participant_id, + }) } + vectors } -fn build_round_1_package( - json_vectors: &Value, - participant_num: usize, -) -> Round1Package { - let inputs = &json_vectors["inputs"]; - let participant = &inputs[participant_num.to_string()]; - let vss_commitment = participant["vss_commitments"] +fn build_round_1_package(json_vectors: &Value) -> Round1Package { + let vss_commitment = json_vectors["vss_commitments"] .as_array() .unwrap() .iter() @@ -123,7 +138,7 @@ fn build_round_1_package( let commitment = VerifiableSecretSharingCommitment::deserialize(vss_commitment).unwrap(); let proof_of_knowledge = Signature::deserialize( - &hex::decode(participant["proof_of_knowledge"].as_str().unwrap()).unwrap(), + &hex::decode(json_vectors["proof_of_knowledge"].as_str().unwrap()).unwrap(), ) .debugless_unwrap(); @@ -136,12 +151,10 @@ fn build_round_1_package( fn build_round_2_package( json_vectors: &Value, - sender_num: usize, + sender_num: &String, ) -> Round2Package { - let inputs = &json_vectors["inputs"]; - let signing_share = SigningShare::deserialize( - json_to_scalar::(&inputs["1"]["signing_shares"][sender_num.to_string()]).as_ref(), + json_to_scalar::(&json_vectors["signing_shares"][sender_num]).as_ref(), ) .unwrap(); @@ -182,41 +195,43 @@ fn build_public_key_package(json_vectors: &Value) -> PublicKeyPa /// Test DKG with the given test vectors for a ciphersuite pub fn check_dkg_keygen(json_vectors: &Value) { - let DKGTestVectors { - secret, - coefficient, - round1_packages, - round2_packages, - public_key_package, - key_package, - participant_id, - } = parse_test_vectors_dkg(json_vectors); - - let min_signers = 2; - let max_signers = 3; - - let (coefficients, commitment) = generate_secret_polynomial( - &secret as &SigningKey, - max_signers, - min_signers, - vec![coefficient], - ) - .unwrap(); + for dkg_vectors in parse_test_vectors_dkg(json_vectors) { + let DKGTestVectors { + secret, + coefficient, + round1_packages, + round2_packages, + public_key_package, + key_package, + participant_id, + } = dkg_vectors; + + let min_signers = 2; + let max_signers = 3; + + let (coefficients, commitment) = generate_secret_polynomial( + &secret as &SigningKey, + max_signers, + min_signers, + vec![coefficient], + ) + .unwrap(); - let round1_secret_package = SecretPackage::new( - participant_id, - coefficients, - commitment.clone(), - min_signers, - max_signers, - ); + let round1_secret_package = SecretPackage::new( + participant_id, + coefficients, + commitment.clone(), + min_signers, + max_signers, + ); - let (round2_secret_package, _round2_packages_1) = - part2(round1_secret_package, &round1_packages).unwrap(); + let (round2_secret_package, _round2_packages_1) = + part2(round1_secret_package, &round1_packages).unwrap(); - let (expected_key_package, expected_public_key_package) = - part3(&round2_secret_package, &round1_packages, &round2_packages).unwrap(); + let (expected_key_package, expected_public_key_package) = + part3(&round2_secret_package, &round1_packages, &round2_packages).unwrap(); - assert_eq!(public_key_package, expected_public_key_package); - assert_eq!(key_package, expected_key_package); + assert_eq!(public_key_package, expected_public_key_package); + assert_eq!(key_package, expected_key_package); + } } diff --git a/frost-ed25519/tests/helpers/vectors_dkg.json b/frost-ed25519/tests/helpers/vectors_dkg.json index bcad1c9..4284fe4 100644 --- a/frost-ed25519/tests/helpers/vectors_dkg.json +++ b/frost-ed25519/tests/helpers/vectors_dkg.json @@ -35,7 +35,7 @@ "3": "7fde55b354d5d8dddc940fe932de5d1a6110b9bc4edeba2db7b32c34074d3a0a" }, "verifying_share": "f326b756ed38b43a94bdac698e044d9e3f3a08a40e7c9d2e5346dd5bfaadf2f5", - "signing_share": "80a16ad6cf78be27995d5e312722ff2d42a5b08380b49dbc2e7e65c124220e0a" + "signing_share": "b9ed88cb30a9ddbc00dbdf7c40d6c76f791989cf9f3119bad899287c5f6c2303" }, "3": { "identifier": 3, @@ -49,7 +49,7 @@ "2": "6b3e57fab1e74e61aacfd93d4926172a7e878a48540fe97367721e38485b3a00" }, "verifying_share": "6bc91a2755902d955ce220ad0df6fbf57162260949d40bcf5a69cfffec9c085a", - "signing_share": "8bd411475cdc423c1ba94cf11b80e776592d5f675419398800d17173d673f40a" + "signing_share": "35b2b8fad8352f6e6a7c8fee082f45a339a37f0085c1f662c44c3f10903d970d" } } } diff --git a/frost-ed448/tests/helpers/vectors_dkg.json b/frost-ed448/tests/helpers/vectors_dkg.json index a0943e8..09a2297 100644 --- a/frost-ed448/tests/helpers/vectors_dkg.json +++ b/frost-ed448/tests/helpers/vectors_dkg.json @@ -42,7 +42,7 @@ "proof_of_knowledge": "eb47491f2461792114d357d02102c1a806451cfa88f1297f7a671a87a04de0ffde478ec1c2b91e743379254fe84eb2e0d170c69aec88bc1980bf8009ffc93d6ee0c3713681aa303cf85595bd975953318ab07be9e56dc6ba22465793ee337e383562fafc7525c05b36732b93f5f4fee22300", "signing_shares": { "1": "77c1831adb90cd94d0404ec2a8730af57d4e29e10ad02e26ee61328c95f1258a17bad98617632d92ee5aa8224793231db3a599d322b82d2300", - "2": "e29c8b642abfa741710945aedadf34ac73ef6863c3e56d599cc3c58039d45b7382674cbd2c8e064c8bae33851c9166536181b83fe34ce02200" + "2": "6aa7e48f3d7f4de3b1a0bb95a5085705e5618b8f67d89cf43dd8d649057d295ff0c837e4a918634a94a773606ed156c9db543c3bcd1e2a0e00" }, "verifying_share": "3a1b5a9945fc64b088174c34e16dbced81f824fe8f9f12d1ec98afd4ea593a6ec75a74f70b77522c66681bd468080b525963dbcc2785d53a00", "signing_share": "da7c3a1048da9e6b8e480a72d48479ab4724f9804e96a44c7f7e04691ffbfdd7a56c2a9644e1ad1075baeae746ce8317f63104e87363652900" diff --git a/frost-secp256k1-tr/tests/helpers/vectors_dkg.json b/frost-secp256k1-tr/tests/helpers/vectors_dkg.json index 9fcbc83..55755fb 100644 --- a/frost-secp256k1-tr/tests/helpers/vectors_dkg.json +++ b/frost-secp256k1-tr/tests/helpers/vectors_dkg.json @@ -13,7 +13,7 @@ "signing_key": "68e3f6904c6043973515a36bf7801a71597da35733f21305d75a5234f06e4529", "coefficient": "25d2d840a3e2718a431ec69e14ee8a015b000d43c7a9868060f01d5aa52a19d1", "vss_commitments": ["03e7ba4acb164d2bd5eba4f47b3a788109ddb3f88f1181792424fa332123a25ea8", "037495e920a1f032916193aa80ea97a4c3a611dec9ab47ccc969deb664f5f88bbe"], - "proof_of_knowledge": "6689a8d414eb4961308e21f8caa1045236efded4f3de9209dc07547e88be3b42e192de9bed27fb78a7a4d4e35a0422f11f52631b8e66d69e609398eaff2770b8", + "proof_of_knowledge": "191a4ef1851286e2fd6cebd483385452cbb12f43386241854939252c4ed8846b8631f9e69be37ccbd3a0b4593a8f63738747e165a22d0b5786eeb74e59a17837", "signing_shares": { "2": "1dd3cb3e2370e6af22917415f0ad584514807b58b3cc40d2230a26e115f02771", "3": "dd25ee86acd01f996618aa0d1153f5e8fbc929a8e8a18b8f0a15f91d087217e2" @@ -27,7 +27,12 @@ "coefficient": "f7ba0cbbffbea8aaceb3ade54bdbf35bafb1cda15b65ad490e0c63dd069a7c9f", "vss_commitments": ["03ef10370a008cd95e179dc51e2cb7828f30b72d254e5166484f927c84ab326582", "022ce0dac0db217ba326fbbe3e6132d45e2a4bfa0a0c3790d91eacce9a1c2d6a10"], "proof_of_knowledge": "a319dd51cf64b3896c22f54154812d4ae76cfa95f46f53ef69241fd702456fef32da76cc93d3a541ca495b723e793ee90c32440da5f314e2e58a2dc30550314a", - "verifying_share": "029ecb3a4db28a82e7b8d600d42711b02790dde3f063f0ecec6f812c1c5d7dcefc" + "signing_shares": { + "1": "b489a711942526abbb5330a8215d2e740f7dbddec3452006993a8cea3ac278cb", + "3": "20255dc07b1fb78bdf90bd85fd2389c988c8250faee11826656a09142fa9fc97" + }, + "verifying_share": "029ecb3a4db28a82e7b8d600d42711b02790dde3f063f0ecec6f812c1c5d7dcefc", + "signing_share": "9131f1241cd8f95f6439b0f5edc2ecb969a2d3db9c85fe5added77116d41d0ce" }, "3": { "identifier": 3, @@ -35,7 +40,12 @@ "coefficient": "42ff6f39ce4f97f279781378ebcf93df47add84d75882cd31b266e83f76e25f6", "vss_commitments": ["02da186c3863c5600b471a2799cb6f15ae4d8315a2f225c177798880e75ac820a0", "03e6a36e7fa4b117c1aa428886672e3a35d926bb4c585a9b07d8ee9a3387420067"], "proof_of_knowledge": "6e115d9e63fd15d432b380ccf1ec4ed03340fcf96caeae8985aedb5f905b1a65dc422ffe5878988fbbc55454857736c7755d9c8f5ee6822c8833ea21d54dba36", - "verifying_share": "02c98b3c2e9f4bde4cf90dc9c7be639e5adda6ea09fc605239880a22cb836f7145" + "signing_shares": { + "1": "da5c7f5238079835fe71f746364bb8756a7dcb228aeea686fa2aaa44dfec929c", + "2": "0d47e4b622ee3804bff8cfe088653efefe865cce0c065aecbf7e318182b89e2d" + }, + "verifying_share": "02c98b3c2e9f4bde4cf90dc9c7be639e5adda6ea09fc605239880a22cb836f7145", + "signing_share": "30a59cedaae84737d8ef28f9a128db7bd1f1fd8fb3373dfa139ce5e29a4555a9" } } } From d7cd2a299187cabdde5dba07e9147f14c95ee1e0 Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Mon, 14 Apr 2025 07:36:59 -0300 Subject: [PATCH 17/86] docs: add warning about encrypted channel for DKG (#888) --- book/src/SUMMARY.md | 1 - book/src/frost.md | 114 ++++++++++++++++----------------- book/src/tutorial/dkg.md | 6 ++ book/src/zcash/ywallet-demo.md | 6 +- 4 files changed, 66 insertions(+), 61 deletions(-) diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 4d75f2d..004ef64 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -2,7 +2,6 @@ [ZF FROST](index.md) - [Understanding FROST](frost.md) - - [Network Topologies](frost.md#network-topologies) - [Tutorial](tutorial.md) - [Importing and General Information](tutorial/importing.md) - [Trusted Dealer Key Generation](tutorial/trusted-dealer.md) diff --git a/book/src/frost.md b/book/src/frost.md index 9bf5668..4b03a3a 100644 --- a/book/src/frost.md +++ b/book/src/frost.md @@ -78,6 +78,62 @@ be able to produce the final signature. Of course, the Coordinator is still free to start the process with only 2 participants if they wish. ``` +## Verifying Signatures + +Signature verification is carried out as normal with single-party signatures, +along with the signed message and the group verifying key as inputs. + +## Repairing Shares + +Repairing shares allow participants to help another participant recover their +share if they have lost it, or also issue a new share to a new participant +(while keeping the same threshold). + +The repair share functionality requires a threshold of participants to work. +For example, in a 2-of-3 scenario, two participants can help the third recover +their share, or they could issue a new share to move to a 2-of-4 group. + +The functionality works in such a way that each participant running the repair +share function is not able to obtain the share that is being recovered or +issued. + +## Refreshing Shares + +Refreshing shares allow participants (or a subset of them) to update their +shares in a way that maintains the same group public key. Some applications are: + +- Make it harder for attackers to compromise the shares. For example, in a + 2-of-3 threshold scenario, if an attacker steals one participant's device and + all participants refresh their shares, the attacker will need to start over + and steal two shares instead of just one more. +- Remove a participant from the group. For example, in a 2-of-3 threshold + scenario, if two participants decide to remove the third they can both refresh + their shares and the third participant would no longer be able to participate + in signing sessions with the others. (They can also then use the repair share + functionality to issue a new share and move from 2-of-2 back to 2-of-3.) + +```admonish danger +It is critically important to keep in mind that the **Refresh Shares +functionality does not "restore full security" to a group**. While the group +evolves and participants are removed and new participants are added, the +security of the group does not depend only on the threshold of the current +participants being honest, but also **on the threshold of all previous set of +participants being honest**! For example, if Alice, Mallory and Eve form a group +and Mallory is eventually excluded from the group and replaced with Bob, it is +not enough to trust 2 out of 3 between Alice, Bob and Eve. **You also need to +trust that Mallory won't collude with, say, Eve which could have kept her +original pre-refresh share and they could both together recompute the original +key and compromise the group.** If that's an unacceptable risk to your use case, +you will need to migrate to a new group if that makes sense to your application. +``` + +## Ciphersuites + +FROST is a generic protocol that works with any adequate prime-order group, +which in practice are constructed from elliptic curves. The spec specifies +five ciphersuites with the Ristretto255, Ed25519, Ed448, P-256 and secp256k1 +groups. It's possible (though not recommended) to use your own ciphersuite. + ## Network Topologies FROST supports different network topologies for both signing and DKG (Distributed Key Generation) processes. Understanding these topologies is crucial for implementing FROST in a way that best suits your application's needs. @@ -151,60 +207,4 @@ Alternative DKG setup: - A central hub relays messages between participants - Simpler networking requirements - Hub must be trusted for message delivery (but cannot learn secrets) -- May be suitable for controlled environments - -## Verifying Signatures - -Signature verification is carried out as normal with single-party signatures, -along with the signed message and the group verifying key as inputs. - -## Repairing Shares - -Repairing shares allow participants to help another participant recover their -share if they have lost it, or also issue a new share to a new participant -(while keeping the same threshold). - -The repair share functionality requires a threshold of participants to work. -For example, in a 2-of-3 scenario, two participants can help the third recover -their share, or they could issue a new share to move to a 2-of-4 group. - -The functionality works in such a way that each participant running the repair -share function is not able to obtain the share that is being recovered or -issued. - -## Refreshing Shares - -Refreshing shares allow participants (or a subset of them) to update their -shares in a way that maintains the same group public key. Some applications are: - -- Make it harder for attackers to compromise the shares. For example, in a - 2-of-3 threshold scenario, if an attacker steals one participant's device and - all participants refresh their shares, the attacker will need to start over - and steal two shares instead of just one more. -- Remove a participant from the group. For example, in a 2-of-3 threshold - scenario, if two participants decide to remove the third they can both refresh - their shares and the third participant would no longer be able to participate - in signing sessions with the others. (They can also then use the repair share - functionality to issue a new share and move from 2-of-2 back to 2-of-3.) - -```admonish danger -It is critically important to keep in mind that the **Refresh Shares -functionality does not "restore full security" to a group**. While the group -evolves and participants are removed and new participants are added, the -security of the group does not depend only on the threshold of the current -participants being honest, but also **on the threshold of all previous set of -participants being honest**! For example, if Alice, Mallory and Eve form a group -and Mallory is eventually excluded from the group and replaced with Bob, it is -not enough to trust 2 out of 3 between Alice, Bob and Eve. **You also need to -trust that Mallory won't collude with, say, Eve which could have kept her -original pre-refresh share and they could both together recompute the original -key and compromise the group.** If that's an unacceptable risk to your use case, -you will need to migrate to a new group if that makes sense to your application. -``` - -## Ciphersuites - -FROST is a generic protocol that works with any adequate prime-order group, -which in practice are constructed from elliptic curves. The spec specifies -five ciphersuites with the Ristretto255, Ed25519, Ed448, P-256 and secp256k1 -groups. It's possible (though not recommended) to use your own ciphersuite. +- May be suitable for controlled environments \ No newline at end of file diff --git a/book/src/tutorial/dkg.md b/book/src/tutorial/dkg.md index 5bab3dd..4bbd543 100644 --- a/book/src/tutorial/dkg.md +++ b/book/src/tutorial/dkg.md @@ -75,6 +75,12 @@ The `round2::Package`s must be sent to their respective participants with the given `Identifier`s, using an [authenticated and confidential communication channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). +```admonish danger +The `round2::Package`s MUST be encrypted, otherwise an attacker who can read +the content of the packages will be able to recreate the secret being +generated. +``` + ## Part 3 Finally, upon receiving the other participant's `round2::Package`, the DKG is diff --git a/book/src/zcash/ywallet-demo.md b/book/src/zcash/ywallet-demo.md index c36513e..1972695 100644 --- a/book/src/zcash/ywallet-demo.md +++ b/book/src/zcash/ywallet-demo.md @@ -31,7 +31,7 @@ Install the `zcash-sign` tool: cargo install --git https://github.com/ZcashFoundation/frost-zcash-demo.git --locked zcash-sign ``` -Switch to an empty folder which will store the files generate in the demo. +Switch to an empty folder which will store the files generated in the demo. For example: ``` @@ -154,7 +154,7 @@ strings with the public keys of the contacts which will participate (along with the user running the command): ``` -frost-client dkg -d 'Alice, Bob and Eve's group' -s localhost:2744 -S , -t 2 -C redpallas -c alice.toml +frost-client dkg -d "Alice, Bob and Eve's group" -s localhost:2744 -S , -t 2 -C redpallas -c alice.toml ``` The user should then notify the others that a signing session has started (e.g. @@ -163,7 +163,7 @@ They should then run the following, replacing the name of the group if they wish and the threshold number with the one given by the first participant. ``` -frost-client dkg -d 'Alice, Bob and Eve's group' -s localhost:2744 -t 2 -C redpallas +frost-client dkg -d "Alice, Bob and Eve's group" -s localhost:2744 -t 2 -C redpallas ``` ```admonish note From a8c8b6d197bd818e23fc8fcfc457144a909872e0 Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Thu, 24 Apr 2025 07:11:14 -0300 Subject: [PATCH 18/86] clippy fixes (#889) --- frost-core/src/benches.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frost-core/src/benches.rs b/frost-core/src/benches.rs index f89ce4a..fd4ea73 100644 --- a/frost-core/src/benches.rs +++ b/frost-core/src/benches.rs @@ -89,6 +89,8 @@ pub fn bench_sign( let mut group = c.benchmark_group(format!("FROST Signing {name}")); for &n in [3u16, 10, 100, 1000].iter() { let max_signers = n; + // div_ceil is in 1.73.0 which is larger than the current MSRV + #[allow(clippy::manual_div_ceil)] let min_signers = (n * 2 + 2) / 3; group.bench_with_input( From 78c70b4bea25ec118f9f062e98cfa0dc7a1195c3 Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Fri, 6 Jun 2025 00:15:47 +0300 Subject: [PATCH 19/86] chore(frost-core): import `String` from `alloc` (#899) --- frost-core/src/tests/vectors_dkg.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frost-core/src/tests/vectors_dkg.rs b/frost-core/src/tests/vectors_dkg.rs index ddd10f2..be268d2 100644 --- a/frost-core/src/tests/vectors_dkg.rs +++ b/frost-core/src/tests/vectors_dkg.rs @@ -1,5 +1,9 @@ //! Helper function for testing with test vectors. -use alloc::{collections::BTreeMap, string::ToString, vec::Vec}; +use alloc::{ + collections::BTreeMap, + string::{String, ToString}, + vec::Vec, +}; use debugless_unwrap::DebuglessUnwrap; use hex::{self}; use serde_json::Value; From 412f04937582d31f089ea8489a91ba0ddd956a8c Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Fri, 6 Jun 2025 16:53:45 +0300 Subject: [PATCH 20/86] feat: bump MSRV to Rust 1.73 (#900) --- .github/workflows/main.yml | 4 ++-- Cargo.toml | 1 + frost-core/CHANGELOG.md | 2 ++ frost-core/Cargo.toml | 1 + frost-core/src/scalar_mul.rs | 23 +---------------------- frost-core/src/tests/vss_commitment.rs | 1 + frost-ed25519/Cargo.toml | 1 + frost-ed448/Cargo.toml | 1 + frost-ed448/src/lib.rs | 2 +- frost-p256/Cargo.toml | 1 + frost-rerandomized/Cargo.toml | 1 + frost-ristretto255/Cargo.toml | 1 + frost-secp256k1-tr/Cargo.toml | 1 + frost-secp256k1/Cargo.toml | 1 + 14 files changed, 16 insertions(+), 25 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7c6d0e4..708ca19 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,7 +19,7 @@ jobs: - run: cargo build build_msrv: - name: build with MSRV (1.66.1) + name: build with MSRV (1.73) runs-on: ubuntu-latest steps: @@ -34,7 +34,7 @@ jobs: - run: cargo update -Z minimal-versions # Now check that `cargo build` works with respect to the oldest possible # deps and the stated MSRV - - uses: dtolnay/rust-toolchain@1.66.1 + - uses: dtolnay/rust-toolchain@1.73 - run: cargo build --all-features # TODO: this is filling up the disk space in CI. See if there is a way to diff --git a/Cargo.toml b/Cargo.toml index c940a76..57c5733 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ [workspace.package] edition = "2021" +rust-version = "1.73" version = "2.1.0" authors = [ "Deirdre Connolly ", diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index 46eaca6..a0a4465 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -4,6 +4,8 @@ Entries are listed in reverse chronological order. ## Unreleased +* MSRV has been bumped to Rust 1.73 + ## 2.1.0 * It is now possible to identify the culprit in `frost_core::keys::dkg::part3()` diff --git a/frost-core/Cargo.toml b/frost-core/Cargo.toml index 0907e8b..ecd3266 100644 --- a/frost-core/Cargo.toml +++ b/frost-core/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "frost-core" edition.workspace = true +rust-version.workspace = true version.workspace = true authors.workspace = true readme = "README.md" diff --git a/frost-core/src/scalar_mul.rs b/frost-core/src/scalar_mul.rs index e8215c4..3d82b28 100644 --- a/frost-core/src/scalar_mul.rs +++ b/frost-core/src/scalar_mul.rs @@ -14,27 +14,6 @@ use alloc::vec::Vec; use crate::{Ciphersuite, Element, Field, Group, Scalar}; -/// Calculates the quotient of `self` and `rhs`, rounding the result towards positive infinity. -/// -/// # Panics -/// -/// This function will panic if `rhs` is 0 or the division results in overflow. -/// -/// This function is similar to `div_ceil` that is [available on -/// Nightly](https://github.com/rust-lang/rust/issues/88581). -/// -// TODO: remove this function and use `div_ceil()` instead when `int_roundings` -// is stabilized. -const fn div_ceil(lhs: usize, rhs: usize) -> usize { - let d = lhs / rhs; - let r = lhs % rhs; - if r > 0 && rhs > 0 { - d + 1 - } else { - d - } -} - /// A trait for transforming a scalar generic over a ciphersuite to a non-adjacent form (NAF). pub trait NonAdjacentForm { fn non_adjacent_form(&self, w: usize) -> Vec; @@ -81,7 +60,7 @@ where let mut naf = vec![0; naf_length]; // Get the number of 64-bit limbs we need. - let num_limbs: usize = div_ceil(naf_length, u64::BITS as usize); + let num_limbs: usize = naf_length.div_ceil(u64::BITS as usize); let mut x_u64 = vec![0u64; num_limbs]; diff --git a/frost-core/src/tests/vss_commitment.rs b/frost-core/src/tests/vss_commitment.rs index 25fe7a2..7b32b5e 100644 --- a/frost-core/src/tests/vss_commitment.rs +++ b/frost-core/src/tests/vss_commitment.rs @@ -5,6 +5,7 @@ use crate::{ tests::helpers::generate_element, Error, Group, }; +use alloc::vec::Vec; use debugless_unwrap::DebuglessUnwrap; use rand_core::{CryptoRng, RngCore}; use serde_json::Value; diff --git a/frost-ed25519/Cargo.toml b/frost-ed25519/Cargo.toml index 36523fd..b0e1a41 100644 --- a/frost-ed25519/Cargo.toml +++ b/frost-ed25519/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "frost-ed25519" edition.workspace = true +rust-version.workspace = true version.workspace = true authors.workspace = true readme = "README.md" diff --git a/frost-ed448/Cargo.toml b/frost-ed448/Cargo.toml index bc7d85b..81f7be3 100644 --- a/frost-ed448/Cargo.toml +++ b/frost-ed448/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "frost-ed448" edition.workspace = true +rust-version.workspace = true version.workspace = true authors.workspace = true readme = "README.md" diff --git a/frost-ed448/src/lib.rs b/frost-ed448/src/lib.rs index 5652356..f8d7570 100644 --- a/frost-ed448/src/lib.rs +++ b/frost-ed448/src/lib.rs @@ -7,7 +7,7 @@ extern crate alloc; -use std::collections::BTreeMap; +use alloc::collections::BTreeMap; use ed448_goldilocks::{ curve::{edwards::CompressedEdwardsY, ExtendedPoint}, diff --git a/frost-p256/Cargo.toml b/frost-p256/Cargo.toml index 1376e4d..d8ca78c 100644 --- a/frost-p256/Cargo.toml +++ b/frost-p256/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "frost-p256" edition.workspace = true +rust-version.workspace = true version.workspace = true authors.workspace = true readme = "README.md" diff --git a/frost-rerandomized/Cargo.toml b/frost-rerandomized/Cargo.toml index cc3a1d3..0045aab 100644 --- a/frost-rerandomized/Cargo.toml +++ b/frost-rerandomized/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "frost-rerandomized" edition.workspace = true +rust-version.workspace = true version.workspace = true authors.workspace = true readme = "README.md" diff --git a/frost-ristretto255/Cargo.toml b/frost-ristretto255/Cargo.toml index 06e8803..53f9f0b 100644 --- a/frost-ristretto255/Cargo.toml +++ b/frost-ristretto255/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "frost-ristretto255" edition.workspace = true +rust-version.workspace = true version.workspace = true authors.workspace = true readme = "README.md" diff --git a/frost-secp256k1-tr/Cargo.toml b/frost-secp256k1-tr/Cargo.toml index 1f9d16e..17ba380 100644 --- a/frost-secp256k1-tr/Cargo.toml +++ b/frost-secp256k1-tr/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "frost-secp256k1-tr" edition.workspace = true +rust-version.workspace = true version.workspace = true authors.workspace = true readme = "README.md" diff --git a/frost-secp256k1/Cargo.toml b/frost-secp256k1/Cargo.toml index c6d6935..ce2a63e 100644 --- a/frost-secp256k1/Cargo.toml +++ b/frost-secp256k1/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "frost-secp256k1" edition.workspace = true +rust-version.workspace = true version.workspace = true authors.workspace = true readme = "README.md" From 43787272e2d424bf329f2d07e9f4e20bdb1f0550 Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Fri, 6 Jun 2025 19:50:57 +0300 Subject: [PATCH 21/86] feat: bump MSRV to Rust 1.81 (#901) --- .github/workflows/main.yml | 4 ++-- Cargo.toml | 4 ++-- frost-core/CHANGELOG.md | 3 ++- frost-core/Cargo.toml | 7 ++----- frost-core/src/error.rs | 7 +------ frost-core/src/lib.rs | 2 +- frost-ed25519/Cargo.toml | 9 +++------ frost-ed25519/src/lib.rs | 2 +- frost-ed448/Cargo.toml | 9 +++------ frost-ed448/src/lib.rs | 1 - frost-p256/Cargo.toml | 9 +++------ frost-p256/src/lib.rs | 2 +- frost-rerandomized/Cargo.toml | 3 --- frost-rerandomized/src/lib.rs | 2 +- frost-ristretto255/Cargo.toml | 9 +++------ frost-ristretto255/src/lib.rs | 2 +- frost-secp256k1-tr/Cargo.toml | 11 ++++------- frost-secp256k1-tr/src/lib.rs | 2 +- frost-secp256k1-tr/tests/helpers/mod.rs | 2 +- frost-secp256k1/Cargo.toml | 9 +++------ frost-secp256k1/src/lib.rs | 2 +- 21 files changed, 36 insertions(+), 65 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 708ca19..20cbcd7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,7 +19,7 @@ jobs: - run: cargo build build_msrv: - name: build with MSRV (1.73) + name: build with MSRV (1.81) runs-on: ubuntu-latest steps: @@ -34,7 +34,7 @@ jobs: - run: cargo update -Z minimal-versions # Now check that `cargo build` works with respect to the oldest possible # deps and the stated MSRV - - uses: dtolnay/rust-toolchain@1.73 + - uses: dtolnay/rust-toolchain@1.81 - run: cargo build --all-features # TODO: this is filling up the disk space in CI. See if there is a way to diff --git a/Cargo.toml b/Cargo.toml index 57c5733..1ebcf7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ members = [ [workspace.package] edition = "2021" -rust-version = "1.73" +rust-version = "1.81" version = "2.1.0" authors = [ "Deirdre Connolly ", @@ -27,7 +27,7 @@ repository = "https://github.com/ZcashFoundation/frost" categories = ["cryptography"] [workspace.dependencies] -criterion = "0.5" +criterion = "0.6" document-features = "0.2.7" hex = { version = "0.4.3", default-features = false, features = ["alloc"] } insta = { version = "1.31.0", features = ["yaml"] } diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index a0a4465..d6530d1 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -4,7 +4,8 @@ Entries are listed in reverse chronological order. ## Unreleased -* MSRV has been bumped to Rust 1.73 +* MSRV has been bumped to Rust 1.81, making all crates no-std (except + `frost-ed448`). The `std` and `nightly` features were removed from all crates ## 2.1.0 diff --git a/frost-core/Cargo.toml b/frost-core/Cargo.toml index ecd3266..b3eab93 100644 --- a/frost-core/Cargo.toml +++ b/frost-core/Cargo.toml @@ -26,8 +26,7 @@ postcard = { version = "1.0.0", features = ["alloc"], optional = true } rand_core = { version = "0.6", default-features = false } serde = { version = "1.0.160", default-features = false, features = ["derive"], optional = true } serdect = { version = "0.2.0", optional = true } -thiserror-nostd-notrait = { version = "1.0.29", default-features = false } -thiserror = { version = "2.0.3", default-features = false, optional = true } +thiserror = { version = "2.0.3", default-features = false } visibility = "0.1.0" zeroize = { version = "1.5.4", default-features = false, features = ["derive"] } itertools = { version = "0.14.0", default-features = false } @@ -46,10 +45,8 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "cheater-detection"] #! ## Features -## Enable standard library support. -std = ["dep:thiserror"] ## Expose internal types, which do not have SemVer guarantees. This is an advanced ## feature which can be useful if you need to build a modified version of FROST. ## The docs won't list them, you will need to check the source code. diff --git a/frost-core/src/error.rs b/frost-core/src/error.rs index 78662df..c32fb0c 100644 --- a/frost-core/src/error.rs +++ b/frost-core/src/error.rs @@ -1,12 +1,7 @@ //! FROST Error types -#[cfg(feature = "std")] -use thiserror::Error; - -#[cfg(not(feature = "std"))] -use thiserror_nostd_notrait::Error; - use crate::{Ciphersuite, Identifier}; +use thiserror::Error; #[derive(Error, Debug, Clone, Copy, Eq, PartialEq)] pub struct ParticipantError(Identifier); diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index a290984..a0e232b 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(feature = "std"), no_std)] +#![no_std] #![allow(non_snake_case)] // It's emitting false positives; see https://github.com/rust-lang/rust-clippy/issues/9413 #![allow(clippy::derive_partial_eq_without_eq)] diff --git a/frost-ed25519/Cargo.toml b/frost-ed25519/Cargo.toml index b0e1a41..89adf48 100644 --- a/frost-ed25519/Cargo.toml +++ b/frost-ed25519/Cargo.toml @@ -25,8 +25,8 @@ sha2 = { version = "0.10.2", default-features = false } [dev-dependencies] criterion.workspace = true -frost-core = { workspace = true, features = ["std", "test-impl"] } -frost-rerandomized = { workspace = true, features = ["std", "test-impl"] } +frost-core = { workspace = true, features = ["test-impl"] } +frost-rerandomized = { workspace = true, features = ["test-impl"] } ed25519-dalek = "2.1.0" insta.workspace = true hex.workspace = true @@ -37,11 +37,8 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -nightly = [] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "cheater-detection"] #! ## Features -## Enable standard library support. -std = ["frost-core/std"] ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports ## `serde` (e.g. JSON with `serde_json`). diff --git a/frost-ed25519/src/lib.rs b/frost-ed25519/src/lib.rs index 9c4bce8..334922d 100644 --- a/frost-ed25519/src/lib.rs +++ b/frost-ed25519/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(feature = "std"), no_std)] +#![no_std] #![allow(non_snake_case)] #![deny(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] diff --git a/frost-ed448/Cargo.toml b/frost-ed448/Cargo.toml index 81f7be3..1d47056 100644 --- a/frost-ed448/Cargo.toml +++ b/frost-ed448/Cargo.toml @@ -25,8 +25,8 @@ sha3 = { version = "0.10.6", default-features = false } [dev-dependencies] criterion.workspace = true -frost-core = { workspace = true, features = ["std", "test-impl"] } -frost-rerandomized = { workspace = true, features = ["std", "test-impl"] } +frost-core = { workspace = true, features = ["test-impl"] } +frost-rerandomized = { workspace = true, features = ["test-impl"] } lazy_static.workspace = true insta.workspace = true hex.workspace = true @@ -36,11 +36,8 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -nightly = [] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "cheater-detection"] #! ## Features -## Enable standard library support. -std = ["frost-core/std"] ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports ## `serde` (e.g. JSON with `serde_json`). diff --git a/frost-ed448/src/lib.rs b/frost-ed448/src/lib.rs index f8d7570..2acc590 100644 --- a/frost-ed448/src/lib.rs +++ b/frost-ed448/src/lib.rs @@ -230,7 +230,6 @@ pub type Identifier = frost::Identifier; /// FROST(Ed448, SHAKE256) keys, key generation, key shares. pub mod keys { use super::*; - use std::collections::BTreeMap; /// The identifier list to use when generating key shares. pub type IdentifierList<'a> = frost::keys::IdentifierList<'a, E>; diff --git a/frost-p256/Cargo.toml b/frost-p256/Cargo.toml index d8ca78c..e69ca45 100644 --- a/frost-p256/Cargo.toml +++ b/frost-p256/Cargo.toml @@ -25,8 +25,8 @@ sha2 = { version = "0.10.2", default-features = false } [dev-dependencies] criterion.workspace = true -frost-core = { workspace = true, features = ["std", "test-impl"] } -frost-rerandomized = { workspace = true, features = ["std", "test-impl"] } +frost-core = { workspace = true, features = ["test-impl"] } +frost-rerandomized = { workspace = true, features = ["test-impl"] } insta.workspace = true hex.workspace = true lazy_static.workspace = true @@ -36,11 +36,8 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -nightly = [] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "cheater-detection"] #! ## Features -## Enable standard library support. -std = ["frost-core/std"] ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports ## `serde` (e.g. JSON with `serde_json`). diff --git a/frost-p256/src/lib.rs b/frost-p256/src/lib.rs index 35c2620..b21cb8d 100644 --- a/frost-p256/src/lib.rs +++ b/frost-p256/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(feature = "std"), no_std)] +#![no_std] #![allow(non_snake_case)] #![deny(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] diff --git a/frost-rerandomized/Cargo.toml b/frost-rerandomized/Cargo.toml index 0045aab..a1b0548 100644 --- a/frost-rerandomized/Cargo.toml +++ b/frost-rerandomized/Cargo.toml @@ -25,11 +25,8 @@ rand_core.workspace = true [dev-dependencies] [features] -nightly = [] default = ["serialization", "cheater-detection"] #! ## Features -## Enable standard library support. -std = ["frost-core/std"] ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports ## `serde` (e.g. JSON with `serde_json`). diff --git a/frost-rerandomized/src/lib.rs b/frost-rerandomized/src/lib.rs index a309d6f..78ee354 100644 --- a/frost-rerandomized/src/lib.rs +++ b/frost-rerandomized/src/lib.rs @@ -9,7 +9,7 @@ //! - Each participant should call [`sign`] and send the resulting //! [`frost::round2::SignatureShare`] back to the Coordinator; //! - The Coordinator should then call [`aggregate`]. -#![cfg_attr(not(feature = "std"), no_std)] +#![no_std] #![allow(non_snake_case)] extern crate alloc; diff --git a/frost-ristretto255/Cargo.toml b/frost-ristretto255/Cargo.toml index 53f9f0b..355d5b4 100644 --- a/frost-ristretto255/Cargo.toml +++ b/frost-ristretto255/Cargo.toml @@ -25,8 +25,8 @@ sha2 = { version = "0.10.2", default-features = false } [dev-dependencies] criterion = { workspace = true, features = ["html_reports"] } -frost-core = { workspace = true, features = ["std", "test-impl"] } -frost-rerandomized = { workspace = true, features = ["std", "test-impl"] } +frost-core = { workspace = true, features = ["test-impl"] } +frost-rerandomized = { workspace = true, features = ["test-impl"] } insta.workspace = true hex.workspace = true lazy_static.workspace = true @@ -37,11 +37,8 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -nightly = [] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "cheater-detection"] #! ## Features -## Enable standard library support. -std = ["frost-core/std"] ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports ## `serde` (e.g. JSON with `serde_json`). diff --git a/frost-ristretto255/src/lib.rs b/frost-ristretto255/src/lib.rs index 0929c22..cd376b6 100644 --- a/frost-ristretto255/src/lib.rs +++ b/frost-ristretto255/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(feature = "std"), no_std)] +#![no_std] #![allow(non_snake_case)] #![deny(missing_docs)] #![doc = include_str!("../README.md")] diff --git a/frost-secp256k1-tr/Cargo.toml b/frost-secp256k1-tr/Cargo.toml index 17ba380..8171d07 100644 --- a/frost-secp256k1-tr/Cargo.toml +++ b/frost-secp256k1-tr/Cargo.toml @@ -25,23 +25,20 @@ sha2 = { version = "0.10.2", default-features = false } [dev-dependencies] criterion.workspace = true -frost-core = { workspace = true, features = ["std", "test-impl"] } -frost-rerandomized = { workspace = true, features = ["std", "test-impl"] } +frost-core = { workspace = true, features = ["test-impl"] } +frost-rerandomized = { workspace = true, features = ["test-impl"] } insta.workspace = true hex.workspace = true lazy_static.workspace = true proptest.workspace = true rand.workspace = true rand_chacha.workspace = true -secp256k1 = "0.30.0" +secp256k1 = "0.31.0" serde_json.workspace = true [features] -nightly = [] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "cheater-detection"] #! ## Features -## Enable standard library support. -std = ["frost-core/std"] ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports ## `serde` (e.g. JSON with `serde_json`). diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs index eee93fd..cc012d0 100644 --- a/frost-secp256k1-tr/src/lib.rs +++ b/frost-secp256k1-tr/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(feature = "std"), no_std)] +#![no_std] #![allow(non_snake_case)] #![deny(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] diff --git a/frost-secp256k1-tr/tests/helpers/mod.rs b/frost-secp256k1-tr/tests/helpers/mod.rs index 0de6147..299e33f 100644 --- a/frost-secp256k1-tr/tests/helpers/mod.rs +++ b/frost-secp256k1-tr/tests/helpers/mod.rs @@ -17,7 +17,7 @@ pub fn verify_signature( group_signature.serialize().unwrap().try_into().unwrap(), ); let pubkey = secp256k1::XOnlyPublicKey::from_byte_array( - &group_pubkey.serialize().unwrap()[1..33].try_into().unwrap(), + group_pubkey.serialize().unwrap()[1..33].try_into().unwrap(), ) .unwrap(); secp.verify_schnorr(&sig, msg, &pubkey).unwrap(); diff --git a/frost-secp256k1/Cargo.toml b/frost-secp256k1/Cargo.toml index ce2a63e..a027958 100644 --- a/frost-secp256k1/Cargo.toml +++ b/frost-secp256k1/Cargo.toml @@ -25,8 +25,8 @@ sha2 = { version = "0.10.2", default-features = false } [dev-dependencies] criterion.workspace = true -frost-core = { workspace = true, features = ["std", "test-impl"] } -frost-rerandomized = { workspace = true, features = ["std", "test-impl"] } +frost-core = { workspace = true, features = ["test-impl"] } +frost-rerandomized = { workspace = true, features = ["test-impl"] } insta.workspace = true hex.workspace = true lazy_static.workspace = true @@ -36,11 +36,8 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -nightly = [] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "cheater-detection"] #! ## Features -## Enable standard library support. -std = ["frost-core/std"] ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports ## `serde` (e.g. JSON with `serde_json`). diff --git a/frost-secp256k1/src/lib.rs b/frost-secp256k1/src/lib.rs index eec2c8e..9485a23 100644 --- a/frost-secp256k1/src/lib.rs +++ b/frost-secp256k1/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(feature = "std"), no_std)] +#![no_std] #![allow(non_snake_case)] #![deny(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] From 81a091d93a17b358161fc0028d15cfc5e06944af Mon Sep 17 00:00:00 2001 From: VolodymyrBg Date: Fri, 6 Jun 2025 22:08:01 +0300 Subject: [PATCH 22/86] Add post_generate to Ciphersuite trait (#884) * Add post_generate to Ciphersuite trait * Add post_generate to Ciphersuite trait * Update keys.rs * Update keys.rs * Update keys.rs * make post_generate take a map of SecretShare * cargo fmt --------- Co-authored-by: Conrado Gouvea --- frost-core/src/keys.rs | 20 +++++++++++--------- frost-core/src/traits.rs | 17 ++++++++++++++++- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/frost-core/src/keys.rs b/frost-core/src/keys.rs index e9c8f4b..c9f02f2 100644 --- a/frost-core/src/keys.rs +++ b/frost-core/src/keys.rs @@ -548,7 +548,6 @@ pub fn split( } }; let mut verifying_shares: BTreeMap, VerifyingShare> = BTreeMap::new(); - let mut secret_shares_by_id: BTreeMap, SecretShare> = BTreeMap::new(); for secret_share in secret_shares { @@ -558,14 +557,17 @@ pub fn split( secret_shares_by_id.insert(secret_share.identifier, secret_share); } - Ok(( - secret_shares_by_id, - PublicKeyPackage { - header: Header::default(), - verifying_shares, - verifying_key, - }, - )) + let public_key_package = PublicKeyPackage { + header: Header::default(), + verifying_shares, + verifying_key, + }; + + // Apply post-processing + let (processed_secret_shares, processed_public_key_package) = + C::post_generate(secret_shares_by_id, public_key_package)?; + + Ok((processed_secret_shares, processed_public_key_package)) } /// Evaluate the polynomial with the given coefficients (constant term first) diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs index 91a5056..9714015 100644 --- a/frost-core/src/traits.rs +++ b/frost-core/src/traits.rs @@ -10,7 +10,7 @@ use rand_core::{CryptoRng, RngCore}; use crate::{ challenge, - keys::{KeyPackage, PublicKeyPackage, VerifyingShare}, + keys::{KeyPackage, PublicKeyPackage, SecretShare, VerifyingShare}, random_nonzero, round1::{self}, round2::{self, SignatureShare}, @@ -422,4 +422,19 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static { ) -> Result<(KeyPackage, PublicKeyPackage), Error> { Ok((key_package, public_key_package)) } + + /// Post-process the output of the key generation for a participant. + #[allow(clippy::type_complexity)] + fn post_generate( + secret_shares: BTreeMap, SecretShare>, + public_key_package: PublicKeyPackage, + ) -> Result< + ( + BTreeMap, SecretShare>, + PublicKeyPackage, + ), + Error, + > { + Ok((secret_shares, public_key_package)) + } } From 6ff59d1d7dcdece583b60df577934dab5f762831 Mon Sep 17 00:00:00 2001 From: Pili Guerra <1311133+mpguerra@users.noreply.github.com> Date: Thu, 12 Jun 2025 15:17:51 +0200 Subject: [PATCH 23/86] Add CONTRIBUTING.md (#902) * Add CONTRIBUTING.md * Update link to point to #frost channel on discord --- CONTRIBUTING.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5ab53de --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# Contributing + +* [Running and Debugging](#running-and-debugging) +* [Bug Reports](#bug-reports) +* [Pull Requests](#pull-requests) + +## Running and Debugging +[running-and-debugging]: #running-and-debugging + +See the [user documentation](https://frost.zfnd.org/user.html) for details on +how to build, run, and use the FROST Rust reference implementation. + +## Bug Reports +[bug-reports]: #bug-reports + +Please [create an issue](https://github.com/ZcashFoundation/frost/issues/new) on the FROST issue tracker. + +## Pull Requests +[pull-requests]: #pull-requests + +PRs are welcome for small and large changes, but please don't make large PRs +without coordinating with us via the [issue tracker](https://github.com/ZcashFoundation/frost/issues) or [Discord](https://discord.gg/muKwd2F83D). This helps +increase development coordination and makes PRs easier to merge. Low-effort PRs, including but not limited to fixing typos and grammatical corrections, will generally be redone by us to dissuade metric farming. + +Check out the [help wanted][hw] label if you're looking for a place to get started! + +FROST follows the [conventional commits][conventional] standard for the commits +merged to main. Since PRs are squashed before merging to main, the PR titles +should follow the conventional commits standard so that the merged commits +are conformant. + +[hw]: https://github.com/ZcashFoundation/frost/labels/E-help-wanted +[conventional]: https://www.conventionalcommits.org/en/v1.0.0/#specification \ No newline at end of file From 99565594a3ebb25b9625db4be1628f4b64731e80 Mon Sep 17 00:00:00 2001 From: Pili Guerra <1311133+mpguerra@users.noreply.github.com> Date: Fri, 20 Jun 2025 16:46:22 +0200 Subject: [PATCH 24/86] Remove reference to private repo (#905) --- book/src/dev/frost-dependencies-for-audit.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/dev/frost-dependencies-for-audit.md b/book/src/dev/frost-dependencies-for-audit.md index cc8c13c..98f8853 100644 --- a/book/src/dev/frost-dependencies-for-audit.md +++ b/book/src/dev/frost-dependencies-for-audit.md @@ -20,8 +20,8 @@ This is a list of production Rust code that is in scope and out of scope for FRO | Name | Version | Notes |------| ------- | ----- -| redjubjub | v0.6.0 | This library is being partially audited as part of the [Zebra audit](https://github.com/ZcashFoundation/zebra-private/blob/d4137908385be7e6df0a935b91bfc83b532261a2/book/src/dev/zebra-dependencies-for-audit.md#zcashzf-dependencies-1). -| reddsa | v0.5.0 | This library is being partially audited as part of the [Zebra audit](https://github.com/ZcashFoundation/zebra-private/blob/d4137908385be7e6df0a935b91bfc83b532261a2/book/src/dev/zebra-dependencies-for-audit.md#zcashzf-dependencies-1). +| redjubjub | v0.6.0 | This library is being partially audited as part of the [Zebra audit](https://github.com/ZcashFoundation/zebra/blob/main/book/src/dev/zebra-dependencies-for-audit.md#zcashzf-dependencies-1). +| reddsa | v0.5.0 | This library is being partially audited as part of the [Zebra audit](https://github.com/ZcashFoundation/zebra/blob/main/book/src/dev/zebra-dependencies-for-audit.md#zcashzf-dependencies-1). --- ## Partial Audit From f62ee736bcc604e6034b56ae3fe962b476525ab5 Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 25 Aug 2025 12:28:12 -0300 Subject: [PATCH 25/86] core: remove unused error (#909) --- frost-core/src/error.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/frost-core/src/error.rs b/frost-core/src/error.rs index c32fb0c..28d74f4 100644 --- a/frost-core/src/error.rs +++ b/frost-core/src/error.rs @@ -3,9 +3,6 @@ use crate::{Ciphersuite, Identifier}; use thiserror::Error; -#[derive(Error, Debug, Clone, Copy, Eq, PartialEq)] -pub struct ParticipantError(Identifier); - /// An error related to FROST. #[non_exhaustive] #[derive(Error, Debug, Copy, Clone, Eq, PartialEq)] From 379ef689c733b3d9c80fd409071d4f3af4dafed2 Mon Sep 17 00:00:00 2001 From: Conrado Date: Wed, 27 Aug 2025 07:11:48 -0300 Subject: [PATCH 26/86] refresh: validate min_signers (#908) * refresh: validate min_signers * add std back so we can make a non-breaking release --- frost-core/CHANGELOG.md | 3 + frost-core/Cargo.toml | 4 +- frost-core/src/keys/dkg.rs | 4 +- frost-core/src/keys/refresh.rs | 115 +++++++-- frost-core/src/tests/refresh.rs | 225 ++++++++++++++++++ frost-ed25519/Cargo.toml | 4 +- frost-ed25519/src/keys/refresh.rs | 51 +++- frost-ed25519/tests/integration_tests.rs | 19 ++ frost-ed448/Cargo.toml | 4 +- frost-ed448/src/keys/refresh.rs | 51 +++- frost-ed448/tests/integration_tests.rs | 19 ++ frost-p256/Cargo.toml | 4 +- frost-p256/src/keys/refresh.rs | 51 +++- frost-p256/tests/integration_tests.rs | 19 ++ frost-rerandomized/Cargo.toml | 4 +- frost-ristretto255/Cargo.toml | 4 +- frost-ristretto255/src/keys/refresh.rs | 51 +++- frost-ristretto255/tests/integration_tests.rs | 20 ++ frost-secp256k1-tr/Cargo.toml | 4 +- frost-secp256k1-tr/src/keys/refresh.rs | 51 +++- frost-secp256k1-tr/tests/integration_tests.rs | 20 ++ frost-secp256k1/Cargo.toml | 4 +- frost-secp256k1/src/keys/refresh.rs | 51 +++- frost-secp256k1/tests/integration_tests.rs | 19 ++ 24 files changed, 734 insertions(+), 67 deletions(-) diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index d6530d1..74bb6ba 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -6,6 +6,9 @@ Entries are listed in reverse chronological order. * MSRV has been bumped to Rust 1.81, making all crates no-std (except `frost-ed448`). The `std` and `nightly` features were removed from all crates +* Added validation for the `min_signers` parameter in the + `frost_core::keys::refresh` functions. +* Added DKG refresh functions to the crate-specific `refresh` modules. ## 2.1.0 diff --git a/frost-core/Cargo.toml b/frost-core/Cargo.toml index b3eab93..64d6088 100644 --- a/frost-core/Cargo.toml +++ b/frost-core/Cargo.toml @@ -45,7 +45,9 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection"] +default = ["serialization", "cheater-detection", "std"] +# No longer needed. Kept for retrocompatibility until 3.0.0 +std = [] #! ## Features ## Expose internal types, which do not have SemVer guarantees. This is an advanced ## feature which can be useful if you need to build a modified version of FROST. diff --git a/frost-core/src/keys/dkg.rs b/frost-core/src/keys/dkg.rs index 541c053..0eac153 100644 --- a/frost-core/src/keys/dkg.rs +++ b/frost-core/src/keys/dkg.rs @@ -484,7 +484,7 @@ pub(crate) fn verify_proof_of_knowledge( /// `round1_packages` maps the identifier of each other participant to the /// [`round1::Package`] they sent to the current participant (the owner of /// `secret_package`). These identifiers must come from whatever mapping the -/// coordinator has between communication channels and participants, i.e. they +/// participant has between communication channels and participants, i.e. they /// must have assurance that the [`round1::Package`] came from the participant /// with that identifier. /// @@ -561,7 +561,7 @@ pub fn part2( /// `round2_packages` maps the identifier of each other participant to the /// [`round2::Package`] they sent to the current participant (the owner of /// `secret_package`). These identifiers must come from whatever mapping the -/// coordinator has between communication channels and participants, i.e. they +/// participant has between communication channels and participants, i.e. they /// must have assurance that the [`round2::Package`] came from the participant /// with that identifier. /// diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index b12c01e..3815aec 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -1,8 +1,27 @@ //! Refresh Shares //! -//! Implements the functionality to refresh a share. This requires the -//! participation of all the remaining signers. This can be done using a Trusted -//! Dealer or DKG. +//! Refreshing shares has two purposes: +//! +//! - Mitigate against share compromise. +//! - Remove participants from a group. +//! +//! Refer to the [FROST +//! book](https://frost.zfnd.org/frost.html#refreshing-shares) for important +//! details. +//! +//! This modules supports refreshing shares using a Trusted Dealer or DKG. You +//! probably want to use the same approach as the original share generation. +//! +//! For the Trusted Dealer approach, the trusted dealer should call +//! [`compute_refreshing_shares()`] and send the returned refreshing shares to +//! the participants. Each participant should then call [`refresh_share()`]. +//! +//! For the DKG approach, the flow is very similar to [DKG +//! itself](`https://frost.zfnd.org/tutorial/dkg.html`). Each participant calls +//! [`refresh_dkg_part_1()`], keeps the returned secret package and sends the +//! returned package to other participants. Then each participants calls +//! [`refresh_dkg_part2()`] and sends the returned packages to the other +//! participants. Finally each participant calls [`refresh_dkg_shares()`]. use alloc::collections::BTreeMap; use alloc::vec::Vec; @@ -21,10 +40,20 @@ use core::iter; use super::{dkg::round1::Package, KeyPackage, SecretShare, VerifiableSecretSharingCommitment}; -/// Generates new zero key shares and a public key package using a trusted -/// dealer Building a new public key package is done by taking the verifying -/// shares from the new public key package and adding them to the original -/// verifying shares +/// Compute refreshing shares for the Trusted Dealer refresh procedure. +/// +/// - `pub_key_package`: the current public key package. +/// - `max_signers`: the number of participants that are refreshing their +/// shares. It can be smaller than the original value, but still equal to or +/// greater than `min_signers`. +/// - `min_signers`: the threshold needed to sign. It must be equal to the +/// original value for the group (i.e. the refresh process can't reduce +/// the threshold). +/// - `identifiers`: The identifiers of all participants that want to refresh +/// their shares. Must be the same length as `max_signers`. +/// +/// It returns a vectors of [`SecretShare`] that must be sent to the participants +/// in the same order as `identifiers`, and the refreshed [`PublicKeyPackage`]. pub fn compute_refreshing_shares( pub_key_package: PublicKeyPackage, max_signers: u16, @@ -86,9 +115,11 @@ pub fn compute_refreshing_shares( Ok((refreshing_shares_minus_identity, refreshed_pub_key_package)) } -/// Each participant refreshes their shares This is done by taking the -/// `refreshing_share` received from the trusted dealer and adding it to the -/// original share +/// Refresh a share in the Trusted Dealer refresh procedure. +/// +/// Must be called by each participant refreshing the shares, with the +/// `refreshing_share` received from the trusted dealer and the +/// `current_key_package` of the participant. pub fn refresh_share( mut refreshing_share: SecretShare, current_key_package: &KeyPackage, @@ -108,6 +139,10 @@ pub fn refresh_share( // Verify refreshing_share secret share let refreshed_share_package = KeyPackage::::try_from(refreshing_share)?; + if refreshed_share_package.min_signers() != current_key_package.min_signers() { + return Err(Error::InvalidMinSigners); + } + let signing_share: SigningShare = SigningShare::new( refreshed_share_package.signing_share.to_scalar() + current_key_package.signing_share.to_scalar(), @@ -119,8 +154,20 @@ pub fn refresh_share( Ok(new_key_package) } -/// Part 1 of refresh share with DKG. A refreshing_key is generated and a new package and secret_package are generated. -/// The identity commitment is removed from the packages. +/// Part 1 of refresh share with DKG. +/// +/// - `identifier`: The identifier of the participant that wants to refresh +/// their share. +/// - `max_signers`: the number of participants that are refreshing their +/// shares. It can be smaller than the original value, but still equal to or +/// greater than `min_signers`. +/// - `min_signers`: the threshold needed to sign. It must be equal to the +/// original value for the group (i.e. the refresh process can't reduce +/// the threshold). +/// +/// It returns the [`round1::SecretPackage`] that must be kept in memory +/// by the participant for the other steps, and the [`round1::Package`] that +/// must be sent to each other participant in the refresh run. pub fn refresh_dkg_part_1( identifier: Identifier, max_signers: u16, @@ -164,7 +211,21 @@ pub fn refresh_dkg_part_1( Ok((secret_package, package)) } -/// Part 2 of refresh share with DKG. The identity commitment needs to be added back into the secret package. +/// Performs the second part of the refresh procedure for the +/// participant holding the given [`round1::SecretPackage`], given the received +/// [`round1::Package`]s received from the other participants. +/// +/// `round1_packages` maps the identifier of each other participant to the +/// [`round1::Package`] they sent to the current participant (the owner of +/// `secret_package`). These identifiers must come from whatever mapping the +/// participant has between communication channels and participants, i.e. they +/// must have assurance that the [`round1::Package`] came from the participant +/// with that identifier. +/// +/// It returns the [`round2::SecretPackage`] that must be kept in memory by the +/// participant for the final step, and the map of [`round2::Package`]s that +/// must be sent to each other participant who has the given identifier in the +/// map key. pub fn refresh_dkg_part2( mut secret_package: round1::SecretPackage, round1_packages: &BTreeMap, round1::Package>, @@ -241,8 +302,28 @@ pub fn refresh_dkg_part2( )) } -/// This is the step that actually refreshes the shares. New public key packages -/// and key packages are created. +/// Performs the third and final part of the refresh procedure for the +/// participant holding the given [`round2::SecretPackage`], given the received +/// [`round1::Package`]s and [`round2::Package`]s received from the other +/// participants. +/// +/// `round1_packages` must be the same used in [`refresh_dkg_part2()`]. +/// +/// `round2_packages` maps the identifier of each other participant to the +/// [`round2::Package`] they sent to the current participant (the owner of +/// `secret_package`). These identifiers must come from whatever mapping the +/// participant has between communication channels and participants, i.e. they +/// must have assurance that the [`round2::Package`] came from the participant +/// with that identifier. +/// +/// `old_pub_key_package` and `old_key_package` are the old values from the +/// participant, which are being refreshed. +/// +/// It returns the refreshed [`KeyPackage`] that has the long-lived key share +/// for the participant, and the refreshed [`PublicKeyPackage`]s that has public +/// information about all participants; both of which are required to compute +/// FROST signatures. Note that while the verifying (group) key of the +/// [`PublicKeyPackage`] will stay the same, the verifying shares will change. pub fn refresh_dkg_shares( round2_secret_package: &round2::SecretPackage, round1_packages: &BTreeMap, round1::Package>, @@ -250,6 +331,10 @@ pub fn refresh_dkg_shares( old_pub_key_package: PublicKeyPackage, old_key_package: KeyPackage, ) -> Result<(KeyPackage, PublicKeyPackage), Error> { + if round2_secret_package.min_signers() != old_key_package.min_signers() { + return Err(Error::InvalidMinSigners); + } + // Add identity commitment back into round1_packages let mut new_round_1_packages = BTreeMap::new(); for (sender_identifier, round1_package) in round1_packages { diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index 0d7d617..cd20ec0 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -245,6 +245,83 @@ pub fn check_refresh_shares_with_dealer_serialisation( + mut rng: R, +) { + // Compute shares + + //////////////////////////////////////////////////////////////////////////// + // Old Key generation + //////////////////////////////////////////////////////////////////////////// + + const MAX_SIGNERS: u16 = 5; + const MIN_SIGNERS: u16 = 3; + let (old_shares, pub_key_package) = generate_with_dealer( + MAX_SIGNERS, + MIN_SIGNERS, + frost::keys::IdentifierList::Default, + &mut rng, + ) + .unwrap(); + + let mut old_key_packages: BTreeMap, KeyPackage> = BTreeMap::new(); + + for (k, v) in old_shares { + let key_package = KeyPackage::try_from(v).unwrap(); + old_key_packages.insert(k, key_package); + } + + //////////////////////////////////////////////////////////////////////////// + // New Key generation + //////////////////////////////////////////////////////////////////////////// + + // Signer 2 will be removed and Signers 1, 3, 4 & 5 will remain + + let remaining_ids = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + + const NEW_MAX_SIGNERS: u16 = 4; + const NEW_MIN_SIGNERS: u16 = 2; + + // Trusted Dealer generates zero keys and new public key package + + let (zero_shares, _new_pub_key_package) = compute_refreshing_shares( + pub_key_package, + NEW_MAX_SIGNERS, + NEW_MIN_SIGNERS, + &remaining_ids, + &mut rng, + ) + .unwrap(); + + // Each participant refreshes their share + + let mut new_shares = BTreeMap::new(); + + for i in 0..remaining_ids.len() { + let identifier = remaining_ids[i]; + let current_share = &old_key_packages[&identifier]; + let new_share = refresh_share(zero_shares[i].clone(), current_share); + new_shares.insert(identifier, new_share); + } + + assert!( + new_shares + .iter() + .all(|(_, v)| v.is_err() && matches!(v, Err(Error::InvalidMinSigners))), + "{:?}", + new_shares + ); +} + /// Test FROST signing with DKG with a Ciphersuite. pub fn check_refresh_shares_with_dkg( mut rng: R, @@ -429,3 +506,151 @@ where // Proceed with the signing test. check_sign(min_signers, key_packages, rng, pubkeys).unwrap() } + +/// Test FROST signing with DKG with a Ciphersuite, using a smaller +/// threshold than the original one. +pub fn check_refresh_shares_with_dkg_smaller_threshold< + C: Ciphersuite + PartialEq, + R: RngCore + CryptoRng, +>( + mut rng: R, +) where + C::Group: core::cmp::PartialEq, +{ + //////////////////////////////////////////////////////////////////////////// + // Old Key generation + //////////////////////////////////////////////////////////////////////////// + + let old_max_signers = 5; + let min_signers = 3; + let (old_shares, pub_key_package) = generate_with_dealer( + old_max_signers, + min_signers, + frost::keys::IdentifierList::Default, + &mut rng, + ) + .unwrap(); + + let mut old_key_packages: BTreeMap, KeyPackage> = BTreeMap::new(); + + for (k, v) in old_shares { + let key_package = KeyPackage::try_from(v).unwrap(); + old_key_packages.insert(k, key_package); + } + + //////////////////////////////////////////////////////////////////////////// + // Key generation, Round 1 + //////////////////////////////////////////////////////////////////////////// + + let max_signers = 4; + // Use a smaller threshold than the original + let min_signers = 2; + + let remaining_ids = vec![ + Identifier::try_from(4).unwrap(), + Identifier::try_from(2).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(1).unwrap(), + ]; + + // Keep track of each participant's round 1 secret package. + // In practice each participant will keep its copy; no one + // will have all the participant's packages. + let mut round1_secret_packages: BTreeMap< + frost::Identifier, + frost::keys::dkg::round1::SecretPackage, + > = BTreeMap::new(); + + // Keep track of all round 1 packages sent to the given participant. + // This is used to simulate the broadcast; in practice the packages + // will be sent through some communication channel. + let mut received_round1_packages: BTreeMap< + frost::Identifier, + BTreeMap, frost::keys::dkg::round1::Package>, + > = BTreeMap::new(); + + // For each participant, perform the first part of the DKG protocol. + // In practice, each participant will perform this on their own environments. + for participant_identifier in remaining_ids.clone() { + let (round1_secret_package, round1_package) = + refresh_dkg_part_1(participant_identifier, max_signers, min_signers, &mut rng).unwrap(); + + // Store the participant's secret package for later use. + // In practice each participant will store it in their own environment. + round1_secret_packages.insert(participant_identifier, round1_secret_package); + + // "Send" the round 1 package to all other participants. In this + // test this is simulated using a BTreeMap; in practice this will be + // sent through some communication channel. + for receiver_participant_identifier in remaining_ids.clone() { + if receiver_participant_identifier == participant_identifier { + continue; + } + received_round1_packages + .entry(receiver_participant_identifier) + .or_default() + .insert(participant_identifier, round1_package.clone()); + } + } + + //////////////////////////////////////////////////////////////////////////// + // Key generation, Round 2 + //////////////////////////////////////////////////////////////////////////// + // Keep track of each participant's round 2 secret package. + // In practice each participant will keep its copy; no one + // will have all the participant's packages. + let mut round2_secret_packages = BTreeMap::new(); + + // Keep track of all round 2 packages sent to the given participant. + // This is used to simulate the broadcast; in practice the packages + // will be sent through some communication channel. + let mut received_round2_packages = BTreeMap::new(); + + // For each participant, perform the second part of the DKG protocol. + // In practice, each participant will perform this on their own environments. + for participant_identifier in remaining_ids.clone() { + let round1_secret_package = round1_secret_packages + .remove(&participant_identifier) + .unwrap(); + let round1_packages = &received_round1_packages[&participant_identifier]; + let (round2_secret_package, round2_packages) = + refresh_dkg_part2(round1_secret_package, round1_packages).expect("should work"); + + // Store the participant's secret package for later use. + // In practice each participant will store it in their own environment. + round2_secret_packages.insert(participant_identifier, round2_secret_package); + + // "Send" the round 2 package to all other participants. In this + // test this is simulated using a BTreeMap; in practice this will be + // sent through some communication channel. + // Note that, in contrast to the previous part, here each other participant + // gets its own specific package. + for (receiver_identifier, round2_package) in round2_packages { + received_round2_packages + .entry(receiver_identifier) + .or_insert_with(BTreeMap::new) + .insert(participant_identifier, round2_package); + } + } + + //////////////////////////////////////////////////////////////////////////// + // Key generation, final computation + //////////////////////////////////////////////////////////////////////////// + + // For each participant, this is where they refresh their shares + // In practice, each participant will perform this on their own environments. + let mut results = Vec::new(); + for participant_identifier in remaining_ids.clone() { + results.push(frost::keys::refresh::refresh_dkg_shares( + &round2_secret_packages[&participant_identifier], + &received_round1_packages[&participant_identifier], + &received_round2_packages[&participant_identifier], + pub_key_package.clone(), + old_key_packages[&participant_identifier].clone(), + )); + } + + assert!(results + .iter() + .all(|r| matches!(r, Err(Error::InvalidMinSigners)))); +} diff --git a/frost-ed25519/Cargo.toml b/frost-ed25519/Cargo.toml index 89adf48..0f86b61 100644 --- a/frost-ed25519/Cargo.toml +++ b/frost-ed25519/Cargo.toml @@ -37,7 +37,9 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection"] +default = ["serialization", "cheater-detection", "std"] +# No longer needed. Kept for retrocompatibility until 3.0.0 +std = [] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-ed25519/src/keys/refresh.rs b/frost-ed25519/src/keys/refresh.rs index c270fc2..3e26bcb 100644 --- a/frost-ed25519/src/keys/refresh.rs +++ b/frost-ed25519/src/keys/refresh.rs @@ -1,15 +1,17 @@ //! Refresh Shares //! -//! Implements the functionality to refresh a share. This requires the participation -//! of all the remaining signers. This can be done using a Trusted Dealer or -//! DKG (not yet implemented) +//! Refer to [`frost_core::keys::refresh`] for more details. -use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; -use alloc::vec::Vec; +use crate::{ + frost, + keys::dkg::{round1, round2}, + Ciphersuite, CryptoRng, Error, Identifier, RngCore, +}; +use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; -/// Refreshes shares using a trusted dealer +/// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, @@ -26,10 +28,45 @@ pub fn compute_refreshing_shares( ) } -/// Each participant refreshed their shares +/// Refer to [`frost_core::keys::refresh::refresh_share`]. pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +pub fn refresh_dkg_part1( + identifier: Identifier, + max_signers: u16, + min_signers: u16, + mut rng: R, +) -> Result<(round1::SecretPackage, round1::Package), Error> { + frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) +} + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. +pub fn refresh_dkg_part2( + secret_package: round1::SecretPackage, + round1_packages: &BTreeMap, +) -> Result<(round2::SecretPackage, BTreeMap), Error> { + frost::keys::refresh::refresh_dkg_part2(secret_package, round1_packages) +} + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_shares`]. +pub fn refresh_dkg_shares( + round2_secret_package: &round2::SecretPackage, + round1_packages: &BTreeMap, + round2_packages: &BTreeMap, + old_pub_key_package: PublicKeyPackage, + old_key_package: KeyPackage, +) -> Result<(KeyPackage, PublicKeyPackage), Error> { + frost::keys::refresh::refresh_dkg_shares( + round2_secret_package, + round1_packages, + round2_packages, + old_pub_key_package, + old_key_package, + ) +} diff --git a/frost-ed25519/tests/integration_tests.rs b/frost-ed25519/tests/integration_tests.rs index d7b5d16..830f645 100644 --- a/frost-ed25519/tests/integration_tests.rs +++ b/frost-ed25519/tests/integration_tests.rs @@ -179,6 +179,16 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { >(max_signers, min_signers, &identifiers, error, rng); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_different_min_signers() { + let rng = rand::rngs::OsRng; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_different_min_signers::< + Ed25519Sha512, + _, + >(rng); +} + #[test] fn check_refresh_shares_with_dkg() { let rng = rand::rngs::OsRng; @@ -186,6 +196,15 @@ fn check_refresh_shares_with_dkg() { frost_core::tests::refresh::check_refresh_shares_with_dkg::(rng); } +#[test] +fn check_refresh_shares_with_dkg_smaller_threshold() { + let rng = rand::rngs::OsRng; + + frost_core::tests::refresh::check_refresh_shares_with_dkg_smaller_threshold::( + rng, + ); +} + #[test] fn check_sign_with_dealer() { let rng = rand::rngs::OsRng; diff --git a/frost-ed448/Cargo.toml b/frost-ed448/Cargo.toml index 1d47056..4d834fe 100644 --- a/frost-ed448/Cargo.toml +++ b/frost-ed448/Cargo.toml @@ -36,7 +36,9 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection"] +default = ["serialization", "cheater-detection", "std"] +# No longer needed. Kept for retrocompatibility until 3.0.0 +std = [] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-ed448/src/keys/refresh.rs b/frost-ed448/src/keys/refresh.rs index c270fc2..3e26bcb 100644 --- a/frost-ed448/src/keys/refresh.rs +++ b/frost-ed448/src/keys/refresh.rs @@ -1,15 +1,17 @@ //! Refresh Shares //! -//! Implements the functionality to refresh a share. This requires the participation -//! of all the remaining signers. This can be done using a Trusted Dealer or -//! DKG (not yet implemented) +//! Refer to [`frost_core::keys::refresh`] for more details. -use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; -use alloc::vec::Vec; +use crate::{ + frost, + keys::dkg::{round1, round2}, + Ciphersuite, CryptoRng, Error, Identifier, RngCore, +}; +use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; -/// Refreshes shares using a trusted dealer +/// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, @@ -26,10 +28,45 @@ pub fn compute_refreshing_shares( ) } -/// Each participant refreshed their shares +/// Refer to [`frost_core::keys::refresh::refresh_share`]. pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +pub fn refresh_dkg_part1( + identifier: Identifier, + max_signers: u16, + min_signers: u16, + mut rng: R, +) -> Result<(round1::SecretPackage, round1::Package), Error> { + frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) +} + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. +pub fn refresh_dkg_part2( + secret_package: round1::SecretPackage, + round1_packages: &BTreeMap, +) -> Result<(round2::SecretPackage, BTreeMap), Error> { + frost::keys::refresh::refresh_dkg_part2(secret_package, round1_packages) +} + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_shares`]. +pub fn refresh_dkg_shares( + round2_secret_package: &round2::SecretPackage, + round1_packages: &BTreeMap, + round2_packages: &BTreeMap, + old_pub_key_package: PublicKeyPackage, + old_key_package: KeyPackage, +) -> Result<(KeyPackage, PublicKeyPackage), Error> { + frost::keys::refresh::refresh_dkg_shares( + round2_secret_package, + round1_packages, + round2_packages, + old_pub_key_package, + old_key_package, + ) +} diff --git a/frost-ed448/tests/integration_tests.rs b/frost-ed448/tests/integration_tests.rs index 872051a..0dfb791 100644 --- a/frost-ed448/tests/integration_tests.rs +++ b/frost-ed448/tests/integration_tests.rs @@ -179,6 +179,16 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { >(max_signers, min_signers, &identifiers, error, rng); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_different_min_signers() { + let rng = rand::rngs::OsRng; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_different_min_signers::< + Ed448Shake256, + _, + >(rng); +} + #[test] fn check_refresh_shares_with_dkg() { let rng = rand::rngs::OsRng; @@ -186,6 +196,15 @@ fn check_refresh_shares_with_dkg() { frost_core::tests::refresh::check_refresh_shares_with_dkg::(rng); } +#[test] +fn check_refresh_shares_with_dkg_smaller_threshold() { + let rng = rand::rngs::OsRng; + + frost_core::tests::refresh::check_refresh_shares_with_dkg_smaller_threshold::( + rng, + ); +} + #[test] fn check_sign_with_dealer() { let rng = rand::rngs::OsRng; diff --git a/frost-p256/Cargo.toml b/frost-p256/Cargo.toml index e69ca45..d047e7b 100644 --- a/frost-p256/Cargo.toml +++ b/frost-p256/Cargo.toml @@ -36,7 +36,9 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection"] +default = ["serialization", "cheater-detection", "std"] +# No longer needed. Kept for retrocompatibility until 3.0.0 +std = [] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-p256/src/keys/refresh.rs b/frost-p256/src/keys/refresh.rs index c270fc2..3e26bcb 100644 --- a/frost-p256/src/keys/refresh.rs +++ b/frost-p256/src/keys/refresh.rs @@ -1,15 +1,17 @@ //! Refresh Shares //! -//! Implements the functionality to refresh a share. This requires the participation -//! of all the remaining signers. This can be done using a Trusted Dealer or -//! DKG (not yet implemented) +//! Refer to [`frost_core::keys::refresh`] for more details. -use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; -use alloc::vec::Vec; +use crate::{ + frost, + keys::dkg::{round1, round2}, + Ciphersuite, CryptoRng, Error, Identifier, RngCore, +}; +use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; -/// Refreshes shares using a trusted dealer +/// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, @@ -26,10 +28,45 @@ pub fn compute_refreshing_shares( ) } -/// Each participant refreshed their shares +/// Refer to [`frost_core::keys::refresh::refresh_share`]. pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +pub fn refresh_dkg_part1( + identifier: Identifier, + max_signers: u16, + min_signers: u16, + mut rng: R, +) -> Result<(round1::SecretPackage, round1::Package), Error> { + frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) +} + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. +pub fn refresh_dkg_part2( + secret_package: round1::SecretPackage, + round1_packages: &BTreeMap, +) -> Result<(round2::SecretPackage, BTreeMap), Error> { + frost::keys::refresh::refresh_dkg_part2(secret_package, round1_packages) +} + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_shares`]. +pub fn refresh_dkg_shares( + round2_secret_package: &round2::SecretPackage, + round1_packages: &BTreeMap, + round2_packages: &BTreeMap, + old_pub_key_package: PublicKeyPackage, + old_key_package: KeyPackage, +) -> Result<(KeyPackage, PublicKeyPackage), Error> { + frost::keys::refresh::refresh_dkg_shares( + round2_secret_package, + round1_packages, + round2_packages, + old_pub_key_package, + old_key_package, + ) +} diff --git a/frost-p256/tests/integration_tests.rs b/frost-p256/tests/integration_tests.rs index 33e4c20..868cc4d 100644 --- a/frost-p256/tests/integration_tests.rs +++ b/frost-p256/tests/integration_tests.rs @@ -179,6 +179,16 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { >(max_signers, min_signers, &identifiers, error, rng); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_different_min_signers() { + let rng = rand::rngs::OsRng; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_different_min_signers::< + P256Sha256, + _, + >(rng); +} + #[test] fn check_refresh_shares_with_dkg() { let rng = rand::rngs::OsRng; @@ -186,6 +196,15 @@ fn check_refresh_shares_with_dkg() { frost_core::tests::refresh::check_refresh_shares_with_dkg::(rng); } +#[test] +fn check_refresh_shares_with_dkg_smaller_threshold() { + let rng = rand::rngs::OsRng; + + frost_core::tests::refresh::check_refresh_shares_with_dkg_smaller_threshold::( + rng, + ); +} + #[test] fn check_sign_with_dealer() { let rng = rand::rngs::OsRng; diff --git a/frost-rerandomized/Cargo.toml b/frost-rerandomized/Cargo.toml index a1b0548..6470cf9 100644 --- a/frost-rerandomized/Cargo.toml +++ b/frost-rerandomized/Cargo.toml @@ -25,7 +25,9 @@ rand_core.workspace = true [dev-dependencies] [features] -default = ["serialization", "cheater-detection"] +default = ["serialization", "cheater-detection", "std"] +# No longer needed. Kept for retrocompatibility until 3.0.0 +std = [] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-ristretto255/Cargo.toml b/frost-ristretto255/Cargo.toml index 355d5b4..c9e6254 100644 --- a/frost-ristretto255/Cargo.toml +++ b/frost-ristretto255/Cargo.toml @@ -37,7 +37,9 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection"] +default = ["serialization", "cheater-detection", "std"] +# No longer needed. Kept for retrocompatibility until 3.0.0 +std = [] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-ristretto255/src/keys/refresh.rs b/frost-ristretto255/src/keys/refresh.rs index c270fc2..3e26bcb 100644 --- a/frost-ristretto255/src/keys/refresh.rs +++ b/frost-ristretto255/src/keys/refresh.rs @@ -1,15 +1,17 @@ //! Refresh Shares //! -//! Implements the functionality to refresh a share. This requires the participation -//! of all the remaining signers. This can be done using a Trusted Dealer or -//! DKG (not yet implemented) +//! Refer to [`frost_core::keys::refresh`] for more details. -use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; -use alloc::vec::Vec; +use crate::{ + frost, + keys::dkg::{round1, round2}, + Ciphersuite, CryptoRng, Error, Identifier, RngCore, +}; +use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; -/// Refreshes shares using a trusted dealer +/// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, @@ -26,10 +28,45 @@ pub fn compute_refreshing_shares( ) } -/// Each participant refreshed their shares +/// Refer to [`frost_core::keys::refresh::refresh_share`]. pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +pub fn refresh_dkg_part1( + identifier: Identifier, + max_signers: u16, + min_signers: u16, + mut rng: R, +) -> Result<(round1::SecretPackage, round1::Package), Error> { + frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) +} + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. +pub fn refresh_dkg_part2( + secret_package: round1::SecretPackage, + round1_packages: &BTreeMap, +) -> Result<(round2::SecretPackage, BTreeMap), Error> { + frost::keys::refresh::refresh_dkg_part2(secret_package, round1_packages) +} + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_shares`]. +pub fn refresh_dkg_shares( + round2_secret_package: &round2::SecretPackage, + round1_packages: &BTreeMap, + round2_packages: &BTreeMap, + old_pub_key_package: PublicKeyPackage, + old_key_package: KeyPackage, +) -> Result<(KeyPackage, PublicKeyPackage), Error> { + frost::keys::refresh::refresh_dkg_shares( + round2_secret_package, + round1_packages, + round2_packages, + old_pub_key_package, + old_key_package, + ) +} diff --git a/frost-ristretto255/tests/integration_tests.rs b/frost-ristretto255/tests/integration_tests.rs index 93a5574..7179fab 100644 --- a/frost-ristretto255/tests/integration_tests.rs +++ b/frost-ristretto255/tests/integration_tests.rs @@ -180,6 +180,16 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { >(max_signers, min_signers, &identifiers, error, rng); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_different_min_signers() { + let rng = rand::rngs::OsRng; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_different_min_signers::< + Ristretto255Sha512, + _, + >(rng); +} + #[test] fn check_refresh_shares_with_dkg() { let rng = rand::rngs::OsRng; @@ -187,6 +197,16 @@ fn check_refresh_shares_with_dkg() { frost_core::tests::refresh::check_refresh_shares_with_dkg::(rng); } +#[test] +fn check_refresh_shares_with_dkg_smaller_threshold() { + let rng = rand::rngs::OsRng; + + frost_core::tests::refresh::check_refresh_shares_with_dkg_smaller_threshold::< + Ristretto255Sha512, + _, + >(rng); +} + #[test] fn check_sign_with_dealer() { let rng = rand::rngs::OsRng; diff --git a/frost-secp256k1-tr/Cargo.toml b/frost-secp256k1-tr/Cargo.toml index 8171d07..c9227fb 100644 --- a/frost-secp256k1-tr/Cargo.toml +++ b/frost-secp256k1-tr/Cargo.toml @@ -37,7 +37,9 @@ secp256k1 = "0.31.0" serde_json.workspace = true [features] -default = ["serialization", "cheater-detection"] +default = ["serialization", "cheater-detection", "std"] +# No longer needed. Kept for retrocompatibility until 3.0.0 +std = [] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-secp256k1-tr/src/keys/refresh.rs b/frost-secp256k1-tr/src/keys/refresh.rs index c270fc2..3e26bcb 100644 --- a/frost-secp256k1-tr/src/keys/refresh.rs +++ b/frost-secp256k1-tr/src/keys/refresh.rs @@ -1,15 +1,17 @@ //! Refresh Shares //! -//! Implements the functionality to refresh a share. This requires the participation -//! of all the remaining signers. This can be done using a Trusted Dealer or -//! DKG (not yet implemented) +//! Refer to [`frost_core::keys::refresh`] for more details. -use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; -use alloc::vec::Vec; +use crate::{ + frost, + keys::dkg::{round1, round2}, + Ciphersuite, CryptoRng, Error, Identifier, RngCore, +}; +use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; -/// Refreshes shares using a trusted dealer +/// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, @@ -26,10 +28,45 @@ pub fn compute_refreshing_shares( ) } -/// Each participant refreshed their shares +/// Refer to [`frost_core::keys::refresh::refresh_share`]. pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +pub fn refresh_dkg_part1( + identifier: Identifier, + max_signers: u16, + min_signers: u16, + mut rng: R, +) -> Result<(round1::SecretPackage, round1::Package), Error> { + frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) +} + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. +pub fn refresh_dkg_part2( + secret_package: round1::SecretPackage, + round1_packages: &BTreeMap, +) -> Result<(round2::SecretPackage, BTreeMap), Error> { + frost::keys::refresh::refresh_dkg_part2(secret_package, round1_packages) +} + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_shares`]. +pub fn refresh_dkg_shares( + round2_secret_package: &round2::SecretPackage, + round1_packages: &BTreeMap, + round2_packages: &BTreeMap, + old_pub_key_package: PublicKeyPackage, + old_key_package: KeyPackage, +) -> Result<(KeyPackage, PublicKeyPackage), Error> { + frost::keys::refresh::refresh_dkg_shares( + round2_secret_package, + round1_packages, + round2_packages, + old_pub_key_package, + old_key_package, + ) +} diff --git a/frost-secp256k1-tr/tests/integration_tests.rs b/frost-secp256k1-tr/tests/integration_tests.rs index 2d5db02..4933040 100644 --- a/frost-secp256k1-tr/tests/integration_tests.rs +++ b/frost-secp256k1-tr/tests/integration_tests.rs @@ -180,6 +180,16 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { >(max_signers, min_signers, &identifiers, error, rng); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_different_min_signers() { + let rng = rand::rngs::OsRng; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_different_min_signers::< + Secp256K1Sha256TR, + _, + >(rng); +} + #[test] fn check_refresh_shares_with_dkg() { let rng = rand::rngs::OsRng; @@ -187,6 +197,16 @@ fn check_refresh_shares_with_dkg() { frost_core::tests::refresh::check_refresh_shares_with_dkg::(rng); } +#[test] +fn check_refresh_shares_with_dkg_smaller_threshold() { + let rng = rand::rngs::OsRng; + + frost_core::tests::refresh::check_refresh_shares_with_dkg_smaller_threshold::< + Secp256K1Sha256TR, + _, + >(rng); +} + #[test] fn check_sign_with_dealer() { let rng = rand::rngs::OsRng; diff --git a/frost-secp256k1/Cargo.toml b/frost-secp256k1/Cargo.toml index a027958..30e20d5 100644 --- a/frost-secp256k1/Cargo.toml +++ b/frost-secp256k1/Cargo.toml @@ -36,7 +36,9 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection"] +default = ["serialization", "cheater-detection", "std"] +# No longer needed. Kept for retrocompatibility until 3.0.0 +std = [] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-secp256k1/src/keys/refresh.rs b/frost-secp256k1/src/keys/refresh.rs index c270fc2..3e26bcb 100644 --- a/frost-secp256k1/src/keys/refresh.rs +++ b/frost-secp256k1/src/keys/refresh.rs @@ -1,15 +1,17 @@ //! Refresh Shares //! -//! Implements the functionality to refresh a share. This requires the participation -//! of all the remaining signers. This can be done using a Trusted Dealer or -//! DKG (not yet implemented) +//! Refer to [`frost_core::keys::refresh`] for more details. -use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; -use alloc::vec::Vec; +use crate::{ + frost, + keys::dkg::{round1, round2}, + Ciphersuite, CryptoRng, Error, Identifier, RngCore, +}; +use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; -/// Refreshes shares using a trusted dealer +/// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, @@ -26,10 +28,45 @@ pub fn compute_refreshing_shares( ) } -/// Each participant refreshed their shares +/// Refer to [`frost_core::keys::refresh::refresh_share`]. pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +pub fn refresh_dkg_part1( + identifier: Identifier, + max_signers: u16, + min_signers: u16, + mut rng: R, +) -> Result<(round1::SecretPackage, round1::Package), Error> { + frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) +} + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. +pub fn refresh_dkg_part2( + secret_package: round1::SecretPackage, + round1_packages: &BTreeMap, +) -> Result<(round2::SecretPackage, BTreeMap), Error> { + frost::keys::refresh::refresh_dkg_part2(secret_package, round1_packages) +} + +/// Refer to [`frost_core::keys::refresh::refresh_dkg_shares`]. +pub fn refresh_dkg_shares( + round2_secret_package: &round2::SecretPackage, + round1_packages: &BTreeMap, + round2_packages: &BTreeMap, + old_pub_key_package: PublicKeyPackage, + old_key_package: KeyPackage, +) -> Result<(KeyPackage, PublicKeyPackage), Error> { + frost::keys::refresh::refresh_dkg_shares( + round2_secret_package, + round1_packages, + round2_packages, + old_pub_key_package, + old_key_package, + ) +} diff --git a/frost-secp256k1/tests/integration_tests.rs b/frost-secp256k1/tests/integration_tests.rs index 0676f61..0356b0a 100644 --- a/frost-secp256k1/tests/integration_tests.rs +++ b/frost-secp256k1/tests/integration_tests.rs @@ -179,6 +179,16 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { >(max_signers, min_signers, &identifiers, error, rng); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_different_min_signers() { + let rng = rand::rngs::OsRng; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_different_min_signers::< + Secp256K1Sha256, + _, + >(rng); +} + #[test] fn check_refresh_shares_with_dkg() { let rng = rand::rngs::OsRng; @@ -186,6 +196,15 @@ fn check_refresh_shares_with_dkg() { frost_core::tests::refresh::check_refresh_shares_with_dkg::(rng); } +#[test] +fn check_refresh_shares_with_dkg_smaller_threshold() { + let rng = rand::rngs::OsRng; + + frost_core::tests::refresh::check_refresh_shares_with_dkg_smaller_threshold::( + rng, + ); +} + #[test] fn check_sign_with_dealer() { let rng = rand::rngs::OsRng; From 3ffc19d8f473d5bc4e07ed41bc884bdb42d6c29f Mon Sep 17 00:00:00 2001 From: Conrado Date: Wed, 27 Aug 2025 11:50:02 -0300 Subject: [PATCH 27/86] bump to 2.2.0 (#914) --- Cargo.toml | 6 +++--- book/src/tutorial/importing.md | 4 ++-- book/src/tutorial/signing.md | 2 +- frost-core/CHANGELOG.md | 27 +++++++++++++++++++++++---- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1ebcf7b..ae89d57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ members = [ [workspace.package] edition = "2021" rust-version = "1.81" -version = "2.1.0" +version = "2.2.0" authors = [ "Deirdre Connolly ", "Chelsea Komlo ", @@ -38,8 +38,8 @@ rand_chacha = "0.3" rand_core = "0.6" serde_json = "1.0" -frost-core = { path = "frost-core", version = "2.1.0", default-features = false } -frost-rerandomized = { path = "frost-rerandomized", version = "2.1.0", default-features = false } +frost-core = { path = "frost-core", version = "2.2.0", default-features = false } +frost-rerandomized = { path = "frost-rerandomized", version = "2.2.0", default-features = false } [profile.test.package."*"] opt-level = 3 diff --git a/book/src/tutorial/importing.md b/book/src/tutorial/importing.md index 2c2dd41..23be045 100644 --- a/book/src/tutorial/importing.md +++ b/book/src/tutorial/importing.md @@ -6,7 +6,7 @@ Add to your `Cargo.toml` file: ``` [dependencies] -frost-ristretto255 = "2.1.0" +frost-ristretto255 = "2.2.0" ``` ## Handling errors @@ -38,7 +38,7 @@ needs to be transmitted. The importing would look like: ``` [dependencies] -frost-ristretto255 = { version = "2.1.0", features = ["serde"] } +frost-ristretto255 = { version = "2.2.0", features = ["serde"] } ``` Note that serde usage is optional. Applications can use different encodings, and diff --git a/book/src/tutorial/signing.md b/book/src/tutorial/signing.md index e978fa4..4a385c2 100644 --- a/book/src/tutorial/signing.md +++ b/book/src/tutorial/signing.md @@ -9,7 +9,7 @@ channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). ## Coordinator, Round 1 To sign, the -[Coordinator](file:///home/conrado/zfnd/frost/book/book/frost.html#signing) must +[Coordinator](../frost.md#signing) must select which participants are going to generate the signature, and must signal to start the process. This needs to be implemented by users of the ZF FROST library and will depend on the communication channel being used. diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index 74bb6ba..a5f2e42 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -2,13 +2,32 @@ Entries are listed in reverse chronological order. -## Unreleased +## 2.2.0 + +### Security Fixes -* MSRV has been bumped to Rust 1.81, making all crates no-std (except - `frost-ed448`). The `std` and `nightly` features were removed from all crates * Added validation for the `min_signers` parameter in the - `frost_core::keys::refresh` functions. + `frost_core::keys::refresh` functions. It was not clear that it is not + possible to change `min_signers` with the refresh procedure. Using a smaller + value would not decrease the threshold, and attempts to sign using a smaller + threshold would fail. Additionally, after refreshing the shares with a smaller + threshold, it would still be possible to sign with the original threshold; + however, this could cause a security loss to the participant's shares. We have + not determined the exact security implications of doing so and judged simpler + to just validate `min_signers`. If for some reason you have done a refresh + share procedure with a smaller `min_signers` we strongly recommend migrating + to a new key. Thank you [BlockSec](https://blocksec.com/) for reporting the + finding. + +### Other Changes + +* MSRV has been bumped to Rust 1.81, making all crates no-std (except + `frost-ed448`). * Added DKG refresh functions to the crate-specific `refresh` modules. +* Added `VerifiableSecretSharingCommitment::{serialize,deserialize}_whole()` + methods. +* Added `Ciphersuite::post_generate()` method to allow more ciphersuite + customization. ## 2.1.0 From 60343168626d2de6f157392457f09643064a9941 Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 29 Sep 2025 13:11:25 -0300 Subject: [PATCH 28/86] core: add aggregate_custom (#911) --- frost-core/src/error.rs | 27 ++--- frost-core/src/lib.rs | 111 ++++++++++++++++---- frost-core/src/round2.rs | 2 +- frost-core/src/tests/ciphersuite_generic.rs | 77 +++++++++++--- frost-ed25519/src/lib.rs | 19 ++++ frost-ed448/src/lib.rs | 19 ++++ frost-p256/src/lib.rs | 19 ++++ frost-ristretto255/src/lib.rs | 19 ++++ frost-secp256k1/src/lib.rs | 19 ++++ 9 files changed, 260 insertions(+), 52 deletions(-) diff --git a/frost-core/src/error.rs b/frost-core/src/error.rs index 28d74f4..e11d9fd 100644 --- a/frost-core/src/error.rs +++ b/frost-core/src/error.rs @@ -1,11 +1,12 @@ //! FROST Error types use crate::{Ciphersuite, Identifier}; +use alloc::vec::Vec; use thiserror::Error; /// An error related to FROST. #[non_exhaustive] -#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Error, Debug, Clone, Eq, PartialEq)] pub enum Error { /// min_signers is invalid #[error("min_signers must be at least 2 and not larger than max_signers")] @@ -62,7 +63,7 @@ pub enum Error { #[error("Invalid signature share.")] InvalidSignatureShare { /// The identifier of the signer whose share validation failed. - culprit: Identifier, + culprits: Vec>, }, /// Secret share verification failed. #[error("Invalid secret share.")] @@ -113,24 +114,18 @@ impl Error where C: Ciphersuite, { - /// Return the identifier of the participant that caused the error. - /// Returns None if not applicable for the error. + /// Return the identifiers of the participants that caused the error. + /// Returns an empty vector if not applicable for the error. /// - /// This can be used to penalize a participant that does not follow the + /// This can be used to penalize participants that do not follow the /// protocol correctly, e.g. removing them from further signings. - pub fn culprit(&self) -> Option> { + pub fn culprits(&self) -> Vec> { // Use an exhaustive match to make sure that if we add new enum items // then we will explicitly check if they should be added here. match self { - Error::InvalidSignatureShare { - culprit: identifier, - } - | Error::InvalidProofOfKnowledge { - culprit: identifier, - } => Some(*identifier), - Error::InvalidSecretShare { - culprit: identifier, - } => *identifier, + Error::InvalidSignatureShare { culprits } => culprits.clone(), + Error::InvalidProofOfKnowledge { culprit } => vec![*culprit], + Error::InvalidSecretShare { culprit } => culprit.map(|i| vec![i]).unwrap_or_default(), Error::InvalidMinSigners | Error::InvalidMaxSigners | Error::InvalidCoefficients @@ -157,7 +152,7 @@ where | Error::IncorrectNumberOfCommitments | Error::SerializationError | Error::DeserializationError - | Error::IdentifierDerivationNotSupported => None, + | Error::IdentifierDerivationNotSupported => vec![], } } } diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index a0e232b..653ed4f 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -566,6 +566,50 @@ pub fn aggregate( signature_shares: &BTreeMap, round2::SignatureShare>, pubkeys: &keys::PublicKeyPackage, ) -> Result, Error> +where + C: Ciphersuite, +{ + #[cfg(feature = "cheater-detection")] + { + aggregate_custom( + signing_package, + signature_shares, + pubkeys, + CheaterDetection::FirstCheater, + ) + } + #[cfg(not(feature = "cheater-detection"))] + { + aggregate_custom( + signing_package, + signature_shares, + pubkeys, + CheaterDetection::Disabled, + ) + } +} + +/// The type of cheater detection to use. +pub enum CheaterDetection { + /// Disable cheater detection. Fast in case there are invalid + /// shares. + Disabled, + /// Detect the first cheater and stop. Performance will depend on where + /// the cheater's share is in the list. + FirstCheater, + /// Detect all cheaters. Slower since all shares must be verified. + /// Performance will be proportional on the size of participants. + AllCheaters, +} + +/// Like [`aggregate()`], but allow specifying a specific cheater detection +/// strategy. +pub fn aggregate_custom( + signing_package: &SigningPackage, + signature_shares: &BTreeMap, round2::SignatureShare>, + pubkeys: &keys::PublicKeyPackage, + cheater_detection: CheaterDetection, +) -> Result, Error> where C: Ciphersuite, { @@ -575,12 +619,16 @@ where return Err(Error::UnknownIdentifier); } - if !signing_package.signing_commitments().keys().all(|id| { - #[cfg(feature = "cheater-detection")] - return signature_shares.contains_key(id) && pubkeys.verifying_shares().contains_key(id); - #[cfg(not(feature = "cheater-detection"))] - return signature_shares.contains_key(id); - }) { + if !signing_package + .signing_commitments() + .keys() + .all(|id| match cheater_detection { + CheaterDetection::Disabled => signature_shares.contains_key(id), + CheaterDetection::FirstCheater | CheaterDetection::AllCheaters => { + signature_shares.contains_key(id) && pubkeys.verifying_shares().contains_key(id) + } + }) + { return Err(Error::UnknownIdentifier); } @@ -619,32 +667,36 @@ where // Only if the verification of the aggregate signature failed; verify each share to find the cheater. // This approach is more efficient since we don't need to verify all shares // if the aggregate signature is valid (which should be the common case). - #[cfg(feature = "cheater-detection")] - if verification_result.is_err() { - detect_cheater( - &group_commitment, - &pubkeys, - &signing_package, - &signature_shares, - &binding_factor_list, - )?; + match cheater_detection { + CheaterDetection::Disabled => { + verification_result?; + } + CheaterDetection::FirstCheater | CheaterDetection::AllCheaters => { + if verification_result.is_err() { + detect_cheater( + &group_commitment, + &pubkeys, + &signing_package, + &signature_shares, + &binding_factor_list, + cheater_detection, + )?; + } + } } - #[cfg(not(feature = "cheater-detection"))] - verification_result?; - Ok(signature) } /// Optional cheater detection feature /// Each share is verified to find the cheater -#[cfg(feature = "cheater-detection")] fn detect_cheater( group_commitment: &GroupCommitment, pubkeys: &keys::PublicKeyPackage, signing_package: &SigningPackage, signature_shares: &BTreeMap, round2::SignatureShare>, binding_factor_list: &BindingFactorList, + cheater_detection: CheaterDetection, ) -> Result<(), Error> { // Compute the per-message challenge. let challenge = ::challenge( @@ -653,6 +705,8 @@ fn detect_cheater( signing_package.message(), )?; + let mut all_culprits = Vec::new(); + // Verify the signature shares. for (identifier, signature_share) in signature_shares { // Look up the public key for this signer, where `signer_pubkey` = _G.ScalarBaseMult(s[i])_, @@ -662,7 +716,7 @@ fn detect_cheater( .get(identifier) .ok_or(Error::UnknownIdentifier)?; - verify_signature_share_precomputed( + let r = verify_signature_share_precomputed( *identifier, signing_package, binding_factor_list, @@ -670,7 +724,22 @@ fn detect_cheater( signature_share, verifying_share, challenge, - )?; + ); + match r { + Ok(_) => {} + Err(Error::InvalidSignatureShare { culprits }) => { + all_culprits.extend(culprits); + if let CheaterDetection::FirstCheater = cheater_detection { + break; + } + } + Err(e) => return Err(e), + } + } + if !all_culprits.is_empty() { + return Err(Error::InvalidSignatureShare { + culprits: all_culprits, + }); } // We should never reach here; but we return an error to be safe. diff --git a/frost-core/src/round2.rs b/frost-core/src/round2.rs index d3147db..39c7cf4 100644 --- a/frost-core/src/round2.rs +++ b/frost-core/src/round2.rs @@ -74,7 +74,7 @@ where + (verifying_share.to_element() * challenge.0 * lambda_i)) { return Err(Error::InvalidSignatureShare { - culprit: identifier, + culprits: vec![identifier], }); } diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs index 220bfb5..195c55f 100644 --- a/frost-core/src/tests/ciphersuite_generic.rs +++ b/frost-core/src/tests/ciphersuite_generic.rs @@ -342,16 +342,65 @@ fn check_aggregate_corrupted_share( mut signature_shares: BTreeMap, frost::round2::SignatureShare>, pubkey_package: frost::keys::PublicKeyPackage, ) { - use crate::round2::SignatureShare; + use crate::{round2::SignatureShare, CheaterDetection}; let one = <::Group as Group>::Field::one(); - // Corrupt a share - let id = *signature_shares.keys().next().unwrap(); - *signature_shares.get_mut(&id).unwrap() = - SignatureShare::new(signature_shares[&id].to_scalar() + one); + // Corrupt two shares + let id1 = *signature_shares.keys().next().unwrap(); + *signature_shares.get_mut(&id1).unwrap() = + SignatureShare::new(signature_shares[&id1].to_scalar() + one); + let id2 = *signature_shares.keys().nth(1).unwrap(); + *signature_shares.get_mut(&id2).unwrap() = + SignatureShare::new(signature_shares[&id2].to_scalar() + one); + let e = frost::aggregate(&signing_package, &signature_shares, &pubkey_package).unwrap_err(); - assert_eq!(e.culprit(), Some(id)); - assert_eq!(e, Error::InvalidSignatureShare { culprit: id }); + assert_eq!(e.culprits(), vec![id1]); + assert_eq!( + e, + Error::InvalidSignatureShare { + culprits: vec![id1] + } + ); + + let e = frost::aggregate_custom( + &signing_package, + &signature_shares, + &pubkey_package, + crate::CheaterDetection::Disabled, + ) + .unwrap_err(); + assert_eq!(e.culprits(), vec![]); + assert_eq!(e, Error::InvalidSignature); + + let e = frost::aggregate_custom( + &signing_package, + &signature_shares, + &pubkey_package, + crate::CheaterDetection::FirstCheater, + ) + .unwrap_err(); + assert_eq!(e.culprits(), vec![id1]); + assert_eq!( + e, + Error::InvalidSignatureShare { + culprits: vec![id1] + } + ); + + let e = frost::aggregate_custom( + &signing_package, + &signature_shares, + &pubkey_package, + CheaterDetection::AllCheaters, + ) + .unwrap_err(); + assert_eq!(e.culprits(), vec![id1, id2]); + assert_eq!( + e, + Error::InvalidSignatureShare { + culprits: vec![id1, id2] + } + ); } /// Test NCC-E008263-4VP audit finding (PublicKeyPackage). @@ -729,7 +778,7 @@ fn check_part2_error( round1_packages.get_mut(&id).unwrap().proof_of_knowledge.z = round1_packages[&id].proof_of_knowledge.z + one; let e = frost::keys::dkg::part2(round1_secret_package, &round1_packages).unwrap_err(); - assert_eq!(e.culprit(), Some(id)); + assert_eq!(e.culprits(), vec![id]); assert_eq!(e, Error::InvalidProofOfKnowledge { culprit: id }); } @@ -738,17 +787,17 @@ pub fn check_error_culprit() { let identifier: frost::Identifier = 42u16.try_into().unwrap(); let e = Error::InvalidSignatureShare { - culprit: identifier, + culprits: vec![identifier], }; - assert_eq!(e.culprit(), Some(identifier)); + assert_eq!(e.culprits(), vec![identifier]); let e = Error::InvalidProofOfKnowledge { culprit: identifier, }; - assert_eq!(e.culprit(), Some(identifier)); + assert_eq!(e.culprits(), vec![identifier]); let e: Error = Error::InvalidSignature; - assert_eq!(e.culprit(), None); + assert_eq!(e.culprits(), vec![]); } /// Test identifier derivation with a Ciphersuite @@ -930,8 +979,8 @@ fn check_verifying_shares( SignatureShare::new(signature_shares[&id].to_scalar() + one); let e = frost::aggregate(&signing_package, &signature_shares, &pubkeys).unwrap_err(); - assert_eq!(e.culprit(), Some(id)); - assert_eq!(e, Error::InvalidSignatureShare { culprit: id }); + assert_eq!(e.culprits(), vec![id]); + assert_eq!(e, Error::InvalidSignatureShare { culprits: vec![id] }); } // Checks if `verify_signature_share()` works correctly. diff --git a/frost-ed25519/src/lib.rs b/frost-ed25519/src/lib.rs index 334922d..6d75602 100644 --- a/frost-ed25519/src/lib.rs +++ b/frost-ed25519/src/lib.rs @@ -419,6 +419,25 @@ pub fn aggregate( frost::aggregate(signing_package, signature_shares, pubkeys) } +/// The type of cheater detection to use. +pub type CheaterDetection = frost::CheaterDetection; + +/// Like [`aggregate()`], but allow specifying a specific cheater detection +/// strategy. +pub fn aggregate_custom( + signing_package: &SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &keys::PublicKeyPackage, + cheater_detection: CheaterDetection, +) -> Result { + frost::aggregate_custom( + signing_package, + signature_shares, + pubkeys, + cheater_detection, + ) +} + /// A signing key for a Schnorr signature on FROST(Ed25519, SHA-512). pub type SigningKey = frost_core::SigningKey; diff --git a/frost-ed448/src/lib.rs b/frost-ed448/src/lib.rs index 2acc590..3c45775 100644 --- a/frost-ed448/src/lib.rs +++ b/frost-ed448/src/lib.rs @@ -413,6 +413,25 @@ pub fn aggregate( frost::aggregate(signing_package, signature_shares, pubkeys) } +/// The type of cheater detection to use. +pub type CheaterDetection = frost::CheaterDetection; + +/// Like [`aggregate()`], but allow specifying a specific cheater detection +/// strategy. +pub fn aggregate_custom( + signing_package: &SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &keys::PublicKeyPackage, + cheater_detection: CheaterDetection, +) -> Result { + frost::aggregate_custom( + signing_package, + signature_shares, + pubkeys, + cheater_detection, + ) +} + /// A signing key for a Schnorr signature on FROST(Ed448, SHAKE256). pub type SigningKey = frost_core::SigningKey; diff --git a/frost-p256/src/lib.rs b/frost-p256/src/lib.rs index b21cb8d..c99a26f 100644 --- a/frost-p256/src/lib.rs +++ b/frost-p256/src/lib.rs @@ -429,6 +429,25 @@ pub fn aggregate( frost::aggregate(signing_package, signature_shares, pubkeys) } +/// The type of cheater detection to use. +pub type CheaterDetection = frost::CheaterDetection; + +/// Like [`aggregate()`], but allow specifying a specific cheater detection +/// strategy. +pub fn aggregate_custom( + signing_package: &SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &keys::PublicKeyPackage, + cheater_detection: CheaterDetection, +) -> Result { + frost::aggregate_custom( + signing_package, + signature_shares, + pubkeys, + cheater_detection, + ) +} + /// A signing key for a Schnorr signature on FROST(P-256, SHA-256). pub type SigningKey = frost_core::SigningKey

; diff --git a/frost-ristretto255/src/lib.rs b/frost-ristretto255/src/lib.rs index cd376b6..9951aad 100644 --- a/frost-ristretto255/src/lib.rs +++ b/frost-ristretto255/src/lib.rs @@ -405,6 +405,25 @@ pub fn aggregate( frost::aggregate(signing_package, signature_shares, pubkeys) } +/// The type of cheater detection to use. +pub type CheaterDetection = frost::CheaterDetection; + +/// Like [`aggregate()`], but allow specifying a specific cheater detection +/// strategy. +pub fn aggregate_custom( + signing_package: &SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &keys::PublicKeyPackage, + cheater_detection: CheaterDetection, +) -> Result { + frost::aggregate_custom( + signing_package, + signature_shares, + pubkeys, + cheater_detection, + ) +} + /// A signing key for a Schnorr signature on FROST(ristretto255, SHA-512). pub type SigningKey = frost_core::SigningKey; diff --git a/frost-secp256k1/src/lib.rs b/frost-secp256k1/src/lib.rs index 9485a23..ee9b87a 100644 --- a/frost-secp256k1/src/lib.rs +++ b/frost-secp256k1/src/lib.rs @@ -429,6 +429,25 @@ pub fn aggregate( frost::aggregate(signing_package, signature_shares, pubkeys) } +/// The type of cheater detection to use. +pub type CheaterDetection = frost::CheaterDetection; + +/// Like [`aggregate()`], but allow specifying a specific cheater detection +/// strategy. +pub fn aggregate_custom( + signing_package: &SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &keys::PublicKeyPackage, + cheater_detection: CheaterDetection, +) -> Result { + frost::aggregate_custom( + signing_package, + signature_shares, + pubkeys, + cheater_detection, + ) +} + /// A signing key for a Schnorr signature on FROST(secp256k1, SHA-256). pub type SigningKey = frost_core::SigningKey; From 29fa065e352671aa173e58a5c6177bfa5e974bd3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 16:22:35 +0000 Subject: [PATCH 29/86] build(deps): bump reviewdog/action-actionlint from 1.64.1 to 1.65.2 (#885) Bumps [reviewdog/action-actionlint](https://github.com/reviewdog/action-actionlint) from 1.64.1 to 1.65.2. - [Release notes](https://github.com/reviewdog/action-actionlint/releases) - [Commits](https://github.com/reviewdog/action-actionlint/compare/v1.64.1...v1.65.2) --- updated-dependencies: - dependency-name: reviewdog/action-actionlint dependency-version: 1.65.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 20cbcd7..413f49e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -165,7 +165,7 @@ jobs: continue-on-error: true steps: - uses: actions/checkout@v4.2.2 - - uses: reviewdog/action-actionlint@v1.64.1 + - uses: reviewdog/action-actionlint@v1.65.2 with: level: warning fail_level: none From e0d2176da19596fe4d3e6ee4092d6dc697f45105 Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Fri, 3 Oct 2025 03:33:01 +0500 Subject: [PATCH 30/86] feat(frost-core): simplify trait bounds, don't use the allocator in some places (#810) * feat(frost-core): simplify trait bounds, don't use the allocator in some places * use as_slice() * Copy -> Clone * fix build --- frost-core/src/lib.rs | 5 +- frost-core/src/serialization.rs | 48 +++++++------------ frost-core/src/signature.rs | 37 +++++++------- .../src/tests/coefficient_commitment.rs | 2 +- frost-core/src/tests/repairable.rs | 10 ++-- frost-core/src/tests/vectors.rs | 3 +- frost-core/src/tests/vectors_dkg.rs | 2 + frost-core/src/tests/vss_commitment.rs | 4 +- frost-core/src/traits.rs | 16 ++++--- 9 files changed, 63 insertions(+), 64 deletions(-) diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index 653ed4f..66b9a2e 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -272,12 +272,13 @@ where fn from_hex>(hex: T) -> Result { let v: Vec = FromHex::from_hex(hex).map_err(|_| "invalid hex")?; - match v.try_into() { + let ret = match v.as_slice().try_into() { Ok(bytes) => <::Field>::deserialize(&bytes) .map(|scalar| Self(scalar)) .map_err(|_| "malformed scalar encoding"), Err(_) => Err("malformed scalar encoding"), - } + }; + ret } } diff --git a/frost-core/src/serialization.rs b/frost-core/src/serialization.rs index fe1df7b..73195c1 100644 --- a/frost-core/src/serialization.rs +++ b/frost-core/src/serialization.rs @@ -27,10 +27,8 @@ where /// Deserialize a Scalar from a serialized buffer. pub fn deserialize(bytes: &[u8]) -> Result> { - let serialized: <::Field as Field>::Serialization = bytes - .to_vec() - .try_into() - .map_err(|_| FieldError::MalformedScalar)?; + let serialized: <::Field as Field>::Serialization = + bytes.try_into().map_err(|_| FieldError::MalformedScalar)?; let scalar = <::Field>::deserialize(&serialized)?; Ok(Self(scalar)) } @@ -59,18 +57,13 @@ where where D: serde::Deserializer<'de>, { - // Get size from the size of the zero scalar + // Get serialization buffer from the zero scalar let zero = <::Field as Field>::zero(); - let len = <::Field as Field>::serialize(&zero) - .as_ref() - .len(); - - let mut bytes = vec![0u8; len]; - serdect::array::deserialize_hex_or_bin(&mut bytes[..], deserializer)?; - let array = bytes - .try_into() - .map_err(|_| serde::de::Error::custom("invalid byte length"))?; - <::Group as Group>::Field::deserialize(&array) + let mut serialization = <::Field as Field>::serialize(&zero); + + serdect::array::deserialize_hex_or_bin(serialization.as_mut(), deserializer)?; + + <::Group as Group>::Field::deserialize(&serialization) .map(|scalar| Self(scalar)) .map_err(serde::de::Error::custom) } @@ -91,10 +84,8 @@ where /// Deserialize an Element. Returns an error if it's malformed or is the /// identity. pub fn deserialize(bytes: &[u8]) -> Result> { - let serialized: ::Serialization = bytes - .to_vec() - .try_into() - .map_err(|_| FieldError::MalformedScalar)?; + let serialized: ::Serialization = + bytes.try_into().map_err(|_| FieldError::MalformedScalar)?; let scalar = ::deserialize(&serialized)?; Ok(Self(scalar)) } @@ -124,19 +115,14 @@ where where D: serde::Deserializer<'de>, { - // Get size from the size of the generator + // Get serialization buffer from the generator let generator = ::generator(); - let len = ::serialize(&generator) - .expect("serializing the generator always works") - .as_ref() - .len(); - - let mut bytes = vec![0u8; len]; - serdect::array::deserialize_hex_or_bin(&mut bytes[..], deserializer)?; - let array = bytes - .try_into() - .map_err(|_| serde::de::Error::custom("invalid byte length"))?; - ::deserialize(&array) + let mut serialization = + ::serialize(&generator).expect("serializing the generator always works"); + + serdect::array::deserialize_hex_or_bin(serialization.as_mut(), deserializer)?; + + ::deserialize(&serialization) .map(|element| Self(element)) .map_err(serde::de::Error::custom) } diff --git a/frost-core/src/signature.rs b/frost-core/src/signature.rs index 9152769..02ccaac 100644 --- a/frost-core/src/signature.rs +++ b/frost-core/src/signature.rs @@ -38,34 +38,31 @@ where // and get its length. Note that we can't use the identity because it can be encoded // shorter in some cases (e.g. P-256, which uses SEC1 encoding). let generator = ::generator(); - let mut R_bytes = Vec::from(::serialize(&generator)?.as_ref()); - let R_bytes_len = R_bytes.len(); + let mut R_serialization = ::serialize(&generator)?; + let R_bytes_len = R_serialization.as_ref().len(); - let one = <::Field as Field>::zero(); - let mut z_bytes = - Vec::from(<::Field as Field>::serialize(&one).as_ref()); - let z_bytes_len = z_bytes.len(); + let zero = <::Field as Field>::zero(); + let mut z_serialization = <::Field as Field>::serialize(&zero); + let z_bytes_len = z_serialization.as_ref().len(); if bytes.len() != R_bytes_len + z_bytes_len { return Err(Error::MalformedSignature); } - R_bytes[..].copy_from_slice(bytes.get(0..R_bytes_len).ok_or(Error::MalformedSignature)?); - - let R_serialization = &R_bytes.try_into().map_err(|_| Error::MalformedSignature)?; + R_serialization + .as_mut() + .copy_from_slice(bytes.get(0..R_bytes_len).ok_or(Error::MalformedSignature)?); // We extract the exact length of bytes we expect, not just the remaining bytes with `bytes[R_bytes_len..]` - z_bytes[..].copy_from_slice( + z_serialization.as_mut().copy_from_slice( bytes .get(R_bytes_len..R_bytes_len + z_bytes_len) .ok_or(Error::MalformedSignature)?, ); - let z_serialization = &z_bytes.try_into().map_err(|_| Error::MalformedSignature)?; - Ok(Self { - R: ::deserialize(R_serialization)?, - z: <::Field>::deserialize(z_serialization)?, + R: ::deserialize(&R_serialization)?, + z: <::Field>::deserialize(&z_serialization)?, }) } @@ -77,10 +74,16 @@ where /// Converts this signature to its default byte serialization. #[cfg_attr(feature = "internals", visibility::make(pub))] pub(crate) fn default_serialize(&self) -> Result, Error> { - let mut bytes = Vec::::new(); + let R_serialization = ::serialize(&self.R)?; + let z_serialization = <::Field>::serialize(&self.z); + + let R_bytes = R_serialization.as_ref(); + let z_bytes = z_serialization.as_ref(); + + let mut bytes = Vec::with_capacity(R_bytes.len() + z_bytes.len()); - bytes.extend(::serialize(&self.R)?.as_ref()); - bytes.extend(<::Field>::serialize(&self.z).as_ref()); + bytes.extend(R_bytes); + bytes.extend(z_bytes); Ok(bytes) } diff --git a/frost-core/src/tests/coefficient_commitment.rs b/frost-core/src/tests/coefficient_commitment.rs index b3392ca..533bf54 100644 --- a/frost-core/src/tests/coefficient_commitment.rs +++ b/frost-core/src/tests/coefficient_commitment.rs @@ -44,7 +44,7 @@ pub fn check_create_coefficient_commitment_error( let values = &commitment_helpers["elements"]; let serialized: ::Serialization = ::Serialization::try_from( - hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(), + &hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(), ) .debugless_unwrap(); diff --git a/frost-core/src/tests/repairable.rs b/frost-core/src/tests/repairable.rs index a922e32..27136f8 100644 --- a/frost-core/src/tests/repairable.rs +++ b/frost-core/src/tests/repairable.rs @@ -92,9 +92,11 @@ pub fn check_rts(mut rng: R) { fn generate_scalar_from_byte_string( bs: &str, -) -> <<::Group as Group>::Field as Field>::Scalar { +) -> <::Field as Field>::Scalar { let decoded = hex::decode(bs).unwrap(); - let out = <::Field>::deserialize(&decoded.try_into().debugless_unwrap()); + let out = <::Field>::deserialize( + &decoded.as_slice().try_into().debugless_unwrap(), + ); out.unwrap() } @@ -160,7 +162,7 @@ pub fn check_repair_share_step_2(repair_share_helpers: &Value) { let expected: Scalar = repair_share_step_2::(&[value_1, value_2, value_3]); - let actual: <<::Group as Group>::Field as Field>::Scalar = + let actual: <::Field as Field>::Scalar = generate_scalar_from_byte_string::(values["random_scalar_sum"].as_str().unwrap()); assert!(actual == expected); @@ -198,7 +200,7 @@ pub fn check_repair_share_step_3( &commitment, ); - let actual_sigma: <<::Group as Group>::Field as Field>::Scalar = + let actual_sigma: <::Field as Field>::Scalar = generate_scalar_from_byte_string::(sigmas["sigma_sum"].as_str().unwrap()); let actual: SecretShare = SecretShare::new( Identifier::try_from(2).unwrap(), diff --git a/frost-core/src/tests/vectors.rs b/frost-core/src/tests/vectors.rs index c11b144..950fe9a 100644 --- a/frost-core/src/tests/vectors.rs +++ b/frost-core/src/tests/vectors.rs @@ -45,7 +45,8 @@ pub fn parse_test_vectors(json_vectors: &Value) -> TestVectors::Field>::deserialize(&vec.try_into().debugless_unwrap()).unwrap() + <::Field>::deserialize(&vec.as_slice().try_into().debugless_unwrap()) + .unwrap() }) .collect(); diff --git a/frost-core/src/tests/vectors_dkg.rs b/frost-core/src/tests/vectors_dkg.rs index be268d2..02d0c85 100644 --- a/frost-core/src/tests/vectors_dkg.rs +++ b/frost-core/src/tests/vectors_dkg.rs @@ -35,12 +35,14 @@ fn json_to_scalar( vector: &Value, ) -> <::Field as Field>::Serialization { (hex::decode(vector.as_str().unwrap()).unwrap()) + .as_slice() .try_into() .debugless_unwrap() } fn json_to_element(vector: &Value) -> ::Serialization { (hex::decode(vector.as_str().unwrap()).unwrap()) + .as_slice() .try_into() .debugless_unwrap() } diff --git a/frost-core/src/tests/vss_commitment.rs b/frost-core/src/tests/vss_commitment.rs index 7b32b5e..6949581 100644 --- a/frost-core/src/tests/vss_commitment.rs +++ b/frost-core/src/tests/vss_commitment.rs @@ -161,7 +161,7 @@ pub fn check_deserialize_vss_commitment_error::Serialization = ::Serialization::try_from( - hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(), + &hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(), ) .debugless_unwrap(); // --- @@ -194,7 +194,7 @@ pub fn check_deserialize_whole_vss_commitment_error::Serialization = ::Serialization::try_from( - hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(), + &hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(), ) .debugless_unwrap(); // --- diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs index 9714015..c65d7d0 100644 --- a/frost-core/src/traits.rs +++ b/frost-core/src/traits.rs @@ -25,7 +25,7 @@ use crate::{ /// pass-through, implemented for a type just for the ciphersuite, and calls through to another /// implementation underneath, so that this trait does not have to be implemented for types you /// don't own. -pub trait Field: Copy + Clone { +pub trait Field: Copy { /// An element of the scalar field GF(p). /// The Eq/PartialEq implementation MUST be constant-time. type Scalar: Add @@ -37,7 +37,7 @@ pub trait Field: Copy + Clone { + Sub; /// A unique byte array buf of fixed length N. - type Serialization: AsRef<[u8]> + Debug + TryFrom>; + type Serialization: Clone + AsRef<[u8]> + AsMut<[u8]> + for<'a> TryFrom<&'a [u8]> + Debug; /// Returns the zero element of the field, the additive identity. fn zero() -> Self::Scalar; @@ -85,7 +85,7 @@ pub type Scalar = <<::Group as Group>::Field as Field>::Sca /// pass-through, implemented for a type just for the ciphersuite, and calls through to another /// implementation underneath, so that this trait does not have to be implemented for types you /// don't own. -pub trait Group: Copy + Clone + PartialEq { +pub trait Group: Copy + PartialEq { /// A prime order finite field GF(q) over which all scalar values for our prime order group can /// be multiplied are defined. type Field: Field; @@ -102,7 +102,7 @@ pub trait Group: Copy + Clone + PartialEq { /// A unique byte array buf of fixed length N. /// /// Little-endian! - type Serialization: AsRef<[u8]> + Debug + TryFrom>; + type Serialization: Clone + AsRef<[u8]> + AsMut<[u8]> + for<'a> TryFrom<&'a [u8]> + Debug; /// The order of the the quotient group when the prime order subgroup divides the order of the /// full curve group. @@ -147,7 +147,7 @@ pub type Element = <::Group as Group>::Element; /// /// [FROST ciphersuite]: https://datatracker.ietf.org/doc/html/rfc9591#name-ciphersuites // See https://github.com/ZcashFoundation/frost/issues/693 for reasoning about the 'static bound. -pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static { +pub trait Ciphersuite: Copy + PartialEq + Debug + 'static { /// The ciphersuite ID string. It should be equal to the contextString in /// the spec. For new ciphersuites, this should be a string that identifies /// the ciphersuite; it's recommended to use a similar format to the @@ -162,7 +162,11 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static { /// A unique byte array of fixed length that is the `Group::ElementSerialization` + /// `Group::ScalarSerialization` - type SignatureSerialization: AsRef<[u8]> + TryFrom>; + type SignatureSerialization: Clone + + AsRef<[u8]> + + AsMut<[u8]> + + for<'a> TryFrom<&'a [u8]> + + Debug; /// [H1] for a FROST ciphersuite. /// From 80fd87eb0db11d58432b5c5f6eaee6816d3cbdd8 Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 6 Oct 2025 09:38:23 -0300 Subject: [PATCH 31/86] ci: install clippy (#923) --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 413f49e..219d4c7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -92,6 +92,8 @@ jobs: persist-credentials: false - uses: dtolnay/rust-toolchain@stable + with: + components: clippy - name: Check workflow permissions id: check_permissions From 76dc3a2efb459e82cc246bd63af3c0327145b0ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:59:34 -0300 Subject: [PATCH 32/86] build(deps): bump reviewdog/action-actionlint from 1.65.2 to 1.68.0 (#938) Bumps [reviewdog/action-actionlint](https://github.com/reviewdog/action-actionlint) from 1.65.2 to 1.68.0. - [Release notes](https://github.com/reviewdog/action-actionlint/releases) - [Commits](https://github.com/reviewdog/action-actionlint/compare/v1.65.2...v1.68.0) --- updated-dependencies: - dependency-name: reviewdog/action-actionlint dependency-version: 1.68.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 219d4c7..17aad0a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -167,7 +167,7 @@ jobs: continue-on-error: true steps: - uses: actions/checkout@v4.2.2 - - uses: reviewdog/action-actionlint@v1.65.2 + - uses: reviewdog/action-actionlint@v1.68.0 with: level: warning fail_level: none From dbaa19007ad1d8f8466773c7fa2c08260479bc90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:00:54 -0300 Subject: [PATCH 33/86] build(deps): bump clechasseur/rs-clippy-check from 4 to 5 (#928) Bumps [clechasseur/rs-clippy-check](https://github.com/clechasseur/rs-clippy-check) from 4 to 5. - [Release notes](https://github.com/clechasseur/rs-clippy-check/releases) - [Commits](https://github.com/clechasseur/rs-clippy-check/compare/v4...v5) --- updated-dependencies: - dependency-name: clechasseur/rs-clippy-check dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 17aad0a..9a00ee4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -104,7 +104,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Run clippy action to produce annotations - uses: clechasseur/rs-clippy-check@v4 + uses: clechasseur/rs-clippy-check@v5 if: ${{ steps.check_permissions.outputs.has-permission }} with: args: --all-features --all-targets -- -D warnings From aad8b8ac34d23b16046e72028cf2dbac7d959ed6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:04:38 -0300 Subject: [PATCH 34/86] build(deps): bump codecov/codecov-action from 5.3.1 to 5.5.1 (#924) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.3.1 to 5.5.1. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v5.3.1...v5.5.1) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 5.5.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/coverage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index dece196..1cf1a1c 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -41,4 +41,4 @@ jobs: run: cargo llvm-cov report --lcov --ignore-filename-regex '.*(tests).*|benches.rs|gencode|helpers.rs|interoperability_tests.rs' --output-path lcov.info - name: Upload coverage report to Codecov - uses: codecov/codecov-action@v5.3.1 + uses: codecov/codecov-action@v5.5.1 From 72c48478f619d3e49da9f2693c83f782d15ad198 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:13:54 -0300 Subject: [PATCH 35/86] build(deps): bump actions/checkout from 4.2.2 to 5.0.0 (#925) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.2 to 5.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4.2.2...v5.0.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 5.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/coverage.yaml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/main.yml | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 1cf1a1c..200f088 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -23,7 +23,7 @@ jobs: RUST_BACKTRACE: full steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d587aa2..eb62d5d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -36,7 +36,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the source code - uses: actions/checkout@v4.2.2 + uses: actions/checkout@v6.0.0 with: persist-credentials: false diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9a00ee4..94893ed 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@v6.0.0 - uses: dtolnay/rust-toolchain@beta - run: cargo build @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@v6.0.0 # Re-resolve Cargo.lock with minimal versions. # This only works with nightly. We pin to a specific version because # newer versions use lock file version 4, but the MSRV cargo does not @@ -45,7 +45,7 @@ jobs: # runs-on: ubuntu-latest # steps: - # - uses: actions/checkout@v4.2.2 + # - uses: actions/checkout@v6.0.0 # - uses: dtolnay/rust-toolchain@stable # - run: cargo install cargo-all-features # # We check and then test because some test dependencies could help @@ -65,7 +65,7 @@ jobs: matrix: crate: [ristretto255, ed25519, p256, secp256k1, secp256k1-tr, rerandomized] steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@v6.0.0 - uses: dtolnay/rust-toolchain@master with: toolchain: stable @@ -78,7 +78,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@v6.0.0 - uses: dtolnay/rust-toolchain@beta - run: cargo test --release --all-features @@ -87,7 +87,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false @@ -118,7 +118,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false @@ -135,7 +135,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false @@ -154,7 +154,7 @@ jobs: RUSTDOCFLAGS: -D warnings steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false @@ -166,7 +166,7 @@ jobs: runs-on: ubuntu-latest continue-on-error: true steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@v6.0.0 - uses: reviewdog/action-actionlint@v1.68.0 with: level: warning From 40edb6e351760cb72b523a9ea180eba011675dc1 Mon Sep 17 00:00:00 2001 From: Conrado Date: Thu, 27 Nov 2025 18:31:07 -0300 Subject: [PATCH 36/86] ci: commit Cargo.lock, update crypto-common to avoid generic-array deprecation warning (#939) --- .gitignore | 1 - Cargo.lock | 1714 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1714 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index 5d3d773..bbb0762 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ /target **/*.rs.bk -Cargo.lock *~ **/.DS_Store .vscode/* diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2ca16f4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1714 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +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 = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "windows-sys 0.59.0", +] + +[[package]] +name = "const-crc32-nostd" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808ac43170e95b11dd23d78aa9eaac5bea45776a602955552c4e833f3f0f823d" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools 0.13.0", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto 0.2.9", + "rand_core 0.6.4", + "rustc_version", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "debugless-unwrap" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f400d0750c0c069e8493f2256cb4da6f604b6d2eeb69a0ca8863acde352f8400" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derive-getters" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "ed448-goldilocks" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88322282bccdc6fa7ab65b0c30cb877fba541547653436d08bb775fa4a4307b4" +dependencies = [ + "fiat-crypto 0.1.20", + "hex", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "frost-core" +version = "2.2.0" +dependencies = [ + "byteorder", + "const-crc32-nostd", + "criterion", + "debugless-unwrap", + "derive-getters", + "document-features", + "hex", + "itertools 0.14.0", + "lazy_static", + "postcard", + "proptest", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde", + "serde_json", + "serdect", + "thiserror", + "visibility", + "zeroize", +] + +[[package]] +name = "frost-ed25519" +version = "2.2.0" +dependencies = [ + "criterion", + "curve25519-dalek", + "document-features", + "ed25519-dalek", + "frost-core", + "frost-rerandomized", + "hex", + "insta", + "lazy_static", + "proptest", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde_json", + "sha2", +] + +[[package]] +name = "frost-ed448" +version = "2.2.0" +dependencies = [ + "criterion", + "document-features", + "ed448-goldilocks", + "frost-core", + "frost-rerandomized", + "hex", + "insta", + "lazy_static", + "proptest", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde_json", + "sha3", +] + +[[package]] +name = "frost-p256" +version = "2.2.0" +dependencies = [ + "criterion", + "document-features", + "frost-core", + "frost-rerandomized", + "hex", + "insta", + "lazy_static", + "p256", + "proptest", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde_json", + "sha2", +] + +[[package]] +name = "frost-rerandomized" +version = "2.2.0" +dependencies = [ + "derive-getters", + "document-features", + "frost-core", + "hex", + "rand_core 0.6.4", +] + +[[package]] +name = "frost-ristretto255" +version = "2.2.0" +dependencies = [ + "criterion", + "curve25519-dalek", + "document-features", + "frost-core", + "frost-rerandomized", + "hex", + "insta", + "lazy_static", + "postcard", + "proptest", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde_json", + "sha2", +] + +[[package]] +name = "frost-secp256k1" +version = "2.2.0" +dependencies = [ + "criterion", + "document-features", + "frost-core", + "frost-rerandomized", + "hex", + "insta", + "k256", + "lazy_static", + "proptest", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde_json", + "sha2", +] + +[[package]] +name = "frost-secp256k1-tr" +version = "2.2.0" +dependencies = [ + "criterion", + "document-features", + "frost-core", + "frost-rerandomized", + "hex", + "insta", + "k256", + "lazy_static", + "proptest", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "secp256k1", + "serde_json", + "sha2", +] + +[[package]] +name = "gencode" +version = "0.1.0" +dependencies = [ + "regex", + "serde_json", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[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 = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "insta" +version = "1.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b246c455fbf8e7bdda56a226b525b24b601c0bbe15458beb72412678319cda5a" +dependencies = [ + "console", + "once_cell", + "serde", + "similar", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[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.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "elliptic-curve", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "elliptic-curve", + "primeorder", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "heapless", + "serde", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[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 = "proptest" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "num-traits", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[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.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[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 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" +dependencies = [ + "bitcoin_hashes", + "rand 0.9.2", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" +dependencies = [ + "cc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[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 = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[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.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[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.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] From 1882937ed8bae5810b8a49e5f442e9afbacc0607 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 20:57:36 +0000 Subject: [PATCH 37/86] build(deps): bump reviewdog/action-actionlint from 1.68.0 to 1.69.0 (#944) Bumps [reviewdog/action-actionlint](https://github.com/reviewdog/action-actionlint) from 1.68.0 to 1.69.0. - [Release notes](https://github.com/reviewdog/action-actionlint/releases) - [Commits](https://github.com/reviewdog/action-actionlint/compare/v1.68.0...v1.69.0) --- updated-dependencies: - dependency-name: reviewdog/action-actionlint dependency-version: 1.69.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 94893ed..4db6557 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -167,7 +167,7 @@ jobs: continue-on-error: true steps: - uses: actions/checkout@v6.0.0 - - uses: reviewdog/action-actionlint@v1.68.0 + - uses: reviewdog/action-actionlint@v1.69.0 with: level: warning fail_level: none From 4431ca12c40d1c26059a32c62c8f55d25f0a2f06 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:50:53 +0000 Subject: [PATCH 38/86] ci(mergify): upgrade configuration to current format (#934) Co-authored-by: Mergify <37929162+mergify[bot]@users.noreply.github.com> --- .mergify.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.mergify.yml b/.mergify.yml index 491837e..7a64a0f 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -9,7 +9,6 @@ queue_rules: # which are the same as the GitHub main branch protection rules # https://docs.mergify.com/conditions/#about-branch-protection - base=main - allow_inplace_checks: true batch_size: 2 # Wait for a few minutes to embark 2 tickets together in a merge train batch_max_wait_time: "3 minutes" From 43c88274e4e000a58e85f0e0805604ee7d1843f7 Mon Sep 17 00:00:00 2001 From: Conrado Date: Tue, 9 Dec 2025 07:30:44 -0300 Subject: [PATCH 39/86] all: remove cheater-detection feature (#958) --- frost-core/CHANGELOG.md | 11 ++++++++++ frost-core/Cargo.toml | 4 +--- frost-core/src/lib.rs | 24 ++++++--------------- frost-core/src/tests/ciphersuite_generic.rs | 9 -------- frost-ed25519/Cargo.toml | 4 +--- frost-ed448/Cargo.toml | 4 +--- frost-p256/Cargo.toml | 4 +--- frost-rerandomized/Cargo.toml | 4 +--- frost-ristretto255/Cargo.toml | 4 +--- frost-secp256k1-tr/Cargo.toml | 4 +--- frost-secp256k1/Cargo.toml | 4 +--- 11 files changed, 25 insertions(+), 51 deletions(-) diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index a5f2e42..80d2fc8 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -2,6 +2,17 @@ Entries are listed in reverse chronological order. +## Unreleases + +### Breaking Changes + +* The `cheater-detection` feature was removed. If you relied on it (either by + using the default features, or by explicitly enabling it), then you don't have + to do anything (other than not enabling it explicitly if you were doing so); + the default behaviour is now as if `cheater-detection` was enabled. If you + explicitly *did not enable* it, you can avoid cheater detection by calling + `aggregate_custom()` with `CheaterDetection::Disabled`. + ## 2.2.0 ### Security Fixes diff --git a/frost-core/Cargo.toml b/frost-core/Cargo.toml index 64d6088..9e7eb0c 100644 --- a/frost-core/Cargo.toml +++ b/frost-core/Cargo.toml @@ -45,7 +45,7 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "std"] # No longer needed. Kept for retrocompatibility until 3.0.0 std = [] #! ## Features @@ -60,8 +60,6 @@ serde = ["dep:serde", "dep:serdect"] serialization = ["serde", "dep:postcard"] # Exposes ciphersuite-generic tests for other crates to use test-impl = ["dep:proptest", "dep:serde_json", "dep:criterion"] -# Enable cheater detection -cheater-detection = [] [lib] bench = false diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index 66b9a2e..2d6fb29 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -570,24 +570,12 @@ pub fn aggregate( where C: Ciphersuite, { - #[cfg(feature = "cheater-detection")] - { - aggregate_custom( - signing_package, - signature_shares, - pubkeys, - CheaterDetection::FirstCheater, - ) - } - #[cfg(not(feature = "cheater-detection"))] - { - aggregate_custom( - signing_package, - signature_shares, - pubkeys, - CheaterDetection::Disabled, - ) - } + aggregate_custom( + signing_package, + signature_shares, + pubkeys, + CheaterDetection::FirstCheater, + ) } /// The type of cheater detection to use. diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs index 195c55f..ce46213 100644 --- a/frost-core/src/tests/ciphersuite_generic.rs +++ b/frost-core/src/tests/ciphersuite_generic.rs @@ -315,14 +315,6 @@ fn check_aggregate_errors( signature_shares: BTreeMap, frost::round2::SignatureShare>, pubkey_package: frost::keys::PublicKeyPackage, ) { - #[cfg(not(feature = "cheater-detection"))] - let pubkey_package = PublicKeyPackage { - header: pubkey_package.header, - verifying_shares: BTreeMap::new(), - verifying_key: pubkey_package.verifying_key, - }; - - #[cfg(feature = "cheater-detection")] check_aggregate_corrupted_share( signing_package.clone(), signature_shares.clone(), @@ -336,7 +328,6 @@ fn check_aggregate_errors( ); } -#[cfg(feature = "cheater-detection")] fn check_aggregate_corrupted_share( signing_package: frost::SigningPackage, mut signature_shares: BTreeMap, frost::round2::SignatureShare>, diff --git a/frost-ed25519/Cargo.toml b/frost-ed25519/Cargo.toml index 0f86b61..eb456a1 100644 --- a/frost-ed25519/Cargo.toml +++ b/frost-ed25519/Cargo.toml @@ -37,7 +37,7 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "std"] # No longer needed. Kept for retrocompatibility until 3.0.0 std = [] #! ## Features @@ -47,8 +47,6 @@ std = [] serde = ["frost-core/serde"] ## Enable a default serialization format. Enables `serde`. serialization = ["serde", "frost-core/serialization", "frost-rerandomized/serialization"] -## Enable cheater detection -cheater-detection = ["frost-core/cheater-detection", "frost-rerandomized/cheater-detection"] [lib] # Disables non-criterion benchmark which is not used; prevents errors diff --git a/frost-ed448/Cargo.toml b/frost-ed448/Cargo.toml index 4d834fe..79a1d56 100644 --- a/frost-ed448/Cargo.toml +++ b/frost-ed448/Cargo.toml @@ -36,7 +36,7 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "std"] # No longer needed. Kept for retrocompatibility until 3.0.0 std = [] #! ## Features @@ -46,8 +46,6 @@ std = [] serde = ["frost-core/serde"] ## Enable a default serialization format. Enables `serde`. serialization = ["serde", "frost-core/serialization", "frost-rerandomized/serialization"] -## Enable cheater detection -cheater-detection = ["frost-core/cheater-detection", "frost-rerandomized/cheater-detection"] [lib] # Disables non-criterion benchmark which is not used; prevents errors diff --git a/frost-p256/Cargo.toml b/frost-p256/Cargo.toml index d047e7b..0299777 100644 --- a/frost-p256/Cargo.toml +++ b/frost-p256/Cargo.toml @@ -36,7 +36,7 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "std"] # No longer needed. Kept for retrocompatibility until 3.0.0 std = [] #! ## Features @@ -46,8 +46,6 @@ std = [] serde = ["frost-core/serde"] ## Enable a default serialization format. Enables `serde`. serialization = ["serde", "frost-core/serialization", "frost-rerandomized/serialization"] -## Enable cheater detection -cheater-detection = ["frost-core/cheater-detection", "frost-rerandomized/cheater-detection"] [lib] # Disables non-criterion benchmark which is not used; prevents errors diff --git a/frost-rerandomized/Cargo.toml b/frost-rerandomized/Cargo.toml index 6470cf9..c255253 100644 --- a/frost-rerandomized/Cargo.toml +++ b/frost-rerandomized/Cargo.toml @@ -25,7 +25,7 @@ rand_core.workspace = true [dev-dependencies] [features] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "std"] # No longer needed. Kept for retrocompatibility until 3.0.0 std = [] #! ## Features @@ -35,7 +35,5 @@ std = [] serde = ["frost-core/serde"] # Exposes ciphersuite-generic tests for other crates to use test-impl = ["frost-core/test-impl", "serialization"] -## Enable cheater detection -cheater-detection = ["frost-core/cheater-detection"] ## Enable a default serialization format. Enables `serde`. serialization = ["serde", "frost-core/serialization"] diff --git a/frost-ristretto255/Cargo.toml b/frost-ristretto255/Cargo.toml index c9e6254..98c6708 100644 --- a/frost-ristretto255/Cargo.toml +++ b/frost-ristretto255/Cargo.toml @@ -37,7 +37,7 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "std"] # No longer needed. Kept for retrocompatibility until 3.0.0 std = [] #! ## Features @@ -47,8 +47,6 @@ std = [] serde = ["frost-core/serde", "curve25519-dalek/serde"] ## Enable a default serialization format. Enables `serde`. serialization = ["serde", "frost-core/serialization", "frost-rerandomized/serialization"] -## Enable cheater detection -cheater-detection = ["frost-core/cheater-detection", "frost-rerandomized/cheater-detection"] [lib] # Disables non-criterion benchmark which is not used; prevents errors diff --git a/frost-secp256k1-tr/Cargo.toml b/frost-secp256k1-tr/Cargo.toml index c9227fb..9d116b3 100644 --- a/frost-secp256k1-tr/Cargo.toml +++ b/frost-secp256k1-tr/Cargo.toml @@ -37,7 +37,7 @@ secp256k1 = "0.31.0" serde_json.workspace = true [features] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "std"] # No longer needed. Kept for retrocompatibility until 3.0.0 std = [] #! ## Features @@ -47,8 +47,6 @@ std = [] serde = ["frost-core/serde"] ## Enable a default serialization format. Enables `serde`. serialization = ["serde", "frost-core/serialization", "frost-rerandomized/serialization"] -## Enable cheater detection -cheater-detection = ["frost-core/cheater-detection", "frost-rerandomized/cheater-detection"] [lib] # Disables non-criterion benchmark which is not used; prevents errors diff --git a/frost-secp256k1/Cargo.toml b/frost-secp256k1/Cargo.toml index 30e20d5..113a6c8 100644 --- a/frost-secp256k1/Cargo.toml +++ b/frost-secp256k1/Cargo.toml @@ -36,7 +36,7 @@ rand_chacha.workspace = true serde_json.workspace = true [features] -default = ["serialization", "cheater-detection", "std"] +default = ["serialization", "std"] # No longer needed. Kept for retrocompatibility until 3.0.0 std = [] #! ## Features @@ -46,8 +46,6 @@ std = [] serde = ["frost-core/serde"] ## Enable a default serialization format. Enables `serde`. serialization = ["serde", "frost-core/serialization", "frost-rerandomized/serialization"] -## Enable cheater detection -cheater-detection = ["frost-core/cheater-detection", "frost-rerandomized/cheater-detection"] [lib] # Disables non-criterion benchmark which is not used; prevents errors From 18d4ed14606a76524e48c5a4034b8f0127d0f62f Mon Sep 17 00:00:00 2001 From: Conrado Date: Tue, 9 Dec 2025 07:30:47 -0300 Subject: [PATCH 40/86] dependabot: update lockfile only (#959) --- .github/dependabot.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 75dc882..7c0e7e5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,6 +8,9 @@ updates: open-pull-requests-limit: 10 - package-ecosystem: cargo directory: "/" + # Update only the lockfile. We shouldn't update Cargo.toml unless it's for + # a security issue, or if we need a new feature of the dependency. + versioning-strategy: lockfile-only schedule: interval: monthly timezone: America/New_York From ca1df5f0c464d36e2047cd91e03ae27df0d167df Mon Sep 17 00:00:00 2001 From: Conrado Date: Tue, 9 Dec 2025 07:35:08 -0300 Subject: [PATCH 41/86] ci: test with latest versions of dependencies (#961) --- .github/workflows/main.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4db6557..edf48e5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,6 +18,15 @@ jobs: - uses: dtolnay/rust-toolchain@beta - run: cargo build + build_latest: + name: build with latest versions of dependencies + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6.0.0 + - uses: dtolnay/rust-toolchain@stable + - run: cargo update && cargo build --all-features + build_msrv: name: build with MSRV (1.81) runs-on: ubuntu-latest From ee450bda85e165dbe2e873546ae08ab0f855bafa Mon Sep 17 00:00:00 2001 From: Conrado Date: Thu, 18 Dec 2025 12:01:51 -0300 Subject: [PATCH 42/86] book: add devtool demo section (#968) --- book/src/SUMMARY.md | 1 + book/src/zcash/devtool-demo.md | 333 +++++++++++++++++++++++++++++++++ 2 files changed, 334 insertions(+) create mode 100644 book/src/zcash/devtool-demo.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 004ef64..1b2584b 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -12,6 +12,7 @@ - [Serialization Format](user/serialization.md) - [FROST with Zcash](zcash.md) - [Technical Details](zcash/technical-details.md) + - [zcash-devtool Demo](zcash/devtool-demo.md) - [Ywallet Demo](zcash/ywallet-demo.md) - [FROST Server](zcash/server.md) - [Terminology](terminology.md) diff --git a/book/src/zcash/devtool-demo.md b/book/src/zcash/devtool-demo.md new file mode 100644 index 0000000..e8e713a --- /dev/null +++ b/book/src/zcash/devtool-demo.md @@ -0,0 +1,333 @@ +# zcash-devtool Tutorial + +This tutorial explaining how to use FROST to sign a Zcash transaction using +[zcash-devtool](https://github.com/zcash/zcash-devtool). + + +## Setting up + +Install `cargo` and `git`. + +Install the `zcash-devtool`: + +``` +cargo install --git https://github.com/zcash/zcash-devtool.git --locked +``` + +Install the `frost-client` tool: + +``` +cargo install --git https://github.com/ZcashFoundation/frost-zcash-demo.git --locked frost-client +``` + +Install the `zcash-sign` tool: + +``` +cargo install --git https://github.com/ZcashFoundation/frost-zcash-demo.git --locked zcash-sign +``` + +Switch to an empty folder which will store the files generated in the demo. +For example: + +``` +mkdir frost-demo +cd frost-demo/ +``` + + +### Running the server + +This demo uses the ZF FROST server (frostd) to help participants communicate. +While in practice users would use an existing online server, for the demo you +can run a local server by following [these instructions](./server.md) (the +"Compiling, Running and Deploying" and "Local Testing" sections). + +The rest of the tutorial assumes the server is up and running. + + +### Initializing the users + +Run the following command to initialize three users (in practice, each user +would run a similar command, but for demo purposes we're assuming +you will simulate all of them in the same machine, so run these +commands in your machine): + +``` +frost-client init -c alice.toml +frost-client init -c bob.toml +frost-client init -c eve.toml +``` + +This will create a config file for three users; Alice, Bob and Eve. + +```admonish note +If you really want to run the demo in separate machines, then you can omit the +`-c alice.toml` part of the command (i.e. run `frost-client init`); it will +save to a default location in the user's home directory. +``` + + +## Generating FROST key shares + +First we will generate the FROST key shares. For simplicity we'll use trusted +dealer; if you want to use Distributed Key Generation, skip to the next section. + +In a new terminal (in case the previous terminal is running the server), run the +following: + +``` +frost-client trusted-dealer -d "Alice, Bob and Eve's group" --names Alice,Bob,Eve -c alice.toml -c bob.toml -c eve.toml -C redpallas +``` + +This will by default generate a 2-of-3 key shares. The key shares will be +written into each participant's config file. You can change the threhsold, +number of shares and file names using the command line; append `-h` to the +commend above for the command line help. + + +## Generating FROST key shares using DKG + +For real-word usage we commend generating key shares using Distributed Key +Generation. If you did the previous section, skip to "Generating the Full +Viewing Key for the wallet". + + +```admonish note +This section assumes each participant is running the commands in their own +machine. If you want to simulate all of them in a single machine, +specify the config file for the user (e.g. `-c alice.toml`) accordingly. +``` + + +### Initializing config files + +If they haven't yet, each participant should run: + +``` +frost-client init +``` + + +### Sharing contacts + +Each participant must now generate a contact string that they will need to share +with the other participants. This contact string will include a name, which they +can choose when exporting and will be shown to whoever they send the contact to. + +Run the following, substituting the name accordingly: + +``` +frost-client export --name 'Alice' +``` + +The command will print an encoded contact string such as +`zffrost1qyqq2stvd93k2g84hudcr98zp67a9rnx9v00euw9e5424hjathvre7ymy344fynjdvxmwxfg`. +Send it to the other participants using some trusted communication channel +(instant messaging, etc.). + +The other participants will send you their contacts. Import them by running the +following command for each contact (replace `` with the contact +string accordingly): + +``` +frost-client import +``` + + +### Generating shares + +Finally, to generate the shares, one of the participants will need to initiate +the process. They will need to public key of each participant, so they need to +first list them with the following command: + +``` +frost-client contacts +``` + +Then run the following command, replacing the `` and `` hex +strings with the public keys of the contacts which will participate (along with +the user running the command): + +``` +frost-client dkg -d "Alice, Bob and Eve's group" -s localhost:2744 -S , -t 2 -C redpallas -c alice.toml +``` + +The user should then notify the others that a signing session has started (e.g. +via instant messaging again), and also share the threshold number that was used. +They should then run the following, replacing the name of the group if they wish +and the threshold number with the one given by the first participant. + +``` +frost-client dkg -d "Alice, Bob and Eve's group" -s localhost:2744 -t 2 -C redpallas +``` + +```admonish note +A future version might not require specifying the threshold and group name. +``` + + +## Generating the Full Viewing Key for the wallet + +Next, we will need to generate a Zcash Full Viewing Key from the FROST group +material we have just generated; this address will then be imported into a wallet +so that we'll be able to create Zcash transactions for it. + +Run the following command: + +``` +frost-client groups +``` + +It will list all groups you're in - at this point it should list the only one +you have just created. Copy the Public Key it shows (it will look like e.g. +`79d6bcee79c88ad9ba259067772b97f5de12f1435b474d03bc98f255be08a610`) + +The run the following command, replacing `` with the value you copied, +and `test` with `main` if you're using Mainnet. + +``` +zcash-sign generate --net test --ak +``` + +It will print an Orchard address, and a Unified Full Viewing Key. Copy and +paste both somewhere to use them later. + + +## Importing the Full Viewing Key into zcash-devtool + +In the zcash-devtool folder, run the following, replacing `` with the +UFVK printed in the last step: + +``` +zcash-devtool wallet -w ./.frost.view/ init-fvk --name FROST_wallet --fvk --birthday 3720000 -s zecrocks +``` + +(Change `./.frost-view` or `FROST_wallet` if you want to change the folder or +name of the wallet.) + + +## Funding the wallet + +Now you will need to fund this wallet with some ZEC. Send ZEC to that address +using another account (or try [ZecFaucet](https://zecfaucet.com/)). + +## Creating the transaction + +Run the following, replacing `` with the address you want to ZEC to, +`` with the value you want to send in Zatoshis, `` with the +memo you want to send: + +``` +zcash-devtool pczt -w ./.frost.view/ create --address --value --memo > frost_pczt.created +``` + + +## Signing the transaction + +Now you will need to simulate two participants and a Coordinator to sign the +transaction, and you should still have the FROST server running which will +handle communications between them. It's probably easier to open three new +terminals. + +Go back to the signer terminal and run the following, replacing `` +with the path to the file you saved in the previous step, and `` +with the path where you want to write the signed transaction (e.g. +`frost_pczt.signed`). + +``` +zcash-sign sign -n test --tx-plan frost_pczt.created -o frost_pczt.signed +``` + +(Replace `test` with `main` if you're using Mainnet.) + +The program will print a SIGHASH and a Randomizer, and will prompt for a +signature. This is what you will get after running FROST, so let's do that; +leave the prompt there without typing anything. + + +### Coordinator + +In the second terminal, the Coordinator, run (in the same folder where you +initialized the users and ran the key generation) the following: + +``` +frost-client groups -c alice.toml +``` + +This will list the groups Alice is in; it should only list the one you created +earlier. You will need to copy some values in the command. Run the following, +replacing the value after `` with the "Public key" listed for the group; +replacing `` and `` with the public keys of Alice and Bob (the +hexadecimal values printed next to their names; Alice's name will be empty to +indicate it's her own). + +``` +frost-client coordinator -c alice.toml --server-url localhost:2744 --group -S , -m - -r - +``` + +It will prompt you for a message. Paste the SIGHASH generated with the +`zcash-sign` tool and press enter. It will then prompt for a randomizer. Paste +the one generated with the `zcash-sign` tool and press enter. + +The tool will connect to the server and wait for the other participants. + +```admonish warning +If you prefer to pass the message (SIGHASH) or randomizer as files by using +the `-m` and `-r` arguments, you will need to convert them to binary format. +``` + + +### Participant 1 (Alice) + +In the third terminal, Participant 1, run the following (replacing `` +with the same group public key used in the previous command): + +``` +frost-client participant -c alice.toml --server-url localhost:2744 --group +``` + +(We are using "Alice" again. There's nothing stopping a Coordinator from being a +Partcipant too!) + + +### Participant 2 (Bob) + +In the fourth terminal, for Participant 2, run the following (replacing `` +again): + +``` +frost-client participant -c bob.toml --server-url localhost:2744 --group +``` + + +### Coordinator + +Go back to the Coordinator CLI. The protocol should run and complete +successfully. It will print the final FROST-generated signature. Hurrah! Copy it +(just the hex value). + +Go back to the signer and paste the signature. It will write the signed +transaction to the file you specified. + +## Proving the transaction + +You will need to prove the transaction separately: + +``` +cargo run -p zcash-sign -- sign -n test --tx-plan frost_pczt.created -o frost_pczt.signed +``` + +And then combine signed and proven into a final transaction: + +``` +zcash-devtool pczt combine -i frost_pczt.signed -i frost_pczt.proven > frost_pczt.combined +``` + + +## Broadcasting the transaction + +Run: + +``` +zcash-devtool pczt -w ./.frost.view/ send -s zecrocks < test_pczt.combined +``` From 705acde7d07263dc63dad0f3adc04303d50aa26d Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 22 Dec 2025 09:21:24 -0300 Subject: [PATCH 43/86] core: fix unused warning due to compiler bug (#971) --- frost-core/src/keys.rs | 2 ++ frost-core/src/round1.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/frost-core/src/keys.rs b/frost-core/src/keys.rs index c9f02f2..7785452 100644 --- a/frost-core/src/keys.rs +++ b/frost-core/src/keys.rs @@ -1,5 +1,7 @@ //! FROST keys, keygen, key shares #![allow(clippy::type_complexity)] +// Remove after https://github.com/rust-lang/rust/issues/147648 is fixed +#![allow(unused_assignments)] use core::iter; diff --git a/frost-core/src/round1.rs b/frost-core/src/round1.rs index 88cdeac..7c3b10c 100644 --- a/frost-core/src/round1.rs +++ b/frost-core/src/round1.rs @@ -1,4 +1,6 @@ //! FROST Round 1 functionality and types +// Remove after https://github.com/rust-lang/rust/issues/147648 is fixed +#![allow(unused_assignments)] use alloc::{ collections::BTreeMap, From a454d0233fe2709c1cd2dd0f9f14713d63770cd2 Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 22 Dec 2025 09:21:26 -0300 Subject: [PATCH 44/86] docs: explain origin of identifiers in aggregate() (#956) --- book/src/tutorial/signing.md | 13 +++++++++++++ frost-core/src/lib.rs | 7 +++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/book/src/tutorial/signing.md b/book/src/tutorial/signing.md index 4a385c2..ecd249e 100644 --- a/book/src/tutorial/signing.md +++ b/book/src/tutorial/signing.md @@ -100,6 +100,19 @@ What should be done in that case is up to the application. The misbehaving parti could be excluded from future signing sessions, for example. ``` +```admonish danger +In `aggregate()` you need to provide a map from `Identifier` to +`SignatureShare`. If you need cheater detection, then it is important that these +identifiers come from a mapping between authenticated channels and identifiers; +i.e. you should not simply send the `Identifier` along with the +`SignatureShare`; otherwise the cheater could simply lie about their identifier. + +For example, if you authenticate the communication channels with TLS, then you +will need to create a public key -> identifier mapping, and use that mapping +to get the identifier for the connection where the `SignatureShare` was read +from. +``` + ## Verifying signatures diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index 2d6fb29..b5b1620 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -551,7 +551,8 @@ where /// [`round2::SignatureShare`] they sent. These identifiers must come from whatever mapping /// the coordinator has between communication channels and participants, i.e. /// they must have assurance that the [`round2::SignatureShare`] came from -/// the participant with that identifier. +/// the participant with that identifier. (This means that you *MUST NOT* send +/// the identifier along with the [`round2::SignatureShare`].) /// /// This operation is performed by a coordinator that can communicate with all /// the signing participants before publishing the final signature. The @@ -592,7 +593,9 @@ pub enum CheaterDetection { } /// Like [`aggregate()`], but allow specifying a specific cheater detection -/// strategy. +/// strategy. If you are disabling cheater detection, then the identifiers +/// in `signature_shares` do not need to correspond to the senders (i.e. +/// you don't need to authenticate the origin of the shares). pub fn aggregate_custom( signing_package: &SigningPackage, signature_shares: &BTreeMap, round2::SignatureShare>, From 4827fd28e866c7cc554a65b32a854cd871bfe94c Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 22 Dec 2025 09:49:30 -0300 Subject: [PATCH 45/86] core: fix `dkg::round2::SecretPackage` serialization (#937) * add test to reproduce the bug * core: remove identity of frost_core::keys::dkg::round2::SecretPackage before returning it; add it back when necessary * additional roundtrip test --- frost-core/src/keys/refresh.rs | 13 +++++++++ frost-core/src/tests/ciphersuite_generic.rs | 27 +++++++++++++++--- frost-core/src/tests/refresh.rs | 31 +++++++++++++++++---- 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index 3815aec..57691ea 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -290,6 +290,8 @@ pub fn refresh_dkg_part2( } let fii = evaluate_polynomial(secret_package.identifier, &secret_package.coefficients()); + // We remove the identity again to make it serializable + secret_package.commitment.0.remove(0); Ok(( round2::SecretPackage::new( secret_package.identifier, @@ -335,6 +337,17 @@ pub fn refresh_dkg_shares( return Err(Error::InvalidMinSigners); } + // Add identity commitment back into the round2_secret_package + let mut commitment = round2_secret_package.commitment.0.clone(); + commitment.insert(0, CoefficientCommitment::new(C::Group::identity())); + let round2_secret_package = round2::SecretPackage::new( + round2_secret_package.identifier, + VerifiableSecretSharingCommitment::::new(commitment), + round2_secret_package.secret_share.0, + round2_secret_package.min_signers, + round2_secret_package.max_signers, + ); + // Add identity commitment back into round1_packages let mut new_round_1_packages = BTreeMap::new(); for (sender_identifier, round1_package) in round1_packages { diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs index ce46213..86021b5 100644 --- a/frost-core/src/tests/ciphersuite_generic.rs +++ b/frost-core/src/tests/ciphersuite_generic.rs @@ -5,6 +5,7 @@ use alloc::{borrow::ToOwned, collections::BTreeMap, vec::Vec}; use rand_core::{CryptoRng, RngCore}; use crate as frost; +use crate::keys::dkg::{round1, round2}; use crate::keys::SigningShare; use crate::round2::SignatureShare; use crate::{ @@ -454,7 +455,12 @@ where // Store the participant's secret package for later use. // In practice each participant will store it in their own environment. - round1_secret_packages.insert(participant_identifier, round1_secret_package); + round1_secret_packages.insert( + participant_identifier, + // Serialization roundtrip to simulate storage for later + round1::SecretPackage::deserialize(&round1_secret_package.serialize().unwrap()) + .unwrap(), + ); // "Send" the round 1 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be @@ -469,7 +475,11 @@ where received_round1_packages .entry(receiver_participant_identifier) .or_default() - .insert(participant_identifier, round1_package.clone()); + .insert( + participant_identifier, + // Serialization roundtrip to simulate communication + round1::Package::deserialize(&round1_package.serialize().unwrap()).unwrap(), + ); } } @@ -501,7 +511,12 @@ where // Store the participant's secret package for later use. // In practice each participant will store it in their own environment. - round2_secret_packages.insert(participant_identifier, round2_secret_package); + round2_secret_packages.insert( + participant_identifier, + // Serialization roundtrip to simulate storage for later + round2::SecretPackage::deserialize(&round2_secret_package.serialize().unwrap()) + .unwrap(), + ); // "Send" the round 2 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be @@ -512,7 +527,11 @@ where received_round2_packages .entry(receiver_identifier) .or_insert_with(BTreeMap::new) - .insert(participant_identifier, round2_package); + .insert( + participant_identifier, + // Serialization roundtrip to simulate communication + round2::Package::deserialize(&round2_package.serialize().unwrap()).unwrap(), + ); } } diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index cd20ec0..83b67b5 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -2,6 +2,7 @@ use rand_core::{CryptoRng, RngCore}; +use crate::keys::dkg::{round1, round2}; use crate::keys::generate_with_dealer; use crate::keys::refresh::{ compute_refreshing_shares, refresh_dkg_part2, refresh_dkg_part_1, refresh_share, @@ -76,7 +77,9 @@ pub fn check_refresh_shares_with_dealer( for i in 0..remaining_ids.len() { let identifier = remaining_ids[i]; let current_share = &old_key_packages[&identifier]; - let new_share = refresh_share(zero_shares[i].clone(), current_share); + // Do a serialization roundtrip to simulate real usage + let zero_share = SecretShare::deserialize(&zero_shares[i].serialize().unwrap()).unwrap(); + let new_share = refresh_share(zero_share, current_share); new_shares.insert(identifier, new_share); } @@ -388,7 +391,12 @@ where // Store the participant's secret package for later use. // In practice each participant will store it in their own environment. - round1_secret_packages.insert(participant_identifier, round1_secret_package); + round1_secret_packages.insert( + participant_identifier, + // Serialization roundtrip to simulate storage for later + round1::SecretPackage::deserialize(&round1_secret_package.serialize().unwrap()) + .unwrap(), + ); // "Send" the round 1 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be @@ -400,7 +408,11 @@ where received_round1_packages .entry(receiver_participant_identifier) .or_default() - .insert(participant_identifier, round1_package.clone()); + .insert( + participant_identifier, + // Serialization roundtrip to simulate communication + round1::Package::deserialize(&round1_package.serialize().unwrap()).unwrap(), + ); } } @@ -429,7 +441,12 @@ where // Store the participant's secret package for later use. // In practice each participant will store it in their own environment. - round2_secret_packages.insert(participant_identifier, round2_secret_package); + round2_secret_packages.insert( + participant_identifier, + // Serialization roundtrip to simulate storage for later + round2::SecretPackage::deserialize(&round2_secret_package.serialize().unwrap()) + .unwrap(), + ); // "Send" the round 2 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be @@ -440,7 +457,11 @@ where received_round2_packages .entry(receiver_identifier) .or_insert_with(BTreeMap::new) - .insert(participant_identifier, round2_package); + .insert( + participant_identifier, + // Serialization roundtrip to simulate communication + round2::Package::deserialize(&round2_package.serialize().unwrap()).unwrap(), + ); } } From 85c16bc032a7c9c9d9b73e3263981b0b3dc5df02 Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 22 Dec 2025 09:53:12 -0300 Subject: [PATCH 46/86] core: expose NonceCommitment getter/new under internals (#947) --- frost-core/src/round1.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frost-core/src/round1.rs b/frost-core/src/round1.rs index 7c3b10c..33b7814 100644 --- a/frost-core/src/round1.rs +++ b/frost-core/src/round1.rs @@ -133,10 +133,15 @@ where C: Ciphersuite, { /// Create a new [`NonceCommitment`] from an [`Element`] + #[cfg_attr(feature = "internals", visibility::make(pub))] + #[cfg_attr(docsrs, doc(cfg(feature = "internals")))] pub(crate) fn new(value: Element) -> Self { Self(SerializableElement(value)) } + /// Get the inner [`Element`] of the [`NonceCommitment`] + #[cfg_attr(feature = "internals", visibility::make(pub))] + #[cfg_attr(docsrs, doc(cfg(feature = "internals")))] pub(crate) fn value(&self) -> Element { self.0 .0 } From 0f2116217ce3ab7591ffb33152674cb4389c20bf Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 22 Dec 2025 10:07:30 -0300 Subject: [PATCH 47/86] core: add pre-commitment computation hooks (#950) --- frost-core/src/lib.rs | 2 ++ frost-core/src/round2.rs | 2 ++ frost-core/src/traits.rs | 29 ++++++++++++++++++++++++++--- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index b5b1620..926c11c 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -631,7 +631,9 @@ where // binding factor. let binding_factor_list: BindingFactorList = compute_binding_factor_list(&signing_package, &pubkeys.verifying_key, &[])?; + // Compute the group commitment from signing commitments produced in round one. + let signing_package = ::pre_commitment_aggregate(&signing_package, &binding_factor_list)?; let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?; // The aggregation of the signature shares by summing them up, resulting in diff --git a/frost-core/src/round2.rs b/frost-core/src/round2.rs index 39c7cf4..b7a1e90 100644 --- a/frost-core/src/round2.rs +++ b/frost-core/src/round2.rs @@ -155,6 +155,8 @@ pub fn sign( .clone(); // Compute the group commitment from signing commitments produced in round one. + let (signing_package, signer_nonces) = + ::pre_commitment_sign(&signing_package, &signer_nonces, &binding_factor_list)?; let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?; // Compute Lagrange coefficient. diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs index c65d7d0..44c1c43 100644 --- a/frost-core/src/traits.rs +++ b/frost-core/src/traits.rs @@ -12,10 +12,10 @@ use crate::{ challenge, keys::{KeyPackage, PublicKeyPackage, SecretShare, VerifyingShare}, random_nonzero, - round1::{self}, + round1::{self, SigningNonces}, round2::{self, SignatureShare}, - BindingFactor, Challenge, Error, FieldError, GroupCommitment, GroupError, Identifier, - Signature, SigningKey, SigningPackage, VerifyingKey, + BindingFactor, BindingFactorList, Challenge, Error, FieldError, GroupCommitment, GroupError, + Identifier, Signature, SigningKey, SigningPackage, VerifyingKey, }; /// A prime order finite field GF(q) over which all scalar values for our prime order group can be @@ -337,6 +337,29 @@ pub trait Ciphersuite: Copy + PartialEq + Debug + 'static { )) } + /// Optional. Pre-process [`crate::compute_group_commitment()`] inputs in + /// [`round2::sign()`]. + #[allow(clippy::type_complexity)] + fn pre_commitment_sign<'a>( + signing_package: &'a SigningPackage, + signing_nonces: &'a SigningNonces, + _binding_factor_list: &'a BindingFactorList, + ) -> Result<(Cow<'a, SigningPackage>, Cow<'a, SigningNonces>), Error> { + Ok(( + Cow::Borrowed(signing_package), + Cow::Borrowed(signing_nonces), + )) + } + + /// Optional. Pre-process [`crate::compute_group_commitment()`] inputs in + /// [`crate::aggregate()`]. + fn pre_commitment_aggregate<'a>( + signing_package: &'a SigningPackage, + _binding_factor_list: &'a BindingFactorList, + ) -> Result>, Error> { + Ok(Cow::Borrowed(signing_package)) + } + /// Optional. Generate a nonce and a commitment to it. Used by /// [`SigningKey`] for regular (non-FROST) signing and internally by the DKG /// to generate proof-of-knowledge signatures. From 2bc191f0348d9b3361fffeb9711762b763c9b41f Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 22 Dec 2025 10:37:43 -0300 Subject: [PATCH 48/86] core: improve zeroization (#945) * core: improve zeroization * fix minimal-versions build * Update book/src/user/zeroization.md Co-authored-by: natalie --------- Co-authored-by: natalie --- Cargo.lock | 1 + book/src/SUMMARY.md | 1 + book/src/user/zeroization.md | 15 +++++++++++++++ frost-core/Cargo.toml | 5 ++++- frost-core/src/keys.rs | 6 +++--- frost-core/src/keys/dkg.rs | 34 +++++++++------------------------ frost-core/src/keys/refresh.rs | 2 +- frost-core/src/serialization.rs | 10 ++++++++++ 8 files changed, 44 insertions(+), 30 deletions(-) create mode 100644 book/src/user/zeroization.md diff --git a/Cargo.lock b/Cargo.lock index 2ca16f4..a2cff93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -549,6 +549,7 @@ dependencies = [ "thiserror", "visibility", "zeroize", + "zeroize_derive", ] [[package]] diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 1b2584b..4ffa00e 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -9,6 +9,7 @@ - [Distributed Key Generation](tutorial/dkg.md) - [Refreshing Shares](tutorial/refreshing-shares.md) - [User Documentation](user.md) + - [Zeroization](user/zeroization.md) - [Serialization Format](user/serialization.md) - [FROST with Zcash](zcash.md) - [Technical Details](zcash/technical-details.md) diff --git a/book/src/user/zeroization.md b/book/src/user/zeroization.md new file mode 100644 index 0000000..a8647fd --- /dev/null +++ b/book/src/user/zeroization.md @@ -0,0 +1,15 @@ +# Zeroization + +The ZF FROST crates have limited best-effort support at zeroization. The +top-level structs (`KeyPackage`, `SecretShare`, etc.) implement the +`Zeroize` and `ZeroizeOnDrop` from the `zeroize` crate. This means that +when they are dropped they are cleared from memory. + +However, be advised that the user is responsible for everything else. For +example, if you serialize the structs, then you will be responsible for +zeroizing the serialized buffers, which _will_ contain secrets. + +Additionally, if you extract the secret fields (e.g. `KeyPackage::signing_share()`) +they you are also responsible for zeroizing them if you make a copy, since +the inner types do not implement `ZeroizeOnDrop` (though most of them do +implement `Zeroize` so you can call `zeroize()` manually). diff --git a/frost-core/Cargo.toml b/frost-core/Cargo.toml index 9e7eb0c..ca03f33 100644 --- a/frost-core/Cargo.toml +++ b/frost-core/Cargo.toml @@ -28,7 +28,10 @@ serde = { version = "1.0.160", default-features = false, features = ["derive"], serdect = { version = "0.2.0", optional = true } thiserror = { version = "2.0.3", default-features = false } visibility = "0.1.0" -zeroize = { version = "1.5.4", default-features = false, features = ["derive"] } +zeroize = { version = "1.5.4", default-features = false, features = ["derive", "alloc"] } +# We indirectly depend on this via `zeroize` but the minimal version enforced by +# `zeroize` does not work for us, so we specify it here. +zeroize_derive = { version = "1.4.2" } itertools = { version = "0.14.0", default-features = false } # Test dependencies used with the test-impl feature diff --git a/frost-core/src/keys.rs b/frost-core/src/keys.rs index 7785452..7219143 100644 --- a/frost-core/src/keys.rs +++ b/frost-core/src/keys.rs @@ -17,7 +17,7 @@ use derive_getters::Getters; use hex::FromHex; use rand_core::{CryptoRng, RngCore}; -use zeroize::{DefaultIsZeroes, Zeroize}; +use zeroize::{DefaultIsZeroes, Zeroize, ZeroizeOnDrop}; use crate::{ serialization::{SerializableElement, SerializableScalar}, @@ -394,7 +394,7 @@ where /// /// To derive a FROST keypair, the receiver of the [`SecretShare`] *must* call /// .into(), which under the hood also performs validation. -#[derive(Clone, Debug, Zeroize, PartialEq, Eq, Getters)] +#[derive(Clone, Debug, Zeroize, PartialEq, Eq, Getters, ZeroizeOnDrop)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] @@ -622,7 +622,7 @@ fn evaluate_vss( /// When using a central dealer, [`SecretShare`]s are distributed to /// participants, who then perform verification, before deriving /// [`KeyPackage`]s, which they store to later use during signing. -#[derive(Clone, Debug, PartialEq, Eq, Getters, Zeroize)] +#[derive(Clone, Debug, PartialEq, Eq, Getters, Zeroize, ZeroizeOnDrop)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] diff --git a/frost-core/src/keys/dkg.rs b/frost-core/src/keys/dkg.rs index 0eac153..6f48c3e 100644 --- a/frost-core/src/keys/dkg.rs +++ b/frost-core/src/keys/dkg.rs @@ -54,7 +54,7 @@ use super::{ pub mod round1 { use alloc::vec::Vec; use derive_getters::Getters; - use zeroize::Zeroize; + use zeroize::{Zeroize, ZeroizeOnDrop}; use super::*; @@ -117,18 +117,20 @@ pub mod round1 { /// # Security /// /// This package MUST NOT be sent to other participants! - #[derive(Clone, PartialEq, Eq, Getters)] + #[derive(Clone, PartialEq, Eq, Getters, Zeroize, ZeroizeOnDrop)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] pub struct SecretPackage { /// The identifier of the participant holding the secret. + #[zeroize(skip)] pub(crate) identifier: Identifier, /// Coefficients of the temporary secret polynomial for the participant. /// These are (a_{i0}, ..., a_{i(t−1)})) which define the polynomial f_i(x) #[getter(skip)] pub(crate) coefficients: Vec>, /// The public commitment for the participant (C_i) + #[zeroize(skip)] pub(crate) commitment: VerifiableSecretSharingCommitment, /// The minimum number of signers. pub(crate) min_signers: u16, @@ -196,23 +198,12 @@ pub mod round1 { .finish() } } - - impl Zeroize for SecretPackage - where - C: Ciphersuite, - { - fn zeroize(&mut self) { - for c in self.coefficients.iter_mut() { - *c = SerializableScalar(<::Field>::zero()); - } - } - } } /// DKG Round 2 structures. pub mod round2 { use derive_getters::Getters; - use zeroize::Zeroize; + use zeroize::{Zeroize, ZeroizeOnDrop}; #[cfg(feature = "serialization")] use alloc::vec::Vec; @@ -275,14 +266,16 @@ pub mod round2 { /// # Security /// /// This package MUST NOT be sent to other participants! - #[derive(Clone, PartialEq, Eq, Getters)] + #[derive(Clone, PartialEq, Eq, Getters, Zeroize, ZeroizeOnDrop)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] pub struct SecretPackage { /// The identifier of the participant holding the secret. + #[zeroize(skip)] pub(crate) identifier: Identifier, /// The public commitment from the participant (C_i) + #[zeroize(skip)] pub(crate) commitment: VerifiableSecretSharingCommitment, /// The participant's own secret share (f_i(i)). #[getter(skip)] @@ -351,15 +344,6 @@ pub mod round2 { .finish() } } - - impl Zeroize for SecretPackage - where - C: Ciphersuite, - { - fn zeroize(&mut self) { - self.secret_share = SerializableScalar(<::Field>::zero()); - } - } } /// Performs the first part of the distributed key generation protocol @@ -542,7 +526,7 @@ pub fn part2( Ok(( round2::SecretPackage::new( secret_package.identifier, - secret_package.commitment, + secret_package.commitment.clone(), fii, secret_package.min_signers, secret_package.max_signers, diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index 57691ea..0749516 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -295,7 +295,7 @@ pub fn refresh_dkg_part2( Ok(( round2::SecretPackage::new( secret_package.identifier, - secret_package.commitment, + secret_package.commitment.clone(), fii, secret_package.min_signers, secret_package.max_signers, diff --git a/frost-core/src/serialization.rs b/frost-core/src/serialization.rs index 73195c1..5fc642f 100644 --- a/frost-core/src/serialization.rs +++ b/frost-core/src/serialization.rs @@ -1,6 +1,7 @@ //! Serialization support. use alloc::vec::Vec; +use zeroize::Zeroize; use crate::{Ciphersuite, FieldError}; @@ -34,6 +35,15 @@ where } } +impl Zeroize for SerializableScalar +where + C: Ciphersuite, +{ + fn zeroize(&mut self) { + self.0 = <::Field as Field>::zero(); + } +} + #[cfg(feature = "serde")] impl serde::Serialize for SerializableScalar where From 99aca96f5f467dff6d8eca59537d0985b0c43426 Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 22 Dec 2025 10:37:45 -0300 Subject: [PATCH 49/86] docs: add warning about secp256k1; clean up READMEs (#954) * docs: add warning about secp256k1; clean up READMEs * Update frost-rerandomized/README.md Co-authored-by: natalie * fix Agreement -> Generation --------- Co-authored-by: natalie --- README.md | 31 +++++++++++++++++++------------ book/src/index.md | 8 +------- book/src/tutorial.md | 6 ++++++ frost-core/README.md | 25 +++---------------------- frost-ed25519/README.md | 7 +++++++ frost-ed448/README.md | 7 +++++++ frost-p256/README.md | 7 +++++++ frost-rerandomized/README.md | 21 +++++++++------------ frost-ristretto255/README.md | 7 +++++++ frost-secp256k1-tr/README.md | 7 +++++++ frost-secp256k1/README.md | 7 +++++++ gencode/src/main.rs | 6 ++++++ performance.md | 2 +- 13 files changed, 87 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 019b60d..0294816 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ | secp256k1 ciphersuite (Taproot) | [`frost-secp256k1-tr`] | [![crates.io](https://img.shields.io/crates/v/frost-secp256k1-tr.svg)](https://crates.io/crates/frost-secp256k1-tr) | [![Documentation](https://docs.rs/frost-secp256k1-tr/badge.svg)](https://docs.rs/frost-secp256k1-tr) | | Generic Re-randomized FROST | [`frost-rerandomized`] | [![crates.io](https://img.shields.io/crates/v/frost-rerandomized.svg)](https://crates.io/crates/frost-rerandomized) | [![Documentation](https://docs.rs/frost-rerandomized/badge.svg)](https://docs.rs/frost-rerandomized) | -Rust implementations of ['Two-Round Threshold Schnorr Signatures with FROST'](https://datatracker.ietf.org/doc/draft-irtf-cfrg-frost/). +Rust implementations of ['RFC 9591: Two-Round Threshold Schnorr Signatures with FROST'](https://datatracker.ietf.org/doc/rfc9591/). Unlike signatures in a single-party setting, threshold signatures require cooperation among a threshold number of signers, each holding a share of a common private key. The security of threshold @@ -21,7 +21,7 @@ schemes in general assume that an adversary can corrupt strictly fewer than a th participants. ['Two-Round Threshold Schnorr Signatures with -FROST'](https://datatracker.ietf.org/doc/draft-irtf-cfrg-frost/) presents a variant of a Flexible +FROST'](https://datatracker.ietf.org/doc/rfc9591/) presents a variant of a Flexible Round-Optimized Schnorr Threshold (FROST) signature scheme originally defined in [FROST20](https://eprint.iacr.org/2020/852.pdf). FROST reduces network overhead during threshold signing operations while employing a novel technique to protect against forgery attacks applicable @@ -29,22 +29,29 @@ to prior Schnorr-based threshold signature constructions. Besides FROST itself, this repository also provides: -- Trusted dealer key generation as specified in the appendix of ['Two-Round Threshold Schnorr Signatures with FROST'](https://datatracker.ietf.org/doc/draft-irtf-cfrg-frost/); +- Trusted dealer key generation as specified in the appendix of ['Two-Round Threshold Schnorr Signatures with FROST'](https://datatracker.ietf.org/doc/rfc9591/); - Distributed key generation as specified in the original paper [FROST20](https://eprint.iacr.org/2020/852.pdf); - Repairable Threshold Scheme (RTS) from ['A Survey and Refinement of Repairable Threshold Schemes'](https://eprint.iacr.org/2017/1155) which allows a participant to recover a lost share with the help of a threshold of other participants; -- Rerandomized FROST (paper under review). -- Refresh Share functionality using a Trusted Dealer. This can be used to refresh the shares of the participants or to remove a participant. +- [Re-Randomized FROST](https://eprint.iacr.org/2024/436). +- Refresh Share functionality using a Trusted Dealer or Distributed Key + Generation. This can be used to refresh the shares of the participants or to + remove a participant. ## Getting Started -Refer to the [ZF FROST book](https://frost.zfnd.org/). +If you're not familiar with FROST, first read [Understanding FROST](frost.md). -## Status ⚠ +Then read the [Tutorial](tutorial.md), and use the [Rust docs](user.md) as +reference. -The FROST specification is not yet finalized, though no significant changes are -expected at this point. This code base has been partially audited by NCC, see -below for details. The APIs and types in the crates contained in this repository -follow SemVer guarantees. +## Status + +The crates are considered stable and feature complete, though eventual API +cleanups and additional functionality might be included in future releases. + +This code base has been partially audited by NCC, see below for details. The +APIs and types in the crates contained in this repository follow SemVer +guarantees. ### NCC Audit @@ -74,7 +81,7 @@ All issues identified in the audit were addressed by us and reviewed by NCC. `frost-core` implements the base traits and types in a generic manner, to enable top-level implementations for different ciphersuites / curves without having to implement all of FROST from -scratch. End-users should not use `frost-core` if they want to sign and verify signatures, they +scratch. End-users should not use `frost-core` if they want to just sign and verify signatures for a specific ciphersuite; they should use the crate specific to their ciphersuite/curve parameters that uses `frost-core` as a dependency. diff --git a/book/src/index.md b/book/src/index.md index 66fad0a..39ee232 100644 --- a/book/src/index.md +++ b/book/src/index.md @@ -2,10 +2,4 @@ This is a guide-level reference for the [ZF FROST library](https://github.com/ZcashFoundation/frost/). -## Getting Started - -If you're not familiar with FROST, first read [Understanding FROST](frost.md). - -Then read the [Tutorial](tutorial.md), and use the [Rust -docs](user.md) as -reference. \ No newline at end of file +{{#include ../../README.md}} \ No newline at end of file diff --git a/book/src/tutorial.md b/book/src/tutorial.md index 15df64c..15d0a25 100644 --- a/book/src/tutorial.md +++ b/book/src/tutorial.md @@ -15,3 +15,9 @@ If you need to support multiple ciphersuites then feel free to use This tutorial will use the `frost-ristretto255` crate, but changing to another ciphersuite should be a matter of simply changing the import. +```admonish note +"The `frost-secp256k1` crate is not compatible with Bitcoin BIP-340 (Taproot) +signatures. Use +[frost-secp256k1-tr](https://crates.io/crates/frost-secp256k1-tr) instead +if you want to support it. +``` diff --git a/frost-core/README.md b/frost-core/README.md index aac4cf2..e54be2f 100644 --- a/frost-core/README.md +++ b/frost-core/README.md @@ -1,30 +1,11 @@ # FROST (Flexible Round-Optimised Schnorr Threshold signatures) Core Base traits and types in Rust that implement ['Two-Round Threshold Schnorr Signatures with -FROST'](https://datatracker.ietf.org/doc/draft-irtf-cfrg-frost/) generically for +FROST'](https://datatracker.ietf.org/doc/rfc9591/) generically for [`Ciphersuite`] implementations. -For key generation, refer to the [`keys`] module. For round-specific -types and functions, refer to the [`round1`] and [`round2`] modules. This module -contains types and functions not directly related to key generation and the -FROST rounds. - - -## Status ⚠ - -The FROST specification is not yet finalized, though no significant changes are -expected at this point. This code base has been audited by NCC. The APIs and -types in `frost-core` are subject to change during the release candidate phase, -and will follow SemVer guarantees after 1.0.0. - -## Usage - -`frost-core` implements the base traits and types in a generic manner, to enable top-level -implementations for different ciphersuites / curves without having to implement all of FROST from -scratch. End-users should not use `frost-core` if they want to sign and verify signatures, they -should use the crate specific to their ciphersuite/curve parameters that uses `frost-core` as a -dependency, such as [`frost_ristretto255`](../frost_ristretto255). +For more details, refer to [The ZF FROST Book](https://frost.zfnd.org/). ## Example -See ciphersuite-specific crates, e.g. [`frost_ristretto255`](../frost_ristretto255). +See ciphersuite-specific crates, e.g. [`frost_ristretto255`]([../frost_ristretto255](https://crates.io/crates/frost-ristretto255)). diff --git a/frost-ed25519/README.md b/frost-ed25519/README.md index 0b9a2c9..500f8f4 100644 --- a/frost-ed25519/README.md +++ b/frost-ed25519/README.md @@ -1,6 +1,13 @@ An implementation of Schnorr signatures on the Ed25519 curve for both single and threshold numbers of signers (FROST). +This crate is a re-export of the ciphersuite-generic +[frost-core](https://crates.io/crates/frost-core) crate, parametrized with the +Ed25519 curve. For more details, refer to [The ZF FROST +Book](https://frost.zfnd.org/). + + + ## Example: key generation with trusted dealer and FROST signing Creating a key with a trusted dealer and splitting into shares; then signing a message diff --git a/frost-ed448/README.md b/frost-ed448/README.md index 34d4e53..77f1eb8 100644 --- a/frost-ed448/README.md +++ b/frost-ed448/README.md @@ -1,6 +1,13 @@ An implementation of Schnorr signatures on the Ed448 curve for both single and threshold numbers of signers (FROST). +This crate is a re-export of the ciphersuite-generic +[frost-core](https://crates.io/crates/frost-core) crate, parametrized with the +Ed448 curve. For more details, refer to [The ZF FROST +Book](https://frost.zfnd.org/). + + + ## Example: key generation with trusted dealer and FROST signing Creating a key with a trusted dealer and splitting into shares; then signing a message diff --git a/frost-p256/README.md b/frost-p256/README.md index ce0aa3d..47f391f 100644 --- a/frost-p256/README.md +++ b/frost-p256/README.md @@ -1,6 +1,13 @@ An implementation of Schnorr signatures on the P-256 curve for both single and threshold numbers of signers (FROST). +This crate is a re-export of the ciphersuite-generic +[frost-core](https://crates.io/crates/frost-core) crate, parametrized with the +P-256 curve. For more details, refer to [The ZF FROST +Book](https://frost.zfnd.org/). + + + ## Example: key generation with trusted dealer and FROST signing Creating a key with a trusted dealer and splitting into shares; then signing a message diff --git a/frost-rerandomized/README.md b/frost-rerandomized/README.md index 733b6e4..543f13b 100644 --- a/frost-rerandomized/README.md +++ b/frost-rerandomized/README.md @@ -1,22 +1,19 @@ # FROST (Flexible Round-Optimised Schnorr Threshold signatures) Rerandomized -Base traits and types in Rust that implement ['Two-Round Threshold Schnorr Signatures with -FROST'](https://datatracker.ietf.org/doc/draft-irtf-cfrg-frost/) generically for -`frost-core::Ciphersuite` implementations, with support for Zcash-compatible -RedDSA re-randomized signatures. - -## Status ⚠ - -The FROST specification is not yet finalized, and this codebase has not yet been audited or -released. The APIs and types in `frost-rerandomized` are subject to change. +A ciphersuite-generic implementation of [Re-Randomized +FROST](https://eprint.iacr.org/2024/436), which allows creating signatures using +FROST under re-randomized keys. ## Usage `frost-rerandomized` is similar to `frost-core`, but provides different `sign()` and `aggregate()` functions adding support for re-randomized signatures. -End-users should not use `frost-rerandomized` if they want to sign and verify signatures, they -should use the crate specific to their ciphersuite/curve parameters that uses `frost-rerandomized` as a -dependency, such as [`reddsa`](https://github.com/ZcashFoundation/reddsa/). + +Currently, the main ciphersuite crates do not re-expose the rerandomization +functions; if you want to use this functionality, you will need to use this +crate parametrized with the chosen ciphersuite. The exceptions are the Zcash +ciphersuites in [`reddsa`](https://github.com/ZcashFoundation/reddsa/) which +do expose the randomized functionality. ## Example diff --git a/frost-ristretto255/README.md b/frost-ristretto255/README.md index 4ba16fe..0f061bd 100644 --- a/frost-ristretto255/README.md +++ b/frost-ristretto255/README.md @@ -1,6 +1,13 @@ An implementation of Schnorr signatures on the Ristretto group for both single and threshold numbers of signers (FROST). +This crate is a re-export of the ciphersuite-generic +[frost-core](https://crates.io/crates/frost-core) crate, parametrized with the +Ristretto group. For more details, refer to [The ZF FROST +Book](https://frost.zfnd.org/). + + + ## Example: key generation with trusted dealer and FROST signing Creating a key with a trusted dealer and splitting into shares; then signing a message diff --git a/frost-secp256k1-tr/README.md b/frost-secp256k1-tr/README.md index 26e2870..bc74f3e 100644 --- a/frost-secp256k1-tr/README.md +++ b/frost-secp256k1-tr/README.md @@ -1,6 +1,13 @@ An implementation of Schnorr signatures on the secp256k1 curve (Taproot) for both single and threshold numbers of signers (FROST). +This crate is a re-export of the ciphersuite-generic +[frost-core](https://crates.io/crates/frost-core) crate, parametrized with the +secp256k1 curve (Taproot). For more details, refer to [The ZF FROST +Book](https://frost.zfnd.org/). + + + ## Example: key generation with trusted dealer and FROST signing Creating a key with a trusted dealer and splitting into shares; then signing a message diff --git a/frost-secp256k1/README.md b/frost-secp256k1/README.md index bdec1ac..c65e578 100644 --- a/frost-secp256k1/README.md +++ b/frost-secp256k1/README.md @@ -1,6 +1,13 @@ An implementation of Schnorr signatures on the secp256k1 curve for both single and threshold numbers of signers (FROST). +This crate is a re-export of the ciphersuite-generic +[frost-core](https://crates.io/crates/frost-core) crate, parametrized with the +secp256k1 curve. For more details, refer to [The ZF FROST +Book](https://frost.zfnd.org/). + +*This crate is not compatible with Bitcoin BIP-340 (Taproot) signatures. Use [frost-secp256k1-tr](https://crates.io/crates/frost-secp256k1-tr) instead* + ## Example: key generation with trusted dealer and FROST signing Creating a key with a trusted dealer and splitting into shares; then signing a message diff --git a/gencode/src/main.rs b/gencode/src/main.rs index 8a420fc..4d0e0a9 100644 --- a/gencode/src/main.rs +++ b/gencode/src/main.rs @@ -212,6 +212,7 @@ fn main() -> ExitCode { "ristretto255_sha512", "ristretto255", "", + "", ] .iter() .map(|x| x.to_string()) @@ -252,6 +253,7 @@ fn main() -> ExitCode { "p256_sha256", "p256", "

", + "", ], ), ( @@ -265,6 +267,7 @@ fn main() -> ExitCode { "ed25519_sha512", "ed25519", "", + "", ], ), ( @@ -278,6 +281,7 @@ fn main() -> ExitCode { "ed448_shake256", "ed448", "", + "", ], ), ( @@ -291,6 +295,7 @@ fn main() -> ExitCode { "secp256k1_sha256", "secp256k1", "", + "*This crate is not compatible with Bitcoin BIP-340 (Taproot) signatures. Use [frost-secp256k1-tr](https://crates.io/crates/frost-secp256k1-tr) instead*", ], ), ( @@ -304,6 +309,7 @@ fn main() -> ExitCode { "secp256k1_tr_sha256", "secp256k1_tr", "", + "", ], ), ] { diff --git a/performance.md b/performance.md index 6b21dd4..029632d 100644 --- a/performance.md +++ b/performance.md @@ -6,7 +6,7 @@ FROST is a threshold Schnorr signature scheme [invented](https://eprint.iacr.org/2020/852) by Chelsea Komlo (researcher at the Zcash Foundation) and Ian Goldberg, and in the process of becoming an [IETF -RFC](https://datatracker.ietf.org/doc/draft-irtf-cfrg-frost/). Threshold +RFC](https://datatracker.ietf.org/doc/rfc9591/). Threshold signatures allow a private key being split into shares given to multiple participants, allowing a subgroup of them (e.g. 3 out of 5, or whatever threshold specified at key generation) to generate a signature that can be From a473fc8c982dc6471f73372dd06de7b976e9d3c4 Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 22 Dec 2025 10:42:16 -0300 Subject: [PATCH 50/86] book: make it clear that auth channel is not needed for signing (#953) * book: make it clear that auth channel is not needed for signing * Update book/src/tutorial/signing.md Co-authored-by: natalie --------- Co-authored-by: natalie --- book/src/tutorial/signing.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/book/src/tutorial/signing.md b/book/src/tutorial/signing.md index ecd249e..18b120c 100644 --- a/book/src/tutorial/signing.md +++ b/book/src/tutorial/signing.md @@ -25,8 +25,13 @@ their commitments (a `SigningCommitments`) by calling ``` The `SigningNonces` must be kept by the participant to use in Round 2, while the -`SigningCommitments` must be sent to the Coordinator using an [authenticated -channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). +`SigningCommitments` must be sent to the Coordinator. + +```admonish info +FROST does not require using an [authenticated nor encrypted +channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel) +during the **signing** process. +``` ## Coordinator, Round 2 @@ -38,10 +43,9 @@ message to be signed, and then build a `SigningPackage` by calling {{#include ../../../frost-ristretto255/README.md:round2_package}} ``` -The `SigningPackage` must then be sent to all the participants using an -[authenticated -channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). (Of course, -if the message is confidential, then the channel must also be confidential.) +The `SigningPackage` must then be sent to all the participants. (If the message +is confidential, then the channel must also be confidential, since the message +is included in the `SigningPackage`.) ```admonish warning In all of the main FROST ciphersuites, the entire message must @@ -63,9 +67,7 @@ their `SigningNonces` from Round 1, by calling {{#include ../../../frost-ristretto255/README.md:round2_sign}} ``` -The resulting `SignatureShare` must then be sent back to the Coordinator using -an [authenticated -channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). +The resulting `SignatureShare` must then be sent back to the Coordinator. ```admonish important In most applications, it is important that the participant must be aware of what @@ -94,7 +96,8 @@ in the `SigningPackage` in Round 2 for the group verifying key in the `PublicKey FROST supports identifiable abort: if a participant misbehaves and produces an invalid signature share, then aggregation will fail and the returned error will have the identifier of the misbehaving participant. (If multiple participants -misbehave, only the first one detected will be returned.) +misbehave, only the first one detected will be returned. If you need to detect +all cheaters, use [`aggregate_custom()`](https://docs.rs/frost-ristretto255/latest/frost_ristretto255/fn.aggregate_custom.html)) What should be done in that case is up to the application. The misbehaving participant could be excluded from future signing sessions, for example. From 94cef54b9cd86b5b3b89a7b68400b829795c0779 Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 22 Dec 2025 11:07:15 -0300 Subject: [PATCH 51/86] core: bind traits to Send + Sync (#946) --- Cargo.lock | 34 +++++++ Cargo.toml | 3 + frost-core/Cargo.toml | 4 +- frost-core/src/tests/ciphersuite_generic.rs | 96 +++++++++++++++++-- frost-core/src/traits.rs | 10 +- frost-ed25519/Cargo.toml | 1 + frost-ed25519/tests/integration_tests.rs | 10 ++ frost-ed448/Cargo.toml | 1 + frost-ed448/tests/integration_tests.rs | 10 ++ frost-p256/Cargo.toml | 1 + frost-p256/tests/integration_tests.rs | 10 ++ frost-ristretto255/Cargo.toml | 1 + frost-ristretto255/tests/integration_tests.rs | 11 +++ frost-secp256k1-tr/Cargo.toml | 1 + frost-secp256k1-tr/tests/integration_tests.rs | 10 ++ frost-secp256k1/Cargo.toml | 1 + frost-secp256k1/tests/integration_tests.rs | 10 ++ gencode/src/main.rs | 7 ++ 18 files changed, 211 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2cff93..3b63dc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -547,6 +547,7 @@ dependencies = [ "serde_json", "serdect", "thiserror", + "tokio", "visibility", "zeroize", "zeroize_derive", @@ -571,6 +572,7 @@ dependencies = [ "rand_core 0.6.4", "serde_json", "sha2", + "tokio", ] [[package]] @@ -591,6 +593,7 @@ dependencies = [ "rand_core 0.6.4", "serde_json", "sha3", + "tokio", ] [[package]] @@ -611,6 +614,7 @@ dependencies = [ "rand_core 0.6.4", "serde_json", "sha2", + "tokio", ] [[package]] @@ -643,6 +647,7 @@ dependencies = [ "rand_core 0.6.4", "serde_json", "sha2", + "tokio", ] [[package]] @@ -663,6 +668,7 @@ dependencies = [ "rand_core 0.6.4", "serde_json", "sha2", + "tokio", ] [[package]] @@ -684,6 +690,7 @@ dependencies = [ "secp256k1", "serde_json", "sha2", + "tokio", ] [[package]] @@ -932,6 +939,12 @@ dependencies = [ "primeorder", ] +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "pkcs8" version = "0.10.2" @@ -1447,6 +1460,27 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "typenum" version = "1.19.0" diff --git a/Cargo.toml b/Cargo.toml index ae89d57..6b89170 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,8 @@ members = [ ] [workspace.package] +# If you update the edition, make sure to also update the argument to rustfmt +# in gencode/src/main.rs. edition = "2021" rust-version = "1.81" version = "2.2.0" @@ -37,6 +39,7 @@ rand = "0.8" rand_chacha = "0.3" rand_core = "0.6" serde_json = "1.0" +tokio = { version = "1.0", features = ["rt", "time", "macros"] } frost-core = { path = "frost-core", version = "2.2.0", default-features = false } frost-rerandomized = { path = "frost-rerandomized", version = "2.2.0", default-features = false } diff --git a/frost-core/Cargo.toml b/frost-core/Cargo.toml index ca03f33..7e3c5d3 100644 --- a/frost-core/Cargo.toml +++ b/frost-core/Cargo.toml @@ -38,6 +38,7 @@ itertools = { version = "0.14.0", default-features = false } proptest = { version = "1.0", optional = true } serde_json = { version = "1.0", optional = true } criterion = { workspace = true, optional = true } +tokio = { workspace = true, optional = true } [dev-dependencies] criterion.workspace = true @@ -47,6 +48,7 @@ rand.workspace = true rand_chacha.workspace = true serde_json.workspace = true + [features] default = ["serialization", "std"] # No longer needed. Kept for retrocompatibility until 3.0.0 @@ -62,7 +64,7 @@ internals = [] serde = ["dep:serde", "dep:serdect"] serialization = ["serde", "dep:postcard"] # Exposes ciphersuite-generic tests for other crates to use -test-impl = ["dep:proptest", "dep:serde_json", "dep:criterion"] +test-impl = ["dep:proptest", "dep:serde_json", "dep:criterion", "dep:tokio"] [lib] bench = false diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs index 86021b5..5b40e42 100644 --- a/frost-core/src/tests/ciphersuite_generic.rs +++ b/frost-core/src/tests/ciphersuite_generic.rs @@ -119,13 +119,11 @@ pub fn check_sign_with_dealer( .unwrap(); // Verifies the secret shares from the dealer - let mut key_packages: BTreeMap, frost::keys::KeyPackage> = - BTreeMap::new(); + let key_packages: BTreeMap, frost::keys::KeyPackage> = shares + .into_iter() + .map(|(k, v)| (k, frost::keys::KeyPackage::try_from(v).unwrap())) + .collect(); - for (k, v) in shares { - let key_package = frost::keys::KeyPackage::try_from(v).unwrap(); - key_packages.insert(k, key_package); - } // Check if it fails with not enough signers. Usually this would return an // error before even running the signing procedure, because `KeyPackage` // contains the correct `min_signers` value and the signing procedure checks @@ -1025,3 +1023,89 @@ fn check_verify_signature_share( .expect_err("should have failed"); } } + +/// Test FROST signing in an async context. +/// The ultimate goal of the test is to ensure that types are Send + Sync. +pub async fn async_check_sign( + mut rng: R, +) { + tokio::spawn(async move { + let max_signers = 5; + let min_signers = 3; + let (shares, pubkey_package) = frost::keys::generate_with_dealer( + max_signers, + min_signers, + frost::keys::IdentifierList::Default, + &mut rng, + ) + .unwrap(); + + // The test is sprinkled with await points to ensure that types that + // cross them are Send + Sync. + tokio::time::sleep(core::time::Duration::from_millis(1)).await; + + // Verifies the secret shares from the dealer + let key_packages: BTreeMap, frost::keys::KeyPackage> = shares + .into_iter() + .map(|(k, v)| (k, frost::keys::KeyPackage::try_from(v).unwrap())) + .collect(); + + tokio::time::sleep(core::time::Duration::from_millis(1)).await; + + let mut nonces_map: BTreeMap, frost::round1::SigningNonces> = + BTreeMap::new(); + let mut commitments_map: BTreeMap< + frost::Identifier, + frost::round1::SigningCommitments, + > = BTreeMap::new(); + + for participant_identifier in key_packages.keys().take(min_signers as usize).cloned() { + // Generate one (1) nonce and one SigningCommitments instance for each + // participant, up to _min_signers_. + let (nonces, commitments) = frost::round1::commit( + key_packages + .get(&participant_identifier) + .unwrap() + .signing_share(), + &mut rng, + ); + tokio::time::sleep(core::time::Duration::from_millis(1)).await; + nonces_map.insert(participant_identifier, nonces); + commitments_map.insert(participant_identifier, commitments); + } + + let mut signature_shares = BTreeMap::new(); + let message = "message to sign".as_bytes(); + let signing_package = SigningPackage::new(commitments_map, message); + + for participant_identifier in nonces_map.keys() { + let key_package = key_packages.get(participant_identifier).unwrap(); + let nonces_to_use = nonces_map.get(participant_identifier).unwrap(); + let signature_share = + frost::round2::sign(&signing_package, nonces_to_use, key_package).unwrap(); + tokio::time::sleep(core::time::Duration::from_millis(1)).await; + signature_shares.insert(*participant_identifier, signature_share); + } + + let group_signature = + frost::aggregate(&signing_package, &signature_shares, &pubkey_package).unwrap(); + tokio::time::sleep(core::time::Duration::from_millis(1)).await; + + pubkey_package + .verifying_key + .verify(message, &group_signature) + .unwrap(); + tokio::time::sleep(core::time::Duration::from_millis(1)).await; + + for (participant_identifier, _) in nonces_map.clone() { + let key_package = key_packages.get(&participant_identifier).unwrap(); + key_package + .verifying_key + .verify(message, &group_signature) + .unwrap(); + tokio::time::sleep(core::time::Duration::from_millis(1)).await; + } + }) + .await + .unwrap(); +} diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs index 44c1c43..321d64f 100644 --- a/frost-core/src/traits.rs +++ b/frost-core/src/traits.rs @@ -34,7 +34,9 @@ pub trait Field: Copy { + Eq + Mul + PartialEq - + Sub; + + Sub + + Send + + Sync; /// A unique byte array buf of fixed length N. type Serialization: Clone + AsRef<[u8]> + AsMut<[u8]> + for<'a> TryFrom<&'a [u8]> + Debug; @@ -97,7 +99,9 @@ pub trait Group: Copy + PartialEq { + Eq + Mul<::Scalar, Output = Self::Element> + PartialEq - + Sub; + + Sub + + Send + + Sync; /// A unique byte array buf of fixed length N. /// @@ -147,7 +151,7 @@ pub type Element = <::Group as Group>::Element; /// /// [FROST ciphersuite]: https://datatracker.ietf.org/doc/html/rfc9591#name-ciphersuites // See https://github.com/ZcashFoundation/frost/issues/693 for reasoning about the 'static bound. -pub trait Ciphersuite: Copy + PartialEq + Debug + 'static { +pub trait Ciphersuite: Copy + PartialEq + Debug + 'static + Send + Sync { /// The ciphersuite ID string. It should be equal to the contextString in /// the spec. For new ciphersuites, this should be a string that identifies /// the ciphersuite; it's recommended to use a similar format to the diff --git a/frost-ed25519/Cargo.toml b/frost-ed25519/Cargo.toml index eb456a1..e16cda6 100644 --- a/frost-ed25519/Cargo.toml +++ b/frost-ed25519/Cargo.toml @@ -35,6 +35,7 @@ proptest.workspace = true rand.workspace = true rand_chacha.workspace = true serde_json.workspace = true +tokio.workspace = true [features] default = ["serialization", "std"] diff --git a/frost-ed25519/tests/integration_tests.rs b/frost-ed25519/tests/integration_tests.rs index 830f645..649576c 100644 --- a/frost-ed25519/tests/integration_tests.rs +++ b/frost-ed25519/tests/integration_tests.rs @@ -379,3 +379,13 @@ fn check_sign_with_incorrect_commitments() { rng, ); } + +#[tokio::test] +async fn check_async_sign_with_dealer() { + tokio::spawn(async { + let rng = rand::rngs::OsRng; + frost_core::tests::ciphersuite_generic::async_check_sign::(rng).await; + }) + .await + .unwrap(); +} diff --git a/frost-ed448/Cargo.toml b/frost-ed448/Cargo.toml index 79a1d56..d9a0bed 100644 --- a/frost-ed448/Cargo.toml +++ b/frost-ed448/Cargo.toml @@ -34,6 +34,7 @@ proptest.workspace = true rand.workspace = true rand_chacha.workspace = true serde_json.workspace = true +tokio.workspace = true [features] default = ["serialization", "std"] diff --git a/frost-ed448/tests/integration_tests.rs b/frost-ed448/tests/integration_tests.rs index 0dfb791..601228d 100644 --- a/frost-ed448/tests/integration_tests.rs +++ b/frost-ed448/tests/integration_tests.rs @@ -379,3 +379,13 @@ fn check_sign_with_incorrect_commitments() { rng, ); } + +#[tokio::test] +async fn check_async_sign_with_dealer() { + tokio::spawn(async { + let rng = rand::rngs::OsRng; + frost_core::tests::ciphersuite_generic::async_check_sign::(rng).await; + }) + .await + .unwrap(); +} diff --git a/frost-p256/Cargo.toml b/frost-p256/Cargo.toml index 0299777..42c429b 100644 --- a/frost-p256/Cargo.toml +++ b/frost-p256/Cargo.toml @@ -34,6 +34,7 @@ proptest.workspace = true rand.workspace = true rand_chacha.workspace = true serde_json.workspace = true +tokio.workspace = true [features] default = ["serialization", "std"] diff --git a/frost-p256/tests/integration_tests.rs b/frost-p256/tests/integration_tests.rs index 868cc4d..1fd354b 100644 --- a/frost-p256/tests/integration_tests.rs +++ b/frost-p256/tests/integration_tests.rs @@ -376,3 +376,13 @@ fn check_sign_with_incorrect_commitments() { rng, ); } + +#[tokio::test] +async fn check_async_sign_with_dealer() { + tokio::spawn(async { + let rng = rand::rngs::OsRng; + frost_core::tests::ciphersuite_generic::async_check_sign::(rng).await; + }) + .await + .unwrap(); +} diff --git a/frost-ristretto255/Cargo.toml b/frost-ristretto255/Cargo.toml index 98c6708..d02077c 100644 --- a/frost-ristretto255/Cargo.toml +++ b/frost-ristretto255/Cargo.toml @@ -35,6 +35,7 @@ proptest.workspace = true rand.workspace = true rand_chacha.workspace = true serde_json.workspace = true +tokio.workspace = true [features] default = ["serialization", "std"] diff --git a/frost-ristretto255/tests/integration_tests.rs b/frost-ristretto255/tests/integration_tests.rs index 7179fab..d16f682 100644 --- a/frost-ristretto255/tests/integration_tests.rs +++ b/frost-ristretto255/tests/integration_tests.rs @@ -383,3 +383,14 @@ fn check_sign_with_incorrect_commitments() { _, >(rng); } + +#[tokio::test] +async fn check_async_sign_with_dealer() { + tokio::spawn(async { + let rng = rand::rngs::OsRng; + frost_core::tests::ciphersuite_generic::async_check_sign::(rng) + .await; + }) + .await + .unwrap(); +} diff --git a/frost-secp256k1-tr/Cargo.toml b/frost-secp256k1-tr/Cargo.toml index 9d116b3..55f83ee 100644 --- a/frost-secp256k1-tr/Cargo.toml +++ b/frost-secp256k1-tr/Cargo.toml @@ -35,6 +35,7 @@ rand.workspace = true rand_chacha.workspace = true secp256k1 = "0.31.0" serde_json.workspace = true +tokio.workspace = true [features] default = ["serialization", "std"] diff --git a/frost-secp256k1-tr/tests/integration_tests.rs b/frost-secp256k1-tr/tests/integration_tests.rs index 4933040..fe5007d 100644 --- a/frost-secp256k1-tr/tests/integration_tests.rs +++ b/frost-secp256k1-tr/tests/integration_tests.rs @@ -383,3 +383,13 @@ fn check_sign_with_incorrect_commitments() { _, >(rng); } + +#[tokio::test] +async fn check_async_sign_with_dealer() { + tokio::spawn(async { + let rng = rand::rngs::OsRng; + frost_core::tests::ciphersuite_generic::async_check_sign::(rng).await; + }) + .await + .unwrap(); +} diff --git a/frost-secp256k1/Cargo.toml b/frost-secp256k1/Cargo.toml index 113a6c8..ab20dab 100644 --- a/frost-secp256k1/Cargo.toml +++ b/frost-secp256k1/Cargo.toml @@ -34,6 +34,7 @@ proptest.workspace = true rand.workspace = true rand_chacha.workspace = true serde_json.workspace = true +tokio.workspace = true [features] default = ["serialization", "std"] diff --git a/frost-secp256k1/tests/integration_tests.rs b/frost-secp256k1/tests/integration_tests.rs index 0356b0a..63ba125 100644 --- a/frost-secp256k1/tests/integration_tests.rs +++ b/frost-secp256k1/tests/integration_tests.rs @@ -380,3 +380,13 @@ fn check_sign_with_incorrect_commitments() { _, >(rng); } + +#[tokio::test] +async fn check_async_sign_with_dealer() { + tokio::spawn(async { + let rng = rand::rngs::OsRng; + frost_core::tests::ciphersuite_generic::async_check_sign::(rng).await; + }) + .await + .unwrap(); +} diff --git a/gencode/src/main.rs b/gencode/src/main.rs index 4d0e0a9..5f92f38 100644 --- a/gencode/src/main.rs +++ b/gencode/src/main.rs @@ -167,6 +167,8 @@ fn copy_and_replace( pub fn rustfmt(source: String) -> String { let mut child = Command::new("rustfmt") + .arg("--edition") + .arg("2021") .stdin(Stdio::piped()) .stderr(Stdio::piped()) .stdout(Stdio::piped()) @@ -181,6 +183,11 @@ pub fn rustfmt(source: String) -> String { }); let output = child.wait_with_output().expect("Failed to read stdout"); + assert!( + output.status.success(), + "rustfmt failed: {}", + String::from_utf8_lossy(&output.stderr) + ); String::from_utf8_lossy(&output.stdout).to_string() } From 5a270164993055c61652d40ff303e1ccc34afded Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 22 Dec 2025 11:26:29 -0300 Subject: [PATCH 52/86] fix function name and ciphersuite refresh modules (#922) * fix function name and ciphersuite refresh modules * core: add min_signers to PublicKeyPackage (#912) * core: add min_signers to PublicKeyPackage * fix JSON deserialization when min_signers is missing * fix warnings, restore wrongly commented out line --- frost-core/CHANGELOG.md | 9 +- frost-core/src/keys.rs | 37 ++- frost-core/src/keys/dkg.rs | 2 +- frost-core/src/keys/refresh.rs | 14 +- frost-core/src/lib.rs | 10 +- frost-core/src/serialization.rs | 249 ++++++++++++++++++ frost-core/src/tests/ciphersuite_generic.rs | 14 +- frost-core/src/tests/refresh.rs | 44 ++-- frost-core/src/tests/vectors.rs | 3 +- frost-core/src/tests/vectors_dkg.rs | 2 + frost-core/src/traits.rs | 6 +- frost-ed25519/src/keys/refresh.rs | 10 +- frost-ed25519/tests/helpers/samples.rs | 14 +- frost-ed25519/tests/integration_tests.rs | 4 +- frost-ed25519/tests/recreation_tests.rs | 19 +- frost-ed25519/tests/serde_tests.rs | 20 +- frost-ed25519/tests/serialization_tests.rs | 11 + ...ey_package_new_postcard_serialization.snap | 5 + frost-ed448/src/keys/refresh.rs | 10 +- frost-ed448/tests/helpers/samples.rs | 14 +- frost-ed448/tests/integration_tests.rs | 4 +- frost-ed448/tests/recreation_tests.rs | 19 +- frost-ed448/tests/serde_tests.rs | 20 +- frost-ed448/tests/serialization_tests.rs | 11 + ...ey_package_new_postcard_serialization.snap | 5 + frost-p256/src/keys/refresh.rs | 10 +- frost-p256/tests/helpers/samples.rs | 14 +- frost-p256/tests/integration_tests.rs | 4 +- frost-p256/tests/recreation_tests.rs | 19 +- frost-p256/tests/serde_tests.rs | 20 +- frost-p256/tests/serialization_tests.rs | 11 + ...ey_package_new_postcard_serialization.snap | 5 + frost-rerandomized/src/lib.rs | 3 +- frost-ristretto255/src/keys/refresh.rs | 10 +- frost-ristretto255/tests/helpers/samples.rs | 14 +- frost-ristretto255/tests/integration_tests.rs | 4 +- frost-ristretto255/tests/recreation_tests.rs | 19 +- frost-ristretto255/tests/serde_tests.rs | 20 +- .../tests/serialization_tests.rs | 11 + ...ey_package_new_postcard_serialization.snap | 5 + frost-secp256k1-tr/src/keys/refresh.rs | 10 +- frost-secp256k1-tr/src/lib.rs | 8 +- frost-secp256k1-tr/tests/helpers/samples.rs | 14 +- frost-secp256k1-tr/tests/integration_tests.rs | 4 +- frost-secp256k1-tr/tests/recreation_tests.rs | 19 +- frost-secp256k1-tr/tests/serde_tests.rs | 20 +- .../tests/serialization_tests.rs | 11 + ...ey_package_new_postcard_serialization.snap | 5 + frost-secp256k1/src/keys/refresh.rs | 10 +- frost-secp256k1/tests/helpers/samples.rs | 14 +- frost-secp256k1/tests/integration_tests.rs | 4 +- frost-secp256k1/tests/recreation_tests.rs | 19 +- frost-secp256k1/tests/serde_tests.rs | 20 +- frost-secp256k1/tests/serialization_tests.rs | 11 + ...ey_package_new_postcard_serialization.snap | 5 + 55 files changed, 785 insertions(+), 114 deletions(-) create mode 100644 frost-ed25519/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap create mode 100644 frost-ed448/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap create mode 100644 frost-p256/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap create mode 100644 frost-ristretto255/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap create mode 100644 frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap create mode 100644 frost-secp256k1/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index 80d2fc8..ca1fbfb 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -2,7 +2,7 @@ Entries are listed in reverse chronological order. -## Unreleases +## Unreleased ### Breaking Changes @@ -12,6 +12,13 @@ Entries are listed in reverse chronological order. the default behaviour is now as if `cheater-detection` was enabled. If you explicitly *did not enable* it, you can avoid cheater detection by calling `aggregate_custom()` with `CheaterDetection::Disabled`. +* The `std` and `nightly` features were removed from all crates +* Renamed `frost_core::keys::refresh::refresh_dkg_part_1` to `refresh_dkg_part1`. +* Fixed the crate-specific versions of the `refresh` module to be non-generic. + +### Additional changes + +* Added DKG refresh functions to the crate-specific `refresh` modules. ## 2.2.0 diff --git a/frost-core/src/keys.rs b/frost-core/src/keys.rs index 7219143..cc6fe7a 100644 --- a/frost-core/src/keys.rs +++ b/frost-core/src/keys.rs @@ -381,6 +381,11 @@ where pub(crate) fn coefficients(&self) -> &[CoefficientCommitment] { &self.0 } + + /// Return the threshold associated with this commitment. + pub(crate) fn min_signers(&self) -> u16 { + self.0.len() as u16 + } } /// A secret share generated by performing a (t-out-of-n) secret sharing scheme, @@ -563,6 +568,7 @@ pub fn split( header: Header::default(), verifying_shares, verifying_key, + min_signers: Some(min_signers), }; // Apply post-processing @@ -706,7 +712,7 @@ where signing_share: secret_share.signing_share, verifying_share, verifying_key, - min_signers: secret_share.commitment.0.len() as u16, + min_signers: secret_share.commitment.min_signers(), }) } } @@ -716,7 +722,7 @@ where /// /// Used for verification purposes before publishing a signature. #[derive(Clone, Debug, PartialEq, Eq, Getters)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] pub struct PublicKeyPackage { @@ -728,6 +734,12 @@ pub struct PublicKeyPackage { pub(crate) verifying_shares: BTreeMap, VerifyingShare>, /// The joint public key for the entire group. pub(crate) verifying_key: VerifyingKey, + /// The minimum number of signers (threshold) required for the group. + /// This can be None in packages created with `frost_core` prior to 3.0.0. + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + #[cfg_attr(feature = "serde", serde(default))] + #[getter(copy)] + pub(crate) min_signers: Option, } impl PublicKeyPackage @@ -738,11 +750,26 @@ where pub fn new( verifying_shares: BTreeMap, VerifyingShare>, verifying_key: VerifyingKey, + min_signers: u16, + ) -> Self { + Self::new_internal(verifying_shares, verifying_key, Some(min_signers)) + } + + /// Create a new [`PublicKeyPackage`] instance, allowing not specifying the + /// number of signers. This is used internally, in particular for testing + /// old [`PublicKeyPackage`]s that do not encode the `min_signers`. + #[cfg_attr(feature = "internals", visibility::make(pub))] + #[cfg_attr(docsrs, doc(cfg(feature = "internals")))] + pub(crate) fn new_internal( + verifying_shares: BTreeMap, VerifyingShare>, + verifying_key: VerifyingKey, + min_signers: Option, ) -> Self { Self { header: Header::default(), verifying_shares, verifying_key, + min_signers, } } @@ -761,6 +788,7 @@ where Ok(PublicKeyPackage::new( verifying_keys, VerifyingKey::from_commitment(commitment)?, + commitment.min_signers(), )) } @@ -777,6 +805,11 @@ where let group_commitment = sum_commitments(&commitments)?; Self::from_commitment(&identifiers, &group_commitment) } + + /// Return the maximum number of signers. + pub fn max_signers(&self) -> u16 { + self.verifying_shares.len() as u16 + } } #[cfg(feature = "serialization")] diff --git a/frost-core/src/keys/dkg.rs b/frost-core/src/keys/dkg.rs index 6f48c3e..5f56256 100644 --- a/frost-core/src/keys/dkg.rs +++ b/frost-core/src/keys/dkg.rs @@ -491,7 +491,7 @@ pub fn part2( } for package in round1_packages.values() { - if package.commitment.0.len() != secret_package.min_signers as usize { + if package.commitment.min_signers() != secret_package.min_signers { return Err(Error::IncorrectNumberOfCommitments); } } diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index 0749516..4df4e6a 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -18,7 +18,7 @@ //! //! For the DKG approach, the flow is very similar to [DKG //! itself](`https://frost.zfnd.org/tutorial/dkg.html`). Each participant calls -//! [`refresh_dkg_part_1()`], keeps the returned secret package and sends the +//! [`refresh_dkg_part1()`], keeps the returned secret package and sends the //! returned package to other participants. Then each participants calls //! [`refresh_dkg_part2()`] and sends the returned packages to the other //! participants. Finally each participant calls [`refresh_dkg_shares()`]. @@ -61,6 +61,14 @@ pub fn compute_refreshing_shares( identifiers: &[Identifier], rng: &mut R, ) -> Result<(Vec>, PublicKeyPackage), Error> { + // Validate min_signers. It's OK if the min_signers is missing, because + // we validate it again in `refresh_share()`. + if let Some(package_min_signers) = pub_key_package.min_signers { + if min_signers != package_min_signers { + return Err(Error::InvalidMinSigners); + } + } + // Validate inputs if identifiers.len() != max_signers as usize { return Err(Error::IncorrectNumberOfIdentifiers); @@ -110,6 +118,7 @@ pub fn compute_refreshing_shares( header: pub_key_package.header, verifying_shares: refreshed_verifying_shares, verifying_key: pub_key_package.verifying_key, + min_signers: Some(pub_key_package.min_signers.unwrap_or(min_signers)), }; Ok((refreshing_shares_minus_identity, refreshed_pub_key_package)) @@ -168,7 +177,7 @@ pub fn refresh_share( /// It returns the [`round1::SecretPackage`] that must be kept in memory /// by the participant for the other steps, and the [`round1::Package`] that /// must be sent to each other participant in the refresh run. -pub fn refresh_dkg_part_1( +pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, @@ -460,6 +469,7 @@ pub fn refresh_dkg_shares( header: old_pub_key_package.header, verifying_shares: new_verifying_shares, verifying_key: old_pub_key_package.verifying_key, + min_signers: Some(round2_secret_package.min_signers), }; let key_package = KeyPackage { diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index 926c11c..8c1b54f 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -758,7 +758,15 @@ pub fn verify_signature_share( // In order to reuse `pre_aggregate()`, we need to create some "dummy" containers let signature_shares = BTreeMap::from([(identifier, *signature_share)]); let verifying_shares = BTreeMap::from([(identifier, *verifying_share)]); - let public_key_package = PublicKeyPackage::new(verifying_shares, *verifying_key); + let public_key_package = PublicKeyPackage { + verifying_shares, + verifying_key: *verifying_key, + // Use None since we don't have the min_signers value here. This + // can only cause problems if the `pre_aggregate` function relies on it. + // This has been documented in `pre_aggregate()`. + min_signers: None, + header: Header::default(), + }; let (signing_package, signature_shares, pubkeys) = ::pre_aggregate(signing_package, &signature_shares, &public_key_package)?; diff --git a/frost-core/src/serialization.rs b/frost-core/src/serialization.rs index 5fc642f..01ca843 100644 --- a/frost-core/src/serialization.rs +++ b/frost-core/src/serialization.rs @@ -1,9 +1,22 @@ //! Serialization support. +#[cfg(feature = "serde")] +use alloc::collections::BTreeMap; +use alloc::string::String; use alloc::vec::Vec; +#[cfg(feature = "serde")] +use core::fmt::Formatter; +#[cfg(feature = "serde")] +use core::marker::PhantomData; use zeroize::Zeroize; +#[cfg(feature = "serde")] +use crate::keys::PublicKeyPackage; +#[cfg(feature = "serde")] +use crate::keys::VerifyingShare; use crate::{Ciphersuite, FieldError}; +#[cfg(feature = "serde")] +use crate::{Header, Identifier, VerifyingKey}; use crate::{Element, Error, Field, Group}; @@ -240,3 +253,239 @@ impl serde::Deserialize<'de>, C: Ciphersuite> Deserialize for T { postcard::from_bytes(bytes).map_err(|_| Error::DeserializationError) } } + +/// Custom deserializer for PublicKeyPackage, which allows a non-existing +/// `min_signers` field for the `postcard` encoding. +#[cfg(feature = "serde")] +impl<'de, C: Ciphersuite> serde::Deserialize<'de> for PublicKeyPackage +where + C: Ciphersuite, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use core::fmt; + + // The following are copied from the `serde::Deserialize` derive, and + // are required to support `visit_map()` which in turn is required for + // `serde_json`. + + enum Field { + Field0, + Field1, + Field2, + Field3, + } + + struct FieldVisitor; + + impl<'de> serde::de::Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting(&self, __formatter: &mut Formatter) -> fmt::Result { + Formatter::write_str(__formatter, "field identifier") + } + + fn visit_u64<__E>(self, __value: u64) -> Result + where + __E: serde::de::Error, + { + match __value { + 0u64 => Ok(Field::Field0), + 1u64 => Ok(Field::Field1), + 2u64 => Ok(Field::Field2), + 3u64 => Ok(Field::Field3), + _ => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Unsigned(__value), + &"field index 0 <= i < 4", + )), + } + } + + fn visit_str<__E>(self, __value: &str) -> Result + where + __E: serde::de::Error, + { + match __value { + "header" => Ok(Field::Field0), + "verifying_shares" => Ok(Field::Field1), + "verifying_key" => Ok(Field::Field2), + "min_signers" => Ok(Field::Field3), + _ => Err(serde::de::Error::unknown_field(__value, FIELDS)), + } + } + + fn visit_bytes<__E>(self, __value: &[u8]) -> Result + where + __E: serde::de::Error, + { + match __value { + b"header" => Ok(Field::Field0), + b"verifying_shares" => Ok(Field::Field1), + b"verifying_key" => Ok(Field::Field2), + b"min_signers" => Ok(Field::Field3), + _ => { + let __value = &String::from_utf8_lossy(__value); + Err(serde::de::Error::unknown_field(__value, FIELDS)) + } + } + } + } + + impl<'de> serde::Deserialize<'de> for Field { + #[inline] + fn deserialize<__D>(__deserializer: __D) -> Result + where + __D: serde::Deserializer<'de>, + { + serde::Deserializer::deserialize_identifier(__deserializer, FieldVisitor) + } + } + + struct Visitor { + marker: PhantomData, + } + + impl<'de, C: Ciphersuite> serde::de::Visitor<'de> for Visitor + where + C: Ciphersuite, + { + type Value = PublicKeyPackage; + + fn expecting(&self, fmt: &mut Formatter) -> core::fmt::Result { + Formatter::write_str(fmt, "struct PublicKeyPackage") + } + + // Postcard serializes structs as sequences, so we override + // `visit_seq` to deserialize the struct from a sequence of elements. + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + // Read the first three fields as usual. + + let header = seq.next_element::>()?.ok_or_else(|| { + serde::de::Error::invalid_length( + 0usize, + &"struct PublicKeyPackage with 4 elements", + ) + })?; + let verifying_shares = seq + .next_element::, VerifyingShare>>()? + .ok_or_else(|| { + serde::de::Error::invalid_length( + 1usize, + &"struct PublicKeyPackage with 4 elements", + ) + })?; + let verifying_key = seq.next_element::>()?.ok_or_else(|| { + serde::de::Error::invalid_length( + 2usize, + &"struct PublicKeyPackage with 4 elements", + ) + })?; + + // For the `min_signers` field, fill it with None if + // `next_element()` fails (i.e. there are no other elements) + let min_signers = match seq.next_element::>() { + Ok(Some(min_signers)) => min_signers, + _ => None, + }; + + Ok(PublicKeyPackage { + header, + verifying_shares, + verifying_key, + min_signers, + }) + } + + // Again this is copied from the `serde::Deserialize` derive; + // the only change is not requiring `min_signers` to be present. + fn visit_map<__A>(self, mut __map: __A) -> Result + where + __A: serde::de::MapAccess<'de>, + { + let mut __field0: Option> = None; + let mut __field1: Option, VerifyingShare>> = None; + let mut __field2: Option> = None; + let mut __field3: Option> = None; + while let Some(__key) = serde::de::MapAccess::next_key::(&mut __map)? { + match __key { + Field::Field0 => { + if Option::is_some(&__field0) { + return Err(<__A::Error as serde::de::Error>::duplicate_field( + "header", + )); + } + __field0 = + Some(serde::de::MapAccess::next_value::>(&mut __map)?); + } + Field::Field1 => { + if Option::is_some(&__field1) { + return Err(<__A::Error as serde::de::Error>::duplicate_field( + "verifying_shares", + )); + } + __field1 = Some(serde::de::MapAccess::next_value::< + BTreeMap, VerifyingShare>, + >(&mut __map)?); + } + Field::Field2 => { + if Option::is_some(&__field2) { + return Err(<__A::Error as serde::de::Error>::duplicate_field( + "verifying_key", + )); + } + __field2 = Some(serde::de::MapAccess::next_value::>( + &mut __map, + )?); + } + Field::Field3 => { + if Option::is_some(&__field3) { + return Err(<__A::Error as serde::de::Error>::duplicate_field( + "min_signers", + )); + } + __field3 = + Some(serde::de::MapAccess::next_value::>(&mut __map)?); + } + } + } + let __field0 = match __field0 { + Some(__field0) => __field0, + None => Err(<__A::Error as serde::de::Error>::missing_field("header"))?, + }; + let __field1 = match __field1 { + Some(__field1) => __field1, + None => Err(<__A::Error as serde::de::Error>::missing_field( + "verifying_shares", + ))?, + }; + let __field2 = match __field2 { + Some(__field2) => __field2, + None => Err(<__A::Error as serde::de::Error>::missing_field( + "verifying_key", + ))?, + }; + let __field3 = __field3.unwrap_or_default(); + Ok(PublicKeyPackage { + header: __field0, + verifying_shares: __field1, + verifying_key: __field2, + min_signers: __field3, + }) + } + } + + const FIELDS: &[&str] = &["header", "verifying_shares", "verifying_key", "min_signers"]; + deserializer.deserialize_struct( + "PublicKeyPackage", + FIELDS, + Visitor { + marker: PhantomData::, + }, + ) + } +} diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs index 5b40e42..a2c0df8 100644 --- a/frost-core/src/tests/ciphersuite_generic.rs +++ b/frost-core/src/tests/ciphersuite_generic.rs @@ -542,9 +542,9 @@ where // will have all the participant's packages. let mut key_packages = BTreeMap::new(); - // Map of the verifying key of each participant. + // Map of the verifying share of each participant. // Used by the signing test that follows. - let mut verifying_keys = BTreeMap::new(); + let mut verifying_shares = BTreeMap::new(); // The group public key, used by the signing test that follows. let mut verifying_key = None; // For each participant, store the set of verifying keys they have computed. @@ -572,7 +572,7 @@ where &received_round2_packages[&participant_identifier], ) .unwrap(); - verifying_keys.insert(participant_identifier, key_package.verifying_share); + verifying_shares.insert(participant_identifier, key_package.verifying_share); // Test if all verifying_key are equal if let Some(previous_verifying_key) = verifying_key { assert_eq!(previous_verifying_key, key_package.verifying_key) @@ -585,10 +585,14 @@ where // Test if the set of verifying keys is correct for all participants. for verifying_keys_for_participant in pubkey_packages_by_participant.values() { - assert!(verifying_keys_for_participant.verifying_shares == verifying_keys); + assert!(verifying_keys_for_participant.verifying_shares == verifying_shares); } - let pubkeys = frost::keys::PublicKeyPackage::new(verifying_keys, verifying_key.unwrap()); + let pubkeys = pubkey_packages_by_participant + .first_key_value() + .unwrap() + .1 + .clone(); // Proceed with the signing test. check_sign(min_signers, key_packages, rng, pubkeys).unwrap() diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index 83b67b5..f9b58db 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -5,7 +5,7 @@ use rand_core::{CryptoRng, RngCore}; use crate::keys::dkg::{round1, round2}; use crate::keys::generate_with_dealer; use crate::keys::refresh::{ - compute_refreshing_shares, refresh_dkg_part2, refresh_dkg_part_1, refresh_share, + compute_refreshing_shares, refresh_dkg_part1, refresh_dkg_part2, refresh_share, }; #[cfg(feature = "serialization")] use crate::keys::{PublicKeyPackage, SecretShare}; @@ -296,33 +296,15 @@ pub fn check_refresh_shares_with_dealer_fails_with_different_min_signers< // Trusted Dealer generates zero keys and new public key package - let (zero_shares, _new_pub_key_package) = compute_refreshing_shares( + let r = compute_refreshing_shares( pub_key_package, NEW_MAX_SIGNERS, NEW_MIN_SIGNERS, &remaining_ids, &mut rng, - ) - .unwrap(); - - // Each participant refreshes their share - - let mut new_shares = BTreeMap::new(); - - for i in 0..remaining_ids.len() { - let identifier = remaining_ids[i]; - let current_share = &old_key_packages[&identifier]; - let new_share = refresh_share(zero_shares[i].clone(), current_share); - new_shares.insert(identifier, new_share); - } - - assert!( - new_shares - .iter() - .all(|(_, v)| v.is_err() && matches!(v, Err(Error::InvalidMinSigners))), - "{:?}", - new_shares ); + + assert_eq!(r, Err(Error::InvalidMinSigners)); } /// Test FROST signing with DKG with a Ciphersuite. @@ -387,7 +369,7 @@ where // In practice, each participant will perform this on their own environments. for participant_identifier in remaining_ids.clone() { let (round1_secret_package, round1_package) = - refresh_dkg_part_1(participant_identifier, max_signers, min_signers, &mut rng).unwrap(); + refresh_dkg_part1(participant_identifier, max_signers, min_signers, &mut rng).unwrap(); // Store the participant's secret package for later use. // In practice each participant will store it in their own environment. @@ -474,9 +456,9 @@ where // will have all the participant's packages. let mut key_packages = BTreeMap::new(); - // Map of the verifying key of each participant. + // Map of the verifying share of each participant. // Used by the signing test that follows. - let mut verifying_keys = BTreeMap::new(); + let mut verifying_shares = BTreeMap::new(); // The group public key, used by the signing test that follows. let mut verifying_key = None; // For each participant, store the set of verifying keys they have computed. @@ -506,7 +488,7 @@ where old_key_packages[&participant_identifier].clone(), ) .unwrap(); - verifying_keys.insert(participant_identifier, key_package.verifying_share); + verifying_shares.insert(participant_identifier, key_package.verifying_share); // Test if all verifying_key are equal if let Some(previous_verifying_key) = verifying_key { assert_eq!(previous_verifying_key, key_package.verifying_key) @@ -519,10 +501,14 @@ where // Test if the set of verifying keys is correct for all participants. for verifying_keys_for_participant in pubkey_packages_by_participant.values() { - assert!(verifying_keys_for_participant.verifying_shares == verifying_keys); + assert!(verifying_keys_for_participant.verifying_shares == verifying_shares); } - let pubkeys = frost::keys::PublicKeyPackage::new(verifying_keys, verifying_key.unwrap()); + let pubkeys = pubkey_packages_by_participant + .first_key_value() + .unwrap() + .1 + .clone(); // Proceed with the signing test. check_sign(min_signers, key_packages, rng, pubkeys).unwrap() @@ -594,7 +580,7 @@ pub fn check_refresh_shares_with_dkg_smaller_threshold< // In practice, each participant will perform this on their own environments. for participant_identifier in remaining_ids.clone() { let (round1_secret_package, round1_package) = - refresh_dkg_part_1(participant_identifier, max_signers, min_signers, &mut rng).unwrap(); + refresh_dkg_part1(participant_identifier, max_signers, min_signers, &mut rng).unwrap(); // Store the participant's secret package for later use. // In practice each participant will store it in their own environment. diff --git a/frost-core/src/tests/vectors.rs b/frost-core/src/tests/vectors.rs index 950fe9a..e278d08 100644 --- a/frost-core/src/tests/vectors.rs +++ b/frost-core/src/tests/vectors.rs @@ -293,7 +293,8 @@ pub fn check_sign_with_test_vectors(json_vectors: &Value) { .map(|(i, key_package)| (i, *key_package.verifying_share())) .collect(); - let pubkey_package = frost::keys::PublicKeyPackage::new(verifying_shares, verifying_key); + let pubkey_package = + frost::keys::PublicKeyPackage::new(verifying_shares, verifying_key, min_signers as u16); //////////////////////////////////////////////////////////////////////////// // Aggregation: collects the signing shares from all participants, diff --git a/frost-core/src/tests/vectors_dkg.rs b/frost-core/src/tests/vectors_dkg.rs index 02d0c85..90fafef 100644 --- a/frost-core/src/tests/vectors_dkg.rs +++ b/frost-core/src/tests/vectors_dkg.rs @@ -176,6 +176,7 @@ fn build_public_key_package(json_vectors: &Value) -> PublicKeyPa let mut verifying_shares = BTreeMap::new(); let max_participants = json_vectors["config"]["MAX_PARTICIPANTS"].as_u64().unwrap() as u8; + let min_participants = json_vectors["config"]["MIN_PARTICIPANTS"].as_u64().unwrap() as u8; for i in 1..=max_participants { let participant_id: Identifier = (inputs[i.to_string()]["identifier"].as_u64().unwrap() @@ -196,6 +197,7 @@ fn build_public_key_package(json_vectors: &Value) -> PublicKeyPa header: Header::default(), verifying_shares, verifying_key, + min_signers: Some(min_participants as u16), } } diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs index 321d64f..9ae9431 100644 --- a/frost-core/src/traits.rs +++ b/frost-core/src/traits.rs @@ -295,9 +295,9 @@ pub trait Ciphersuite: Copy + PartialEq + Debug + 'static + Send + Sync { /// Optional. Pre-process [`crate::aggregate()`] and /// [`crate::verify_signature_share()`] inputs. In the latter case, "dummy" /// container BTreeMap and PublicKeyPackage are passed with the relevant - /// values. The default implementation returns them as-is. [`Cow`] is used - /// so implementations can choose to return the same passed reference or a - /// modified clone. + /// values (PublicKeyPackage.min_signers will be None). The default + /// implementation returns them as-is. [`Cow`] is used so implementations + /// can choose to return the same passed reference or a modified clone. #[allow(clippy::type_complexity)] fn pre_aggregate<'a>( signing_package: &'a SigningPackage, diff --git a/frost-ed25519/src/keys/refresh.rs b/frost-ed25519/src/keys/refresh.rs index 3e26bcb..36797d9 100644 --- a/frost-ed25519/src/keys/refresh.rs +++ b/frost-ed25519/src/keys/refresh.rs @@ -5,14 +5,14 @@ use crate::{ frost, keys::dkg::{round1, round2}, - Ciphersuite, CryptoRng, Error, Identifier, RngCore, + CryptoRng, Error, Identifier, RngCore, }; use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. -pub fn compute_refreshing_shares( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, @@ -29,21 +29,21 @@ pub fn compute_refreshing_shares( } /// Refer to [`frost_core::keys::refresh::refresh_share`]. -pub fn refresh_share( +pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } -/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part1`]. pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, mut rng: R, ) -> Result<(round1::SecretPackage, round1::Package), Error> { - frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) + frost::keys::refresh::refresh_dkg_part1(identifier, max_signers, min_signers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. diff --git a/frost-ed25519/tests/helpers/samples.rs b/frost-ed25519/tests/helpers/samples.rs index e1d87e1..3ce2f8a 100644 --- a/frost-ed25519/tests/helpers/samples.rs +++ b/frost-ed25519/tests/helpers/samples.rs @@ -104,7 +104,19 @@ pub fn public_key_package() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, None) +} + +/// Generate a sample PublicKeyPackage with `min_signers`. +pub fn public_key_package_new() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key, 2) } /// Generate a sample round1::SecretPackage. diff --git a/frost-ed25519/tests/integration_tests.rs b/frost-ed25519/tests/integration_tests.rs index 649576c..d43664a 100644 --- a/frost-ed25519/tests/integration_tests.rs +++ b/frost-ed25519/tests/integration_tests.rs @@ -117,7 +117,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s Identifier::try_from(4).unwrap(), Identifier::try_from(5).unwrap(), ]; - let min_signers = 3; + let min_signers = 2; let max_signers = 3; let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; @@ -150,7 +150,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 3; + let min_signers = 2; let max_signers = 1; let error = Error::InvalidMaxSigners; diff --git a/frost-ed25519/tests/recreation_tests.rs b/frost-ed25519/tests/recreation_tests.rs index 5ae8964..4261cb4 100644 --- a/frost-ed25519/tests/recreation_tests.rs +++ b/frost-ed25519/tests/recreation_tests.rs @@ -101,8 +101,25 @@ fn check_public_key_package_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers(); - let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + let new_public_key_package = + PublicKeyPackage::new_internal(verifying_shares.clone(), *verifying_key, min_signers); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_new_recreation() { + let public_key_package = samples::public_key_package_new(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers().unwrap(); + + let new_public_key_package = + PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); assert!(public_key_package == new_public_key_package); } diff --git a/frost-ed25519/tests/serde_tests.rs b/frost-ed25519/tests/serde_tests.rs index 9f72279..a36c278 100644 --- a/frost-ed25519/tests/serde_tests.rs +++ b/frost-ed25519/tests/serde_tests.rs @@ -434,7 +434,7 @@ fn check_key_package_serialization() { #[test] fn check_public_key_package_serialization() { - let public_key_package = samples::public_key_package(); + let public_key_package = samples::public_key_package_new(); let json = serde_json::to_string_pretty(&public_key_package).unwrap(); println!("{}", json); @@ -450,11 +450,27 @@ fn check_public_key_package_serialization() { "verifying_shares": { "2a00000000000000000000000000000000000000000000000000000000000000": "5866666666666666666666666666666666666666666666666666666666666666" }, - "verifying_key": "5866666666666666666666666666666666666666666666666666666666666666" + "verifying_key": "5866666666666666666666666666666666666666666666666666666666666666", + "min_signers": 2 }"#; let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); assert!(public_key_package == decoded_public_key_package); + // Old version without min_signers + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-ED25519-SHA512-v1" + }, + "verifying_shares": { + "2a00000000000000000000000000000000000000000000000000000000000000": "5866666666666666666666666666666666666666666666666666666666666666" + }, + "verifying_key": "5866666666666666666666666666666666666666666666666666666666666666" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package.verifying_key() == decoded_public_key_package.verifying_key()); + assert!(public_key_package.verifying_shares() == decoded_public_key_package.verifying_shares()); + let invalid_json = "{}"; assert!(serde_json::from_str::(invalid_json).is_err()); diff --git a/frost-ed25519/tests/serialization_tests.rs b/frost-ed25519/tests/serialization_tests.rs index 7fe52b9..6af9016 100644 --- a/frost-ed25519/tests/serialization_tests.rs +++ b/frost-ed25519/tests/serialization_tests.rs @@ -82,6 +82,17 @@ fn check_public_key_package_postcard_serialization() { ); } +#[test] +fn check_public_key_package_new_postcard_serialization() { + let public_key_package = samples::public_key_package_new(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + #[test] fn check_round1_secret_package_postcard_serialization() { let round1_secret_package = samples::round1_secret_package(); diff --git a/frost-ed25519/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap b/frost-ed25519/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap new file mode 100644 index 0000000..342de36 --- /dev/null +++ b/frost-ed25519/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-ed25519/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00b169f0da012a00000000000000000000000000000000000000000000000000000000000000586666666666666666666666666666666666666666666666666666666666666658666666666666666666666666666666666666666666666666666666666666660102 diff --git a/frost-ed448/src/keys/refresh.rs b/frost-ed448/src/keys/refresh.rs index 3e26bcb..36797d9 100644 --- a/frost-ed448/src/keys/refresh.rs +++ b/frost-ed448/src/keys/refresh.rs @@ -5,14 +5,14 @@ use crate::{ frost, keys::dkg::{round1, round2}, - Ciphersuite, CryptoRng, Error, Identifier, RngCore, + CryptoRng, Error, Identifier, RngCore, }; use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. -pub fn compute_refreshing_shares( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, @@ -29,21 +29,21 @@ pub fn compute_refreshing_shares( } /// Refer to [`frost_core::keys::refresh::refresh_share`]. -pub fn refresh_share( +pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } -/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part1`]. pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, mut rng: R, ) -> Result<(round1::SecretPackage, round1::Package), Error> { - frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) + frost::keys::refresh::refresh_dkg_part1(identifier, max_signers, min_signers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. diff --git a/frost-ed448/tests/helpers/samples.rs b/frost-ed448/tests/helpers/samples.rs index 4994b51..d4e0452 100644 --- a/frost-ed448/tests/helpers/samples.rs +++ b/frost-ed448/tests/helpers/samples.rs @@ -104,7 +104,19 @@ pub fn public_key_package() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, None) +} + +/// Generate a sample PublicKeyPackage with `min_signers`. +pub fn public_key_package_new() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key, 2) } /// Generate a sample round1::SecretPackage. diff --git a/frost-ed448/tests/integration_tests.rs b/frost-ed448/tests/integration_tests.rs index 601228d..d22705c 100644 --- a/frost-ed448/tests/integration_tests.rs +++ b/frost-ed448/tests/integration_tests.rs @@ -117,7 +117,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s Identifier::try_from(4).unwrap(), Identifier::try_from(5).unwrap(), ]; - let min_signers = 3; + let min_signers = 2; let max_signers = 3; let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; @@ -150,7 +150,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 3; + let min_signers = 2; let max_signers = 1; let error = Error::InvalidMaxSigners; diff --git a/frost-ed448/tests/recreation_tests.rs b/frost-ed448/tests/recreation_tests.rs index 44b7abf..a0a457a 100644 --- a/frost-ed448/tests/recreation_tests.rs +++ b/frost-ed448/tests/recreation_tests.rs @@ -101,8 +101,25 @@ fn check_public_key_package_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers(); - let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + let new_public_key_package = + PublicKeyPackage::new_internal(verifying_shares.clone(), *verifying_key, min_signers); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_new_recreation() { + let public_key_package = samples::public_key_package_new(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers().unwrap(); + + let new_public_key_package = + PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); assert!(public_key_package == new_public_key_package); } diff --git a/frost-ed448/tests/serde_tests.rs b/frost-ed448/tests/serde_tests.rs index 3b5c667..db380ee 100644 --- a/frost-ed448/tests/serde_tests.rs +++ b/frost-ed448/tests/serde_tests.rs @@ -434,7 +434,7 @@ fn check_key_package_serialization() { #[test] fn check_public_key_package_serialization() { - let public_key_package = samples::public_key_package(); + let public_key_package = samples::public_key_package_new(); let json = serde_json::to_string_pretty(&public_key_package).unwrap(); println!("{}", json); @@ -450,11 +450,27 @@ fn check_public_key_package_serialization() { "verifying_shares": { "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900" }, - "verifying_key": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900" + "verifying_key": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900", + "min_signers": 2 }"#; let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); assert!(public_key_package == decoded_public_key_package); + // Old version without min_signers + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-ED448-SHAKE256-v1" + }, + "verifying_shares": { + "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900" + }, + "verifying_key": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package.verifying_key() == decoded_public_key_package.verifying_key()); + assert!(public_key_package.verifying_shares() == decoded_public_key_package.verifying_shares()); + let invalid_json = "{}"; assert!(serde_json::from_str::(invalid_json).is_err()); diff --git a/frost-ed448/tests/serialization_tests.rs b/frost-ed448/tests/serialization_tests.rs index e6a177e..e9810a6 100644 --- a/frost-ed448/tests/serialization_tests.rs +++ b/frost-ed448/tests/serialization_tests.rs @@ -82,6 +82,17 @@ fn check_public_key_package_postcard_serialization() { ); } +#[test] +fn check_public_key_package_new_postcard_serialization() { + let public_key_package = samples::public_key_package_new(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + #[test] fn check_round1_secret_package_postcard_serialization() { let round1_secret_package = samples::round1_secret_package(); diff --git a/frost-ed448/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap b/frost-ed448/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap new file mode 100644 index 0000000..fcfcff4 --- /dev/null +++ b/frost-ed448/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-ed448/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +005a064cfd012a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f690014fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f69000102 diff --git a/frost-p256/src/keys/refresh.rs b/frost-p256/src/keys/refresh.rs index 3e26bcb..36797d9 100644 --- a/frost-p256/src/keys/refresh.rs +++ b/frost-p256/src/keys/refresh.rs @@ -5,14 +5,14 @@ use crate::{ frost, keys::dkg::{round1, round2}, - Ciphersuite, CryptoRng, Error, Identifier, RngCore, + CryptoRng, Error, Identifier, RngCore, }; use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. -pub fn compute_refreshing_shares( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, @@ -29,21 +29,21 @@ pub fn compute_refreshing_shares( } /// Refer to [`frost_core::keys::refresh::refresh_share`]. -pub fn refresh_share( +pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } -/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part1`]. pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, mut rng: R, ) -> Result<(round1::SecretPackage, round1::Package), Error> { - frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) + frost::keys::refresh::refresh_dkg_part1(identifier, max_signers, min_signers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. diff --git a/frost-p256/tests/helpers/samples.rs b/frost-p256/tests/helpers/samples.rs index be57e47..df9407b 100644 --- a/frost-p256/tests/helpers/samples.rs +++ b/frost-p256/tests/helpers/samples.rs @@ -104,7 +104,19 @@ pub fn public_key_package() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, None) +} + +/// Generate a sample PublicKeyPackage with `min_signers`. +pub fn public_key_package_new() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key, 2) } /// Generate a sample round1::SecretPackage. diff --git a/frost-p256/tests/integration_tests.rs b/frost-p256/tests/integration_tests.rs index 1fd354b..8a9a92e 100644 --- a/frost-p256/tests/integration_tests.rs +++ b/frost-p256/tests/integration_tests.rs @@ -117,7 +117,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s Identifier::try_from(4).unwrap(), Identifier::try_from(5).unwrap(), ]; - let min_signers = 3; + let min_signers = 2; let max_signers = 3; let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; @@ -150,7 +150,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 3; + let min_signers = 2; let max_signers = 1; let error = Error::InvalidMaxSigners; diff --git a/frost-p256/tests/recreation_tests.rs b/frost-p256/tests/recreation_tests.rs index 0f4dbf7..e5816bf 100644 --- a/frost-p256/tests/recreation_tests.rs +++ b/frost-p256/tests/recreation_tests.rs @@ -101,8 +101,25 @@ fn check_public_key_package_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers(); - let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + let new_public_key_package = + PublicKeyPackage::new_internal(verifying_shares.clone(), *verifying_key, min_signers); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_new_recreation() { + let public_key_package = samples::public_key_package_new(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers().unwrap(); + + let new_public_key_package = + PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); assert!(public_key_package == new_public_key_package); } diff --git a/frost-p256/tests/serde_tests.rs b/frost-p256/tests/serde_tests.rs index c147581..7091bd7 100644 --- a/frost-p256/tests/serde_tests.rs +++ b/frost-p256/tests/serde_tests.rs @@ -434,7 +434,7 @@ fn check_key_package_serialization() { #[test] fn check_public_key_package_serialization() { - let public_key_package = samples::public_key_package(); + let public_key_package = samples::public_key_package_new(); let json = serde_json::to_string_pretty(&public_key_package).unwrap(); println!("{}", json); @@ -450,11 +450,27 @@ fn check_public_key_package_serialization() { "verifying_shares": { "000000000000000000000000000000000000000000000000000000000000002a": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296" }, - "verifying_key": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296" + "verifying_key": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + "min_signers": 2 }"#; let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); assert!(public_key_package == decoded_public_key_package); + // Old version without min_signers + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-P256-SHA256-v1" + }, + "verifying_shares": { + "000000000000000000000000000000000000000000000000000000000000002a": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296" + }, + "verifying_key": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package.verifying_key() == decoded_public_key_package.verifying_key()); + assert!(public_key_package.verifying_shares() == decoded_public_key_package.verifying_shares()); + let invalid_json = "{}"; assert!(serde_json::from_str::(invalid_json).is_err()); diff --git a/frost-p256/tests/serialization_tests.rs b/frost-p256/tests/serialization_tests.rs index b83b12e..dbd32ce 100644 --- a/frost-p256/tests/serialization_tests.rs +++ b/frost-p256/tests/serialization_tests.rs @@ -82,6 +82,17 @@ fn check_public_key_package_postcard_serialization() { ); } +#[test] +fn check_public_key_package_new_postcard_serialization() { + let public_key_package = samples::public_key_package_new(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + #[test] fn check_round1_secret_package_postcard_serialization() { let round1_secret_package = samples::round1_secret_package(); diff --git a/frost-p256/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap b/frost-p256/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap new file mode 100644 index 0000000..158419f --- /dev/null +++ b/frost-p256/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-p256/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00a132f0c901000000000000000000000000000000000000000000000000000000000000002a036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2960102 diff --git a/frost-rerandomized/src/lib.rs b/frost-rerandomized/src/lib.rs index 78ee354..4099893 100644 --- a/frost-rerandomized/src/lib.rs +++ b/frost-rerandomized/src/lib.rs @@ -111,9 +111,10 @@ impl Randomize for PublicKeyPackage { }) .collect(); - Ok(PublicKeyPackage::new( + Ok(PublicKeyPackage::new_internal( randomized_verifying_shares, randomized_params.randomized_verifying_key, + self.min_signers(), )) } } diff --git a/frost-ristretto255/src/keys/refresh.rs b/frost-ristretto255/src/keys/refresh.rs index 3e26bcb..36797d9 100644 --- a/frost-ristretto255/src/keys/refresh.rs +++ b/frost-ristretto255/src/keys/refresh.rs @@ -5,14 +5,14 @@ use crate::{ frost, keys::dkg::{round1, round2}, - Ciphersuite, CryptoRng, Error, Identifier, RngCore, + CryptoRng, Error, Identifier, RngCore, }; use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. -pub fn compute_refreshing_shares( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, @@ -29,21 +29,21 @@ pub fn compute_refreshing_shares( } /// Refer to [`frost_core::keys::refresh::refresh_share`]. -pub fn refresh_share( +pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } -/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part1`]. pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, mut rng: R, ) -> Result<(round1::SecretPackage, round1::Package), Error> { - frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) + frost::keys::refresh::refresh_dkg_part1(identifier, max_signers, min_signers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. diff --git a/frost-ristretto255/tests/helpers/samples.rs b/frost-ristretto255/tests/helpers/samples.rs index d6f58b9..0b0a6e3 100644 --- a/frost-ristretto255/tests/helpers/samples.rs +++ b/frost-ristretto255/tests/helpers/samples.rs @@ -104,7 +104,19 @@ pub fn public_key_package() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, None) +} + +/// Generate a sample PublicKeyPackage with `min_signers`. +pub fn public_key_package_new() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key, 2) } /// Generate a sample round1::SecretPackage. diff --git a/frost-ristretto255/tests/integration_tests.rs b/frost-ristretto255/tests/integration_tests.rs index d16f682..8ff8af1 100644 --- a/frost-ristretto255/tests/integration_tests.rs +++ b/frost-ristretto255/tests/integration_tests.rs @@ -118,7 +118,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s Identifier::try_from(4).unwrap(), Identifier::try_from(5).unwrap(), ]; - let min_signers = 3; + let min_signers = 2; let max_signers = 3; let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; @@ -151,7 +151,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 3; + let min_signers = 2; let max_signers = 1; let error = Error::InvalidMaxSigners; diff --git a/frost-ristretto255/tests/recreation_tests.rs b/frost-ristretto255/tests/recreation_tests.rs index a5974a9..fc6947b 100644 --- a/frost-ristretto255/tests/recreation_tests.rs +++ b/frost-ristretto255/tests/recreation_tests.rs @@ -101,8 +101,25 @@ fn check_public_key_package_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers(); - let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + let new_public_key_package = + PublicKeyPackage::new_internal(verifying_shares.clone(), *verifying_key, min_signers); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_new_recreation() { + let public_key_package = samples::public_key_package_new(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers().unwrap(); + + let new_public_key_package = + PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); assert!(public_key_package == new_public_key_package); } diff --git a/frost-ristretto255/tests/serde_tests.rs b/frost-ristretto255/tests/serde_tests.rs index faf1769..d73e846 100644 --- a/frost-ristretto255/tests/serde_tests.rs +++ b/frost-ristretto255/tests/serde_tests.rs @@ -434,7 +434,7 @@ fn check_key_package_serialization() { #[test] fn check_public_key_package_serialization() { - let public_key_package = samples::public_key_package(); + let public_key_package = samples::public_key_package_new(); let json = serde_json::to_string_pretty(&public_key_package).unwrap(); println!("{}", json); @@ -450,11 +450,27 @@ fn check_public_key_package_serialization() { "verifying_shares": { "2a00000000000000000000000000000000000000000000000000000000000000": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" }, - "verifying_key": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" + "verifying_key": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", + "min_signers": 2 }"#; let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); assert!(public_key_package == decoded_public_key_package); + // Old version without min_signers + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-RISTRETTO255-SHA512-v1" + }, + "verifying_shares": { + "2a00000000000000000000000000000000000000000000000000000000000000": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" + }, + "verifying_key": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package.verifying_key() == decoded_public_key_package.verifying_key()); + assert!(public_key_package.verifying_shares() == decoded_public_key_package.verifying_shares()); + let invalid_json = "{}"; assert!(serde_json::from_str::(invalid_json).is_err()); diff --git a/frost-ristretto255/tests/serialization_tests.rs b/frost-ristretto255/tests/serialization_tests.rs index 1aa96a2..641bb47 100644 --- a/frost-ristretto255/tests/serialization_tests.rs +++ b/frost-ristretto255/tests/serialization_tests.rs @@ -82,6 +82,17 @@ fn check_public_key_package_postcard_serialization() { ); } +#[test] +fn check_public_key_package_new_postcard_serialization() { + let public_key_package = samples::public_key_package_new(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + #[test] fn check_round1_secret_package_postcard_serialization() { let round1_secret_package = samples::round1_secret_package(); diff --git a/frost-ristretto255/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap b/frost-ristretto255/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap new file mode 100644 index 0000000..8a5fd31 --- /dev/null +++ b/frost-ristretto255/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-ristretto255/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00d76ecff5012a00000000000000000000000000000000000000000000000000000000000000e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d760102 diff --git a/frost-secp256k1-tr/src/keys/refresh.rs b/frost-secp256k1-tr/src/keys/refresh.rs index 3e26bcb..36797d9 100644 --- a/frost-secp256k1-tr/src/keys/refresh.rs +++ b/frost-secp256k1-tr/src/keys/refresh.rs @@ -5,14 +5,14 @@ use crate::{ frost, keys::dkg::{round1, round2}, - Ciphersuite, CryptoRng, Error, Identifier, RngCore, + CryptoRng, Error, Identifier, RngCore, }; use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. -pub fn compute_refreshing_shares( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, @@ -29,21 +29,21 @@ pub fn compute_refreshing_shares( } /// Refer to [`frost_core::keys::refresh::refresh_share`]. -pub fn refresh_share( +pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } -/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part1`]. pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, mut rng: R, ) -> Result<(round1::SecretPackage, round1::Package), Error> { - frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) + frost::keys::refresh::refresh_dkg_part1(identifier, max_signers, min_signers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs index cc012d0..d4e5f7e 100644 --- a/frost-secp256k1-tr/src/lib.rs +++ b/frost-secp256k1-tr/src/lib.rs @@ -645,7 +645,7 @@ pub mod keys { (*i, vs) }) .collect(); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, self.min_signers()) } else { self } @@ -766,7 +766,11 @@ pub mod keys { (*i, vs) }) .collect(); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal( + verifying_shares, + verifying_key, + public_key_package.min_signers(), + ) } } diff --git a/frost-secp256k1-tr/tests/helpers/samples.rs b/frost-secp256k1-tr/tests/helpers/samples.rs index 337ae70..e94ae01 100644 --- a/frost-secp256k1-tr/tests/helpers/samples.rs +++ b/frost-secp256k1-tr/tests/helpers/samples.rs @@ -104,7 +104,19 @@ pub fn public_key_package() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, None) +} + +/// Generate a sample PublicKeyPackage with `min_signers`. +pub fn public_key_package_new() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key, 2) } /// Generate a sample round1::SecretPackage. diff --git a/frost-secp256k1-tr/tests/integration_tests.rs b/frost-secp256k1-tr/tests/integration_tests.rs index fe5007d..11db8b2 100644 --- a/frost-secp256k1-tr/tests/integration_tests.rs +++ b/frost-secp256k1-tr/tests/integration_tests.rs @@ -118,7 +118,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s Identifier::try_from(4).unwrap(), Identifier::try_from(5).unwrap(), ]; - let min_signers = 3; + let min_signers = 2; let max_signers = 3; let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; @@ -151,7 +151,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 3; + let min_signers = 2; let max_signers = 1; let error = Error::InvalidMaxSigners; diff --git a/frost-secp256k1-tr/tests/recreation_tests.rs b/frost-secp256k1-tr/tests/recreation_tests.rs index f6f71ea..bcdd3df 100644 --- a/frost-secp256k1-tr/tests/recreation_tests.rs +++ b/frost-secp256k1-tr/tests/recreation_tests.rs @@ -101,8 +101,25 @@ fn check_public_key_package_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers(); - let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + let new_public_key_package = + PublicKeyPackage::new_internal(verifying_shares.clone(), *verifying_key, min_signers); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_new_recreation() { + let public_key_package = samples::public_key_package_new(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers().unwrap(); + + let new_public_key_package = + PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); assert!(public_key_package == new_public_key_package); } diff --git a/frost-secp256k1-tr/tests/serde_tests.rs b/frost-secp256k1-tr/tests/serde_tests.rs index 62a70e7..e13cf1d 100644 --- a/frost-secp256k1-tr/tests/serde_tests.rs +++ b/frost-secp256k1-tr/tests/serde_tests.rs @@ -434,7 +434,7 @@ fn check_key_package_serialization() { #[test] fn check_public_key_package_serialization() { - let public_key_package = samples::public_key_package(); + let public_key_package = samples::public_key_package_new(); let json = serde_json::to_string_pretty(&public_key_package).unwrap(); println!("{}", json); @@ -450,11 +450,27 @@ fn check_public_key_package_serialization() { "verifying_shares": { "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" }, - "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "min_signers": 2 }"#; let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); assert!(public_key_package == decoded_public_key_package); + // Old version without min_signers + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "verifying_shares": { + "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }, + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package.verifying_key() == decoded_public_key_package.verifying_key()); + assert!(public_key_package.verifying_shares() == decoded_public_key_package.verifying_shares()); + let invalid_json = "{}"; assert!(serde_json::from_str::(invalid_json).is_err()); diff --git a/frost-secp256k1-tr/tests/serialization_tests.rs b/frost-secp256k1-tr/tests/serialization_tests.rs index d38bc5e..9975017 100644 --- a/frost-secp256k1-tr/tests/serialization_tests.rs +++ b/frost-secp256k1-tr/tests/serialization_tests.rs @@ -82,6 +82,17 @@ fn check_public_key_package_postcard_serialization() { ); } +#[test] +fn check_public_key_package_new_postcard_serialization() { + let public_key_package = samples::public_key_package_new(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + #[test] fn check_round1_secret_package_postcard_serialization() { let round1_secret_package = samples::round1_secret_package(); diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap new file mode 100644 index 0000000..b147c34 --- /dev/null +++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-secp256k1-tr/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00230f8ab301000000000000000000000000000000000000000000000000000000000000002a0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980102 diff --git a/frost-secp256k1/src/keys/refresh.rs b/frost-secp256k1/src/keys/refresh.rs index 3e26bcb..36797d9 100644 --- a/frost-secp256k1/src/keys/refresh.rs +++ b/frost-secp256k1/src/keys/refresh.rs @@ -5,14 +5,14 @@ use crate::{ frost, keys::dkg::{round1, round2}, - Ciphersuite, CryptoRng, Error, Identifier, RngCore, + CryptoRng, Error, Identifier, RngCore, }; use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. -pub fn compute_refreshing_shares( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, @@ -29,21 +29,21 @@ pub fn compute_refreshing_shares( } /// Refer to [`frost_core::keys::refresh::refresh_share`]. -pub fn refresh_share( +pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } -/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part1`]. pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, mut rng: R, ) -> Result<(round1::SecretPackage, round1::Package), Error> { - frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) + frost::keys::refresh::refresh_dkg_part1(identifier, max_signers, min_signers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. diff --git a/frost-secp256k1/tests/helpers/samples.rs b/frost-secp256k1/tests/helpers/samples.rs index a69632d..2b1bb8e 100644 --- a/frost-secp256k1/tests/helpers/samples.rs +++ b/frost-secp256k1/tests/helpers/samples.rs @@ -104,7 +104,19 @@ pub fn public_key_package() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, None) +} + +/// Generate a sample PublicKeyPackage with `min_signers`. +pub fn public_key_package_new() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key, 2) } /// Generate a sample round1::SecretPackage. diff --git a/frost-secp256k1/tests/integration_tests.rs b/frost-secp256k1/tests/integration_tests.rs index 63ba125..e2c111c 100644 --- a/frost-secp256k1/tests/integration_tests.rs +++ b/frost-secp256k1/tests/integration_tests.rs @@ -117,7 +117,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s Identifier::try_from(4).unwrap(), Identifier::try_from(5).unwrap(), ]; - let min_signers = 3; + let min_signers = 2; let max_signers = 3; let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; @@ -150,7 +150,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 3; + let min_signers = 2; let max_signers = 1; let error = Error::InvalidMaxSigners; diff --git a/frost-secp256k1/tests/recreation_tests.rs b/frost-secp256k1/tests/recreation_tests.rs index e61b303..f72c154 100644 --- a/frost-secp256k1/tests/recreation_tests.rs +++ b/frost-secp256k1/tests/recreation_tests.rs @@ -101,8 +101,25 @@ fn check_public_key_package_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers(); - let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + let new_public_key_package = + PublicKeyPackage::new_internal(verifying_shares.clone(), *verifying_key, min_signers); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_new_recreation() { + let public_key_package = samples::public_key_package_new(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers().unwrap(); + + let new_public_key_package = + PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); assert!(public_key_package == new_public_key_package); } diff --git a/frost-secp256k1/tests/serde_tests.rs b/frost-secp256k1/tests/serde_tests.rs index 82a0735..5b02fda 100644 --- a/frost-secp256k1/tests/serde_tests.rs +++ b/frost-secp256k1/tests/serde_tests.rs @@ -434,7 +434,7 @@ fn check_key_package_serialization() { #[test] fn check_public_key_package_serialization() { - let public_key_package = samples::public_key_package(); + let public_key_package = samples::public_key_package_new(); let json = serde_json::to_string_pretty(&public_key_package).unwrap(); println!("{}", json); @@ -450,11 +450,27 @@ fn check_public_key_package_serialization() { "verifying_shares": { "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" }, - "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "min_signers": 2 }"#; let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); assert!(public_key_package == decoded_public_key_package); + // Old version without min_signers + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-v1" + }, + "verifying_shares": { + "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }, + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package.verifying_key() == decoded_public_key_package.verifying_key()); + assert!(public_key_package.verifying_shares() == decoded_public_key_package.verifying_shares()); + let invalid_json = "{}"; assert!(serde_json::from_str::(invalid_json).is_err()); diff --git a/frost-secp256k1/tests/serialization_tests.rs b/frost-secp256k1/tests/serialization_tests.rs index ec8d6f8..08e53d3 100644 --- a/frost-secp256k1/tests/serialization_tests.rs +++ b/frost-secp256k1/tests/serialization_tests.rs @@ -82,6 +82,17 @@ fn check_public_key_package_postcard_serialization() { ); } +#[test] +fn check_public_key_package_new_postcard_serialization() { + let public_key_package = samples::public_key_package_new(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + #[test] fn check_round1_secret_package_postcard_serialization() { let round1_secret_package = samples::round1_secret_package(); diff --git a/frost-secp256k1/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap b/frost-secp256k1/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap new file mode 100644 index 0000000..0435fda --- /dev/null +++ b/frost-secp256k1/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-secp256k1/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00eed6b1b101000000000000000000000000000000000000000000000000000000000000002a0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980102 From 8f60e6da72d4d948f64c309bb70b16fc21c7cf37 Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 22 Dec 2025 11:53:28 -0300 Subject: [PATCH 53/86] core: add more serialization roundtrips to tests (#952) --- frost-core/src/tests/ciphersuite_generic.rs | 77 ++++++++++++++++++--- frost-core/src/tests/refresh.rs | 40 ++++++++++- 2 files changed, 106 insertions(+), 11 deletions(-) diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs index a2c0df8..a15bd25 100644 --- a/frost-core/src/tests/ciphersuite_generic.rs +++ b/frost-core/src/tests/ciphersuite_generic.rs @@ -6,7 +6,8 @@ use rand_core::{CryptoRng, RngCore}; use crate as frost; use crate::keys::dkg::{round1, round2}; -use crate::keys::SigningShare; +use crate::keys::{SecretShare, SigningShare}; +use crate::round1::SigningNonces; use crate::round2::SignatureShare; use crate::{ keys::PublicKeyPackage, Error, Field, Group, Identifier, Signature, SigningKey, SigningPackage, @@ -26,6 +27,8 @@ pub fn check_zero_key_fails() { /// Test share generation with a Ciphersuite pub fn check_share_generation(mut rng: R) { let secret = crate::SigningKey::::new(&mut rng); + // Simulate serialization / deserialization to ensure it works + let secret = SigningKey::deserialize(&secret.serialize()).unwrap(); let max_signers = 5; let min_signers = 3; @@ -110,20 +113,30 @@ pub fn check_sign_with_dealer( let max_signers = 5; let min_signers = 3; - let (shares, pubkeys) = frost::keys::generate_with_dealer( + let (shares, pub_key_package) = frost::keys::generate_with_dealer( max_signers, min_signers, frost::keys::IdentifierList::Default, &mut rng, ) .unwrap(); + // Simulate serialization / deserialization to ensure it works + let pub_key_package = + PublicKeyPackage::deserialize(&pub_key_package.serialize().unwrap()).unwrap(); // Verifies the secret shares from the dealer - let key_packages: BTreeMap, frost::keys::KeyPackage> = shares - .into_iter() - .map(|(k, v)| (k, frost::keys::KeyPackage::try_from(v).unwrap())) - .collect(); + let mut key_packages: BTreeMap, frost::keys::KeyPackage> = + BTreeMap::new(); + for (k, v) in shares { + // Simulate serialization / deserialization to ensure it works + let v = SecretShare::::deserialize(&v.serialize().unwrap()).unwrap(); + let key_package = frost::keys::KeyPackage::try_from(v).unwrap(); + // Simulate serialization / deserialization to ensure it works + let key_package = + frost::keys::KeyPackage::deserialize(&key_package.serialize().unwrap()).unwrap(); + key_packages.insert(k, key_package); + } // Check if it fails with not enough signers. Usually this would return an // error before even running the signing procedure, because `KeyPackage` // contains the correct `min_signers` value and the signing procedure checks @@ -144,11 +157,11 @@ pub fn check_sign_with_dealer( }) .collect(), &mut rng, - pubkeys.clone(), + pub_key_package.clone(), ); assert_eq!(r, Err(Error::InvalidSignature)); - check_sign(min_signers, key_packages, rng, pubkeys).unwrap() + check_sign(min_signers, key_packages, rng, pub_key_package).unwrap() } /// Test FROST signing with trusted dealer fails with invalid numbers of signers. @@ -203,7 +216,10 @@ pub fn check_sign( // Round 1: generating nonces and signing commitments for each participant //////////////////////////////////////////////////////////////////////////// - for participant_identifier in key_packages.keys().take(min_signers as usize).cloned() { + for participant_identifier in key_packages.keys().take(min_signers as usize) { + // Simulate serialization / deserialization to ensure it works + let participant_identifier = + Identifier::deserialize(&participant_identifier.serialize()).unwrap(); // Generate one (1) nonce and one SigningCommitments instance for each // participant, up to _min_signers_. let (nonces, commitments) = frost::round1::commit( @@ -213,6 +229,11 @@ pub fn check_sign( .signing_share(), &mut rng, ); + // Simulate serialization / deserialization to ensure it works + let nonces = SigningNonces::deserialize(&nonces.serialize().unwrap()).unwrap(); + let commitments = + frost::round1::SigningCommitments::deserialize(&commitments.serialize().unwrap()) + .unwrap(); nonces_map.insert(participant_identifier, nonces); commitments_map.insert(participant_identifier, commitments); } @@ -223,6 +244,9 @@ pub fn check_sign( let mut signature_shares = BTreeMap::new(); let message = "message to sign".as_bytes(); let signing_package = SigningPackage::new(commitments_map, message); + // Simulate serialization / deserialization to ensure it works + let signing_package = + SigningPackage::deserialize(&signing_package.serialize().unwrap()).unwrap(); //////////////////////////////////////////////////////////////////////////// // Round 2: each participant generates their signature share @@ -241,6 +265,8 @@ pub fn check_sign( // Each participant generates their signature share. let signature_share = frost::round2::sign(&signing_package, nonces_to_use, key_package)?; + // Simulate serialization / deserialization to ensure it works + let signature_share = SignatureShare::deserialize(&signature_share.serialize()).unwrap(); signature_shares.insert(*participant_identifier, signature_share); } @@ -265,6 +291,8 @@ pub fn check_sign( // Aggregate (also verifies the signature shares) let group_signature = frost::aggregate(&signing_package, &signature_shares, &pubkey_package)?; + // Simulate serialization / deserialization to ensure it works + let group_signature = Signature::deserialize(&group_signature.serialize().unwrap()).unwrap(); // Check that the threshold signature can be verified by the group public // key (the verification key). @@ -451,6 +479,16 @@ where frost::keys::dkg::part1(participant_identifier, max_signers, min_signers, &mut rng) .unwrap(); + // Simulate serialization / deserialization to ensure it works + let round1_secret_package = frost::keys::dkg::round1::SecretPackage::::deserialize( + &round1_secret_package.serialize().unwrap(), + ) + .unwrap(); + let round1_package = frost::keys::dkg::round1::Package::::deserialize( + &round1_package.serialize().unwrap(), + ) + .unwrap(); + // Store the participant's secret package for later use. // In practice each participant will store it in their own environment. round1_secret_packages.insert( @@ -507,6 +545,12 @@ where let (round2_secret_package, round2_packages) = frost::keys::dkg::part2(round1_secret_package, round1_packages).expect("should work"); + // Simulate serialization / deserialization to ensure it works + let round2_secret_package = frost::keys::dkg::round2::SecretPackage::::deserialize( + &round2_secret_package.serialize().unwrap(), + ) + .unwrap(); + // Store the participant's secret package for later use. // In practice each participant will store it in their own environment. round2_secret_packages.insert( @@ -522,6 +566,11 @@ where // Note that, in contrast to the previous part, here each other participant // gets its own specific package. for (receiver_identifier, round2_package) in round2_packages { + // Simulate serialization / deserialization to ensure it works + let round2_package = frost::keys::dkg::round2::Package::::deserialize( + &round2_package.serialize().unwrap(), + ) + .unwrap(); received_round2_packages .entry(receiver_identifier) .or_insert_with(BTreeMap::new) @@ -572,6 +621,13 @@ where &received_round2_packages[&participant_identifier], ) .unwrap(); + // Simulate serialization / deserialization to ensure it works + let key_package = + frost::keys::KeyPackage::deserialize(&key_package.serialize().unwrap()).unwrap(); + let pubkey_package_for_participant = frost::keys::PublicKeyPackage::deserialize( + &pubkey_package_for_participant.serialize().unwrap(), + ) + .unwrap(); verifying_shares.insert(participant_identifier, key_package.verifying_share); // Test if all verifying_key are equal if let Some(previous_verifying_key) = verifying_key { @@ -593,6 +649,9 @@ where .unwrap() .1 .clone(); + // Simulate serialization / deserialization to ensure it works + let pubkeys = + frost::keys::PublicKeyPackage::deserialize(&pubkeys.serialize().unwrap()).unwrap(); // Proceed with the signing test. check_sign(min_signers, key_packages, rng, pubkeys).unwrap() diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index f9b58db..a8fdfb5 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -69,6 +69,10 @@ pub fn check_refresh_shares_with_dealer( &mut rng, ) .unwrap(); + // Simulate serialization / deserialization to ensure it works + let new_pub_key_package = + frost::keys::PublicKeyPackage::deserialize(&new_pub_key_package.serialize().unwrap()) + .unwrap(); // Each participant refreshes their share @@ -79,14 +83,16 @@ pub fn check_refresh_shares_with_dealer( let current_share = &old_key_packages[&identifier]; // Do a serialization roundtrip to simulate real usage let zero_share = SecretShare::deserialize(&zero_shares[i].serialize().unwrap()).unwrap(); - let new_share = refresh_share(zero_share, current_share); + let new_share = refresh_share(zero_share, current_share).unwrap(); new_shares.insert(identifier, new_share); } let mut key_packages: BTreeMap, KeyPackage> = BTreeMap::new(); for (k, v) in new_shares { - key_packages.insert(k, v.unwrap()); + // Simulate serialization / deserialization to ensure it works + let v = KeyPackage::::deserialize(&v.serialize().unwrap()).unwrap(); + key_packages.insert(k, v); } check_sign(MIN_SIGNERS, key_packages, rng, new_pub_key_package).unwrap(); } @@ -371,6 +377,16 @@ where let (round1_secret_package, round1_package) = refresh_dkg_part1(participant_identifier, max_signers, min_signers, &mut rng).unwrap(); + // Simulate serialization / deserialization to ensure it works + let round1_secret_package = frost::keys::dkg::round1::SecretPackage::::deserialize( + &round1_secret_package.serialize().unwrap(), + ) + .unwrap(); + let round1_package = frost::keys::dkg::round1::Package::::deserialize( + &round1_package.serialize().unwrap(), + ) + .unwrap(); + // Store the participant's secret package for later use. // In practice each participant will store it in their own environment. round1_secret_packages.insert( @@ -421,6 +437,12 @@ where let (round2_secret_package, round2_packages) = refresh_dkg_part2(round1_secret_package, round1_packages).expect("should work"); + // Simulate serialization / deserialization to ensure it works + let round2_secret_package = frost::keys::dkg::round2::SecretPackage::::deserialize( + &round2_secret_package.serialize().unwrap(), + ) + .unwrap(); + // Store the participant's secret package for later use. // In practice each participant will store it in their own environment. round2_secret_packages.insert( @@ -436,6 +458,11 @@ where // Note that, in contrast to the previous part, here each other participant // gets its own specific package. for (receiver_identifier, round2_package) in round2_packages { + // Simulate serialization / deserialization to ensure it works + let round2_package = frost::keys::dkg::round2::Package::::deserialize( + &round2_package.serialize().unwrap(), + ) + .unwrap(); received_round2_packages .entry(receiver_identifier) .or_insert_with(BTreeMap::new) @@ -488,6 +515,12 @@ where old_key_packages[&participant_identifier].clone(), ) .unwrap(); + // Simulate serialization / deserialization to ensure it works + let key_package = KeyPackage::deserialize(&key_package.serialize().unwrap()).unwrap(); + let pubkey_package_for_participant = frost::keys::PublicKeyPackage::deserialize( + &pubkey_package_for_participant.serialize().unwrap(), + ) + .unwrap(); verifying_shares.insert(participant_identifier, key_package.verifying_share); // Test if all verifying_key are equal if let Some(previous_verifying_key) = verifying_key { @@ -509,6 +542,9 @@ where .unwrap() .1 .clone(); + // Simulate serialization / deserialization to ensure it works + let pubkeys = + frost::keys::PublicKeyPackage::deserialize(&pubkeys.serialize().unwrap()).unwrap(); // Proceed with the signing test. check_sign(min_signers, key_packages, rng, pubkeys).unwrap() From 59b4dbd8be6f6914e7811dc0a555f940926562e9 Mon Sep 17 00:00:00 2001 From: Conrado Date: Mon, 22 Dec 2025 14:05:50 -0300 Subject: [PATCH 54/86] update debugless-unwrap (#962) --- Cargo.lock | 4 ++-- Cargo.toml | 2 ++ frost-core/Cargo.toml | 5 +++-- frost-core/src/tests/coefficient_commitment.rs | 2 +- frost-core/src/tests/repairable.rs | 2 +- frost-core/src/tests/vectors.rs | 2 +- frost-core/src/tests/vectors_dkg.rs | 2 +- frost-core/src/tests/vss_commitment.rs | 2 +- 8 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3b63dc0..d808235 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -353,9 +353,9 @@ dependencies = [ [[package]] name = "debugless-unwrap" -version = "0.0.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f400d0750c0c069e8493f2256cb4da6f604b6d2eeb69a0ca8863acde352f8400" +checksum = "b93fdcfc175d53094fca1d23d171685621bb67f1658413514f2053d575b3b38c" [[package]] name = "der" diff --git a/Cargo.toml b/Cargo.toml index 6b89170..dc52fea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,8 @@ repository = "https://github.com/ZcashFoundation/frost" categories = ["cryptography"] [workspace.dependencies] +# Currently holding back from updating due to MSRV 1.86 of criterion 0.8 +# (we don't want to raise our MSRV just for this) criterion = "0.6" document-features = "0.2.7" hex = { version = "0.4.3", default-features = false, features = ["alloc"] } diff --git a/frost-core/Cargo.toml b/frost-core/Cargo.toml index 7e3c5d3..a514782 100644 --- a/frost-core/Cargo.toml +++ b/frost-core/Cargo.toml @@ -19,7 +19,7 @@ rustdoc-args = ["--cfg", "docsrs"] byteorder = { version = "1.4", default-features = false } const-crc32 = { version = "1.2.0", package = "const-crc32-nostd" } document-features.workspace = true -debugless-unwrap = "0.0.4" +debugless-unwrap = { version = "1.0.0", optional = true } derive-getters = "0.5.0" hex.workspace = true postcard = { version = "1.0.0", features = ["alloc"], optional = true } @@ -41,6 +41,7 @@ criterion = { workspace = true, optional = true } tokio = { workspace = true, optional = true } [dev-dependencies] +debugless-unwrap = "1.0.0" criterion.workspace = true lazy_static.workspace = true proptest.workspace = true @@ -64,7 +65,7 @@ internals = [] serde = ["dep:serde", "dep:serdect"] serialization = ["serde", "dep:postcard"] # Exposes ciphersuite-generic tests for other crates to use -test-impl = ["dep:proptest", "dep:serde_json", "dep:criterion", "dep:tokio"] +test-impl = ["dep:proptest", "dep:serde_json", "dep:criterion", "dep:tokio", "dep:debugless-unwrap"] [lib] bench = false diff --git a/frost-core/src/tests/coefficient_commitment.rs b/frost-core/src/tests/coefficient_commitment.rs index 533bf54..b491108 100644 --- a/frost-core/src/tests/coefficient_commitment.rs +++ b/frost-core/src/tests/coefficient_commitment.rs @@ -2,7 +2,7 @@ use crate as frost; use crate::{keys::CoefficientCommitment, tests::helpers::generate_element, Group}; -use debugless_unwrap::DebuglessUnwrap; +use debugless_unwrap::DebuglessUnwrapExt; use rand_core::{CryptoRng, RngCore}; use serde_json::Value; diff --git a/frost-core/src/tests/repairable.rs b/frost-core/src/tests/repairable.rs index 27136f8..4c1aaf8 100644 --- a/frost-core/src/tests/repairable.rs +++ b/frost-core/src/tests/repairable.rs @@ -2,7 +2,7 @@ use alloc::collections::BTreeMap; -use debugless_unwrap::DebuglessUnwrap; +use debugless_unwrap::DebuglessUnwrapExt; use rand_core::{CryptoRng, RngCore}; use serde_json::Value; diff --git a/frost-core/src/tests/vectors.rs b/frost-core/src/tests/vectors.rs index e278d08..b3bfd7f 100644 --- a/frost-core/src/tests/vectors.rs +++ b/frost-core/src/tests/vectors.rs @@ -1,7 +1,7 @@ //! Helper function for testing with test vectors. use alloc::collections::BTreeMap; -use debugless_unwrap::DebuglessUnwrap; +use debugless_unwrap::DebuglessUnwrapExt; use hex::{self, FromHex}; use serde_json::Value; diff --git a/frost-core/src/tests/vectors_dkg.rs b/frost-core/src/tests/vectors_dkg.rs index 90fafef..6f1ce53 100644 --- a/frost-core/src/tests/vectors_dkg.rs +++ b/frost-core/src/tests/vectors_dkg.rs @@ -4,7 +4,7 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; -use debugless_unwrap::DebuglessUnwrap; +use debugless_unwrap::DebuglessUnwrapExt; use hex::{self}; use serde_json::Value; diff --git a/frost-core/src/tests/vss_commitment.rs b/frost-core/src/tests/vss_commitment.rs index 6949581..62a2755 100644 --- a/frost-core/src/tests/vss_commitment.rs +++ b/frost-core/src/tests/vss_commitment.rs @@ -6,7 +6,7 @@ use crate::{ Error, Group, }; use alloc::vec::Vec; -use debugless_unwrap::DebuglessUnwrap; +use debugless_unwrap::DebuglessUnwrapExt; use rand_core::{CryptoRng, RngCore}; use serde_json::Value; From 504ef202a6297b173f962a3f7c31dcf1be1cee2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kyle=20=F0=9F=90=86?= Date: Tue, 6 Jan 2026 10:10:45 -0400 Subject: [PATCH 55/86] Implement ZeroizeOnDrop for SigningNonces (#987) --- frost-core/src/round1.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frost-core/src/round1.rs b/frost-core/src/round1.rs index 33b7814..b6f2049 100644 --- a/frost-core/src/round1.rs +++ b/frost-core/src/round1.rs @@ -14,7 +14,7 @@ use derive_getters::Getters; use hex::FromHex; use rand_core::{CryptoRng, RngCore}; -use zeroize::Zeroize; +use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::{ serialization::{SerializableElement, SerializableScalar}, @@ -209,7 +209,7 @@ where /// Note that [`SigningNonces`] must be used *only once* for a signing /// operation; re-using nonces will result in leakage of a signer's long-lived /// signing key. -#[derive(Clone, Zeroize, PartialEq, Eq, Getters)] +#[derive(Clone, Zeroize, ZeroizeOnDrop, PartialEq, Eq, Getters)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] From 78066bffcffd40895ce836f45bf10ce18ff2a440 Mon Sep 17 00:00:00 2001 From: Conrado Date: Fri, 23 Jan 2026 07:58:20 -0300 Subject: [PATCH 56/86] all: cleanup repairable module (#957) * all: cleanup repairable module --- frost-core/CHANGELOG.md | 10 ++ frost-core/src/keys/repairable.rs | 195 ++++++++++++++++------ frost-core/src/tests/repairable.rs | 151 ++++++++++------- frost-ed25519/src/keys/repairable.rs | 52 +++--- frost-ed448/src/keys/repairable.rs | 52 +++--- frost-p256/src/keys/repairable.rs | 52 +++--- frost-ristretto255/src/keys/repairable.rs | 52 +++--- frost-secp256k1-tr/src/keys/repairable.rs | 52 +++--- frost-secp256k1/src/keys/repairable.rs | 52 +++--- 9 files changed, 412 insertions(+), 256 deletions(-) diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index ca1fbfb..be59fdf 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -15,6 +15,16 @@ Entries are listed in reverse chronological order. * The `std` and `nightly` features were removed from all crates * Renamed `frost_core::keys::refresh::refresh_dkg_part_1` to `refresh_dkg_part1`. * Fixed the crate-specific versions of the `refresh` module to be non-generic. +* Refactored the `frost_core::keys::repairable` module: + * `repair_share_step_1()` now takes a `KeyPackage` and returns a map with + a new `Delta` type instead of a raw `Scalar` + * `repair_share_step_2()` now takes the `Delta` type and returns a new `Sigma` + type instead of a raw `Scalar` + * `repair_share_step_3()` now takes the `Sigma` type and a `PublicKeyPackage` + instead of `VerifiableSecretSharingCommitment`; and returns a `KeyPackage` + instead of `SecretShare`. + * These changes provide more type safety and are make it more useful since + `SecretPackage`s are not expected to be stored ### Additional changes diff --git a/frost-core/src/keys/repairable.rs b/frost-core/src/keys/repairable.rs index 24cd32b..6acd52e 100644 --- a/frost-core/src/keys/repairable.rs +++ b/frost-core/src/keys/repairable.rs @@ -1,40 +1,128 @@ //! Repairable Threshold Scheme //! -//! Implements the Repairable Threshold Scheme (RTS) from . -//! The RTS is used to help a signer (participant) repair their lost share. This is achieved -//! using a subset of the other signers known here as `helpers`. +//! Implements the Repairable Threshold Scheme (RTS) from +//! . The RTS is used to help a signer +//! (participant) repair their lost share. This is achieved using a subset of +//! the other signers known here as `helpers`. +//! +//! The repair procedure should be run as follows: +//! +//! - Participants need to agree somehow on who are going to be the `helpers` +//! for the repair, and which participant is going to repair their share. +//! - Each helper runs `repair_share_step_1`, generating a set of `delta` values +//! to be sent to each helper (including themselves). +//! - Each helper runs `repair_share_step_2`, passing the received `delta` +//! values, generating a `sigma` value to be sent to the participant repairing +//! their share. +//! - The participant repairing their share runs `repair_share_step_3`, passing +//! all the received `sigma` values, recovering their lost `KeyPackage`. (They +//! will also need the `PublicKeyPackage` for this step which could be +//! provided by any of the helpers). use alloc::collections::{BTreeMap, BTreeSet}; use alloc::vec::Vec; +use crate::keys::{KeyPackage, PublicKeyPackage}; +use crate::serialization::SerializableScalar; use crate::{ - compute_lagrange_coefficient, Ciphersuite, CryptoRng, Error, Field, Group, Header, Identifier, - RngCore, Scalar, + compute_lagrange_coefficient, Ciphersuite, CryptoRng, Error, Field, Group, Identifier, RngCore, + Scalar, }; -use super::{generate_coefficients, SecretShare, SigningShare, VerifiableSecretSharingCommitment}; +use super::{generate_coefficients, SigningShare}; + +/// A delta value which is the output of step 1 of RTS. +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] +#[cfg_attr(feature = "serde", serde(transparent))] +pub struct Delta(pub(crate) SerializableScalar); + +impl Delta +where + C: Ciphersuite, +{ + /// Create a new [`Delta`] from a scalar. + #[cfg_attr(feature = "internals", visibility::make(pub))] + #[cfg_attr(docsrs, doc(cfg(feature = "internals")))] + pub(crate) fn new(scalar: Scalar) -> Self { + Self(SerializableScalar(scalar)) + } + + /// Get the inner scalar. + #[cfg_attr(feature = "internals", visibility::make(pub))] + #[cfg_attr(docsrs, doc(cfg(feature = "internals")))] + pub(crate) fn to_scalar(&self) -> Scalar { + self.0 .0 + } + + /// Deserialize from bytes + pub fn deserialize(bytes: &[u8]) -> Result> { + Ok(Self(SerializableScalar::deserialize(bytes)?)) + } + + /// Serialize to bytes + pub fn serialize(&self) -> Vec { + self.0.serialize() + } +} + +/// A sigma value which is the output of step 2 of RTS. +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] +#[cfg_attr(feature = "serde", serde(transparent))] +pub struct Sigma(pub(crate) SerializableScalar); + +impl Sigma +where + C: Ciphersuite, +{ + /// Create a new [`Sigma`] from a scalar. + #[cfg_attr(feature = "internals", visibility::make(pub))] + #[cfg_attr(docsrs, doc(cfg(feature = "internals")))] + pub(crate) fn new(scalar: Scalar) -> Self { + Self(SerializableScalar(scalar)) + } + + /// Get the inner scalar. + #[cfg_attr(feature = "internals", visibility::make(pub))] + #[cfg_attr(docsrs, doc(cfg(feature = "internals")))] + pub(crate) fn to_scalar(&self) -> Scalar { + self.0 .0 + } + + /// Deserialize from bytes + pub fn deserialize(bytes: &[u8]) -> Result> { + Ok(Self(SerializableScalar::deserialize(bytes)?)) + } + + /// Serialize to bytes + pub fn serialize(&self) -> Vec { + self.0.serialize() + } +} /// Step 1 of RTS. /// -/// Generates the "delta" values from `helper_i` to help `participant` recover their share -/// where `helpers` contains the identifiers of all the helpers (including `helper_i`), and `share_i` -/// is the share of `helper_i`. +/// Generates the "delta" values from the helper with `key_package_i` to send to +/// `helpers` (which includes the helper with `key_package_i`), to help +/// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. pub fn repair_share_step_1( helpers: &[Identifier], - share_i: &SecretShare, + key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, -) -> Result, Scalar>, Error> { +) -> Result, Delta>, Error> { if helpers.len() < 2 { - return Err(Error::InvalidMinSigners); - } - - if helpers.is_empty() { return Err(Error::IncorrectNumberOfIdentifiers); } + if !helpers.contains(&key_package_i.identifier) { + return Err(Error::UnknownIdentifier); + } let xset: BTreeSet<_> = helpers.iter().cloned().collect(); if xset.len() != helpers.len() { return Err(Error::DuplicatedIdentifier); @@ -42,7 +130,7 @@ pub fn repair_share_step_1( let rand_val: Vec> = generate_coefficients::(helpers.len() - 1, rng); - compute_last_random_value(&xset, share_i, &rand_val, participant) + compute_last_random_value(&xset, key_package_i, &rand_val, participant) } /// Compute the last delta value given the (generated uniformly at random) remaining ones @@ -51,19 +139,20 @@ pub fn repair_share_step_1( /// Returns a BTreeMap mapping which value should be sent to which participant. fn compute_last_random_value( helpers: &BTreeSet>, - share_i: &SecretShare, + key_package_i: &KeyPackage, random_values: &Vec>, participant: Identifier, -) -> Result, Scalar>, Error> { +) -> Result, Delta>, Error> { // Calculate Lagrange Coefficient for helper_i - let zeta_i = compute_lagrange_coefficient(helpers, Some(participant), share_i.identifier)?; + let zeta_i = + compute_lagrange_coefficient(helpers, Some(participant), key_package_i.identifier)?; - let lhs = zeta_i * share_i.signing_share.to_scalar(); + let lhs = zeta_i * key_package_i.signing_share.to_scalar(); - let mut out: BTreeMap, Scalar> = helpers + let mut out: BTreeMap, Delta> = helpers .iter() .copied() - .zip(random_values.iter().copied()) + .zip(random_values.iter().map(|v| Delta::new(*v))) .collect(); let mut sum_i_deltas = <::Field>::zero(); @@ -74,58 +163,56 @@ fn compute_last_random_value( out.insert( *helpers.last().ok_or(Error::IncorrectNumberOfIdentifiers)?, - lhs - sum_i_deltas, + Delta::new(lhs - sum_i_deltas), ); Ok(out) } -// Communication round -// -// `helper_i` sends 1 `delta_j` to all other helpers (j) -// `helper_i` retains 1 `delta_j` - /// Step 2 of RTS. /// -/// Generates the `sigma` values from all `deltas` received from `helpers` -/// to help `participant` recover their share. -/// `sigma` is the sum of all received `delta` and the `delta_i` generated for `helper_i`. -/// -/// Returns a scalar -pub fn repair_share_step_2(deltas_j: &[Scalar]) -> Scalar { +/// Generates the "sigma" value from all `deltas` received from all helpers. +/// The "sigma" value must be sent to the participant repairing their share. +pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { let mut sigma_j = <::Field>::zero(); - for d in deltas_j { - sigma_j = sigma_j + *d; + for d in deltas { + sigma_j = sigma_j + d.to_scalar(); } - sigma_j + Sigma::new(sigma_j) } -// Communication round -// -// `helper_j` sends 1 `sigma_j` to the `participant` repairing their share. - -/// Step 3 of RTS +/// Step 3 of RTS. +/// +/// The participant with the given `identifier` recovers their `KeyPackage` +/// with the "sigma" values received from all helpers and the `PublicKeyPackage` +/// of the group (which can be sent by any of the helpers). /// -/// The `participant` sums all `sigma_j` received to compute the `share`. The `SecretShare` -/// is made up of the `identifier`and `commitment` of the `participant` as well as the -/// `value` which is the `SigningShare`. +/// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. +/// This happens for `PublicKeyPackage`s created before the 3.0.0 release; +/// in that case, the user should set the `min_signers` field manually. pub fn repair_share_step_3( - sigmas: &[Scalar], + sigmas: &[Sigma], identifier: Identifier, - commitment: &VerifiableSecretSharingCommitment, -) -> SecretShare { + public_key_package: &PublicKeyPackage, +) -> Result, Error> { let mut share = <::Field>::zero(); for s in sigmas { - share = share + *s; + share = share + s.to_scalar(); } + let signing_share = SigningShare::new(share); + let verifying_share = signing_share.into(); - SecretShare { - header: Header::default(), + Ok(KeyPackage { + header: Default::default(), identifier, - signing_share: SigningShare::new(share), - commitment: commitment.clone(), - } + signing_share, + verifying_share, + verifying_key: *public_key_package.verifying_key(), + min_signers: public_key_package + .min_signers() + .ok_or(Error::InvalidMinSigners)?, + }) } diff --git a/frost-core/src/tests/repairable.rs b/frost-core/src/tests/repairable.rs index 4c1aaf8..4dd5986 100644 --- a/frost-core/src/tests/repairable.rs +++ b/frost-core/src/tests/repairable.rs @@ -7,13 +7,15 @@ use rand_core::{CryptoRng, RngCore}; use serde_json::Value; use crate as frost; +use crate::keys::repairable::{Delta, Sigma}; +use crate::keys::KeyPackage; use crate::{ compute_lagrange_coefficient, keys::{ repairable::{repair_share_step_1, repair_share_step_2, repair_share_step_3}, - PublicKeyPackage, SecretShare, SigningShare, + PublicKeyPackage, SecretShare, }, - Ciphersuite, Error, Field, Group, Identifier, Scalar, + Ciphersuite, Error, Field, Group, Identifier, }; /// We want to test that recovered share matches the original share @@ -26,24 +28,30 @@ pub fn check_rts(mut rng: R) { let max_signers = 5; let min_signers = 3; - let (shares, _pubkeys): (BTreeMap, SecretShare>, PublicKeyPackage) = - frost::keys::generate_with_dealer( - max_signers, - min_signers, - frost::keys::IdentifierList::Default, - &mut rng, - ) - .unwrap(); + let (shares, public_key_package): ( + BTreeMap, SecretShare>, + PublicKeyPackage, + ) = frost::keys::generate_with_dealer( + max_signers, + min_signers, + frost::keys::IdentifierList::Default, + &mut rng, + ) + .unwrap(); + let key_packages = shares + .into_iter() + .map(|(id, share)| (id, share.try_into().unwrap())) + .collect::, KeyPackage>>(); // Try to recover a share // Signer 2 will lose their share // Signer 1, 4 and 5 will help signer 2 to recover their share - let helper_1 = &shares[&Identifier::try_from(1).unwrap()]; - let helper_4 = &shares[&Identifier::try_from(4).unwrap()]; - let helper_5 = &shares[&Identifier::try_from(5).unwrap()]; - let participant = &shares[&Identifier::try_from(2).unwrap()]; + let helper_1 = &key_packages[&Identifier::try_from(1).unwrap()]; + let helper_4 = &key_packages[&Identifier::try_from(4).unwrap()]; + let helper_5 = &key_packages[&Identifier::try_from(5).unwrap()]; + let participant = &key_packages[&Identifier::try_from(2).unwrap()]; let helpers: [Identifier; 3] = [ helper_1.identifier, @@ -62,17 +70,17 @@ pub fn check_rts(mut rng: R) { // Each helper calculates their sigma from the random values received from the other helpers - let helper_1_sigma: Scalar = repair_share_step_2::(&[ + let helper_1_sigma: Sigma = repair_share_step_2::(&[ helper_1_deltas[&helpers[0]], helper_4_deltas[&helpers[0]], helper_5_deltas[&helpers[0]], ]); - let helper_4_sigma: Scalar = repair_share_step_2::(&[ + let helper_4_sigma: Sigma = repair_share_step_2::(&[ helper_1_deltas[&helpers[1]], helper_4_deltas[&helpers[1]], helper_5_deltas[&helpers[1]], ]); - let helper_5_sigma: Scalar = repair_share_step_2::(&[ + let helper_5_sigma: Sigma = repair_share_step_2::(&[ helper_1_deltas[&helpers[2]], helper_4_deltas[&helpers[2]], helper_5_deltas[&helpers[2]], @@ -83,10 +91,10 @@ pub fn check_rts(mut rng: R) { let participant_recovered_share = repair_share_step_3( &[helper_1_sigma, helper_4_sigma, helper_5_sigma], participant.identifier, - &participant.commitment, - ); + &public_key_package, + ) + .unwrap(); - // TODO: assert on commitment equality as well once updates have been made to VerifiableSecretSharingCommitment assert!(participant.signing_share() == participant_recovered_share.signing_share()) } @@ -114,14 +122,18 @@ pub fn check_repair_share_step_1(mut rng &mut rng, ) .unwrap(); + let key_packages = shares + .into_iter() + .map(|(id, share)| (id, share.try_into().unwrap())) + .collect::, KeyPackage>>(); // Signer 2 will lose their share // Signers (helpers) 1, 4 and 5 will help signer 2 (participant) to recover their share - let helper_1 = &shares[&Identifier::try_from(1).unwrap()]; - let helper_4 = &shares[&Identifier::try_from(4).unwrap()]; - let helper_5 = &shares[&Identifier::try_from(5).unwrap()]; - let participant = &shares[&Identifier::try_from(2).unwrap()]; + let helper_1 = &key_packages[&Identifier::try_from(1).unwrap()]; + let helper_4 = &key_packages[&Identifier::try_from(4).unwrap()]; + let helper_5 = &key_packages[&Identifier::try_from(5).unwrap()]; + let participant = &key_packages[&Identifier::try_from(2).unwrap()]; let helpers: [Identifier; 3] = [ helper_1.identifier, @@ -141,7 +153,7 @@ pub fn check_repair_share_step_1(mut rng let mut rhs = <::Field>::zero(); for (_k, v) in deltas { - rhs = rhs + v; + rhs = rhs + v.to_scalar(); } let lhs = lagrange_coefficient * helper_4.signing_share.to_scalar(); @@ -153,17 +165,20 @@ pub fn check_repair_share_step_1(mut rng pub fn check_repair_share_step_2(repair_share_helpers: &Value) { let values = &repair_share_helpers["scalar_generation"]; - let value_1 = - generate_scalar_from_byte_string::(values["random_scalar_1"].as_str().unwrap()); - let value_2 = - generate_scalar_from_byte_string::(values["random_scalar_2"].as_str().unwrap()); - let value_3 = - generate_scalar_from_byte_string::(values["random_scalar_3"].as_str().unwrap()); - - let expected: Scalar = repair_share_step_2::(&[value_1, value_2, value_3]); - - let actual: <::Field as Field>::Scalar = - generate_scalar_from_byte_string::(values["random_scalar_sum"].as_str().unwrap()); + let value_1 = Delta::new(generate_scalar_from_byte_string::( + values["random_scalar_1"].as_str().unwrap(), + )); + let value_2 = Delta::new(generate_scalar_from_byte_string::( + values["random_scalar_2"].as_str().unwrap(), + )); + let value_3 = Delta::new(generate_scalar_from_byte_string::( + values["random_scalar_3"].as_str().unwrap(), + )); + let expected = repair_share_step_2::(&[value_1, value_2, value_3]); + + let actual = Sigma::new(generate_scalar_from_byte_string::( + values["random_scalar_sum"].as_str().unwrap(), + )); assert!(actual == expected); } @@ -173,42 +188,46 @@ pub fn check_repair_share_step_3( mut rng: R, repair_share_helpers: &Value, ) { - // Generate shares + // We need a dummy public key package to call the function let max_signers = 5; let min_signers = 3; - let (shares, _pubkeys): (BTreeMap, SecretShare>, PublicKeyPackage) = - frost::keys::generate_with_dealer( - max_signers, - min_signers, - frost::keys::IdentifierList::Default, - &mut rng, - ) - .unwrap(); + let (_shares, public_key_package): ( + BTreeMap, SecretShare>, + PublicKeyPackage, + ) = frost::keys::generate_with_dealer( + max_signers, + min_signers, + frost::keys::IdentifierList::Default, + &mut rng, + ) + .unwrap(); let sigmas: &Value = &repair_share_helpers["sigma_generation"]; - let sigma_1 = generate_scalar_from_byte_string::(sigmas["sigma_1"].as_str().unwrap()); - let sigma_2 = generate_scalar_from_byte_string::(sigmas["sigma_2"].as_str().unwrap()); - let sigma_3 = generate_scalar_from_byte_string::(sigmas["sigma_3"].as_str().unwrap()); - let sigma_4 = generate_scalar_from_byte_string::(sigmas["sigma_4"].as_str().unwrap()); - - let commitment = (shares[&Identifier::try_from(1).unwrap()].commitment).clone(); - - let expected = repair_share_step_3::( + let sigma_1 = Sigma::new(generate_scalar_from_byte_string::( + sigmas["sigma_1"].as_str().unwrap(), + )); + let sigma_2 = Sigma::new(generate_scalar_from_byte_string::( + sigmas["sigma_2"].as_str().unwrap(), + )); + let sigma_3 = Sigma::new(generate_scalar_from_byte_string::( + sigmas["sigma_3"].as_str().unwrap(), + )); + let sigma_4 = Sigma::new(generate_scalar_from_byte_string::( + sigmas["sigma_4"].as_str().unwrap(), + )); + + let actual = repair_share_step_3::( &[sigma_1, sigma_2, sigma_3, sigma_4], Identifier::try_from(2).unwrap(), - &commitment, - ); + &public_key_package, + ) + .unwrap(); - let actual_sigma: <::Field as Field>::Scalar = + let expected: <::Field as Field>::Scalar = generate_scalar_from_byte_string::(sigmas["sigma_sum"].as_str().unwrap()); - let actual: SecretShare = SecretShare::new( - Identifier::try_from(2).unwrap(), - SigningShare::new(actual_sigma), - commitment, - ); - assert!(actual.signing_share == expected.signing_share); + assert!(expected == actual.signing_share().to_scalar()); } /// Test repair share step 1 fails with invalid numbers of signers. @@ -229,16 +248,20 @@ pub fn check_repair_share_step_1_fails_with_invalid_min_signers< &mut rng, ) .unwrap(); + let key_packages = shares + .into_iter() + .map(|(id, share)| (id, share.try_into().unwrap())) + .collect::, KeyPackage>>(); let helper = Identifier::try_from(3).unwrap(); let out = repair_share_step_1( &[helper], - &shares[&helper], + &key_packages[&helper], &mut rng, Identifier::try_from(2).unwrap(), ); assert!(out.is_err()); - assert!(out == Err(Error::InvalidMinSigners)) + assert!(out == Err(Error::IncorrectNumberOfIdentifiers)) } diff --git a/frost-ed25519/src/keys/repairable.rs b/frost-ed25519/src/keys/repairable.rs index f0df91b..0d9930f 100644 --- a/frost-ed25519/src/keys/repairable.rs +++ b/frost-ed25519/src/keys/repairable.rs @@ -6,52 +6,58 @@ use alloc::collections::BTreeMap; +use crate::keys::{KeyPackage, PublicKeyPackage}; // This is imported separately to make `gencode` work. // (if it were below, the position of the import would vary between ciphersuites // after `cargo fmt`) -use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar}; +use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore}; use crate::{Ed25519Sha512, Error}; -use super::{SecretShare, VerifiableSecretSharingCommitment}; +/// A delta value which is the output of step 1 of RTS. +pub type Delta = frost::keys::repairable::Delta; + +/// A sigma value which is the output of step 2 of RTS. +pub type Sigma = frost::keys::repairable::Sigma; /// Step 1 of RTS. /// -/// Generates the "delta" values from `helper_i` to help `participant` recover their share -/// where `helpers` contains the identifiers of all the helpers (including `helper_i`), and `share_i` -/// is the share of `helper_i`. +/// Generates the "delta" values from the helper with `key_package_i` to send to +/// `helpers` (which includes the helper with `key_package_i`), to help +/// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. pub fn repair_share_step_1( helpers: &[Identifier], - share_i: &SecretShare, + key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, -) -> Result, Error> { - frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant) +) -> Result, Error> { + frost::keys::repairable::repair_share_step_1(helpers, key_package_i, rng, participant) } /// Step 2 of RTS. /// -/// Generates the `sigma` values from all `deltas` received from `helpers` -/// to help `participant` recover their share. -/// `sigma` is the sum of all received `delta` and the `delta_i` generated for `helper_i`. -/// -/// Returns a scalar -pub fn repair_share_step_2(deltas_j: &[Scalar]) -> Scalar { - frost::keys::repairable::repair_share_step_2::(deltas_j) +/// Generates the "sigma" value from all `deltas` received from all helpers. +/// The "sigma" value must be sent to the participant repairing their share. +pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { + frost::keys::repairable::repair_share_step_2::(deltas) } -/// Step 3 of RTS +/// Step 3 of RTS. +/// +/// The participant with the given `identifier` recovers their `KeyPackage` +/// with the "sigma" values received from all helpers and the `PublicKeyPackage` +/// of the group (which can be sent by any of the helpers). /// -/// The `participant` sums all `sigma_j` received to compute the `share`. The `SecretShare` -/// is made up of the `identifier`and `commitment` of the `participant` as well as the -/// `value` which is the `SigningShare`. +/// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. +/// This happens for `PublicKeyPackage`s created before the 3.0.0 release; +/// in that case, the user should set the `min_signers` field manually. pub fn repair_share_step_3( - sigmas: &[Scalar], + sigmas: &[Sigma], identifier: Identifier, - commitment: &VerifiableSecretSharingCommitment, -) -> SecretShare { - frost::keys::repairable::repair_share_step_3(sigmas, identifier, commitment) + public_key_package: &PublicKeyPackage, +) -> Result { + frost::keys::repairable::repair_share_step_3(sigmas, identifier, public_key_package) } #[cfg(test)] diff --git a/frost-ed448/src/keys/repairable.rs b/frost-ed448/src/keys/repairable.rs index 7bbdeca..2aa9366 100644 --- a/frost-ed448/src/keys/repairable.rs +++ b/frost-ed448/src/keys/repairable.rs @@ -6,52 +6,58 @@ use alloc::collections::BTreeMap; +use crate::keys::{KeyPackage, PublicKeyPackage}; // This is imported separately to make `gencode` work. // (if it were below, the position of the import would vary between ciphersuites // after `cargo fmt`) -use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar}; +use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore}; use crate::{Ed448Shake256, Error}; -use super::{SecretShare, VerifiableSecretSharingCommitment}; +/// A delta value which is the output of step 1 of RTS. +pub type Delta = frost::keys::repairable::Delta; + +/// A sigma value which is the output of step 2 of RTS. +pub type Sigma = frost::keys::repairable::Sigma; /// Step 1 of RTS. /// -/// Generates the "delta" values from `helper_i` to help `participant` recover their share -/// where `helpers` contains the identifiers of all the helpers (including `helper_i`), and `share_i` -/// is the share of `helper_i`. +/// Generates the "delta" values from the helper with `key_package_i` to send to +/// `helpers` (which includes the helper with `key_package_i`), to help +/// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. pub fn repair_share_step_1( helpers: &[Identifier], - share_i: &SecretShare, + key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, -) -> Result, Error> { - frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant) +) -> Result, Error> { + frost::keys::repairable::repair_share_step_1(helpers, key_package_i, rng, participant) } /// Step 2 of RTS. /// -/// Generates the `sigma` values from all `deltas` received from `helpers` -/// to help `participant` recover their share. -/// `sigma` is the sum of all received `delta` and the `delta_i` generated for `helper_i`. -/// -/// Returns a scalar -pub fn repair_share_step_2(deltas_j: &[Scalar]) -> Scalar { - frost::keys::repairable::repair_share_step_2::(deltas_j) +/// Generates the "sigma" value from all `deltas` received from all helpers. +/// The "sigma" value must be sent to the participant repairing their share. +pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { + frost::keys::repairable::repair_share_step_2::(deltas) } -/// Step 3 of RTS +/// Step 3 of RTS. +/// +/// The participant with the given `identifier` recovers their `KeyPackage` +/// with the "sigma" values received from all helpers and the `PublicKeyPackage` +/// of the group (which can be sent by any of the helpers). /// -/// The `participant` sums all `sigma_j` received to compute the `share`. The `SecretShare` -/// is made up of the `identifier`and `commitment` of the `participant` as well as the -/// `value` which is the `SigningShare`. +/// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. +/// This happens for `PublicKeyPackage`s created before the 3.0.0 release; +/// in that case, the user should set the `min_signers` field manually. pub fn repair_share_step_3( - sigmas: &[Scalar], + sigmas: &[Sigma], identifier: Identifier, - commitment: &VerifiableSecretSharingCommitment, -) -> SecretShare { - frost::keys::repairable::repair_share_step_3(sigmas, identifier, commitment) + public_key_package: &PublicKeyPackage, +) -> Result { + frost::keys::repairable::repair_share_step_3(sigmas, identifier, public_key_package) } #[cfg(test)] diff --git a/frost-p256/src/keys/repairable.rs b/frost-p256/src/keys/repairable.rs index 484a9fc..c691b94 100644 --- a/frost-p256/src/keys/repairable.rs +++ b/frost-p256/src/keys/repairable.rs @@ -6,52 +6,58 @@ use alloc::collections::BTreeMap; +use crate::keys::{KeyPackage, PublicKeyPackage}; // This is imported separately to make `gencode` work. // (if it were below, the position of the import would vary between ciphersuites // after `cargo fmt`) -use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar}; +use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore}; use crate::{Error, P256Sha256}; -use super::{SecretShare, VerifiableSecretSharingCommitment}; +/// A delta value which is the output of step 1 of RTS. +pub type Delta = frost::keys::repairable::Delta; + +/// A sigma value which is the output of step 2 of RTS. +pub type Sigma = frost::keys::repairable::Sigma; /// Step 1 of RTS. /// -/// Generates the "delta" values from `helper_i` to help `participant` recover their share -/// where `helpers` contains the identifiers of all the helpers (including `helper_i`), and `share_i` -/// is the share of `helper_i`. +/// Generates the "delta" values from the helper with `key_package_i` to send to +/// `helpers` (which includes the helper with `key_package_i`), to help +/// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. pub fn repair_share_step_1( helpers: &[Identifier], - share_i: &SecretShare, + key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, -) -> Result, Error> { - frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant) +) -> Result, Error> { + frost::keys::repairable::repair_share_step_1(helpers, key_package_i, rng, participant) } /// Step 2 of RTS. /// -/// Generates the `sigma` values from all `deltas` received from `helpers` -/// to help `participant` recover their share. -/// `sigma` is the sum of all received `delta` and the `delta_i` generated for `helper_i`. -/// -/// Returns a scalar -pub fn repair_share_step_2(deltas_j: &[Scalar]) -> Scalar { - frost::keys::repairable::repair_share_step_2::(deltas_j) +/// Generates the "sigma" value from all `deltas` received from all helpers. +/// The "sigma" value must be sent to the participant repairing their share. +pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { + frost::keys::repairable::repair_share_step_2::(deltas) } -/// Step 3 of RTS +/// Step 3 of RTS. +/// +/// The participant with the given `identifier` recovers their `KeyPackage` +/// with the "sigma" values received from all helpers and the `PublicKeyPackage` +/// of the group (which can be sent by any of the helpers). /// -/// The `participant` sums all `sigma_j` received to compute the `share`. The `SecretShare` -/// is made up of the `identifier`and `commitment` of the `participant` as well as the -/// `value` which is the `SigningShare`. +/// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. +/// This happens for `PublicKeyPackage`s created before the 3.0.0 release; +/// in that case, the user should set the `min_signers` field manually. pub fn repair_share_step_3( - sigmas: &[Scalar], + sigmas: &[Sigma], identifier: Identifier, - commitment: &VerifiableSecretSharingCommitment, -) -> SecretShare { - frost::keys::repairable::repair_share_step_3(sigmas, identifier, commitment) + public_key_package: &PublicKeyPackage, +) -> Result { + frost::keys::repairable::repair_share_step_3(sigmas, identifier, public_key_package) } #[cfg(test)] diff --git a/frost-ristretto255/src/keys/repairable.rs b/frost-ristretto255/src/keys/repairable.rs index e3d20a7..45bc27e 100644 --- a/frost-ristretto255/src/keys/repairable.rs +++ b/frost-ristretto255/src/keys/repairable.rs @@ -6,52 +6,58 @@ use alloc::collections::BTreeMap; +use crate::keys::{KeyPackage, PublicKeyPackage}; // This is imported separately to make `gencode` work. // (if it were below, the position of the import would vary between ciphersuites // after `cargo fmt`) -use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar}; +use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore}; use crate::{Error, Ristretto255Sha512}; -use super::{SecretShare, VerifiableSecretSharingCommitment}; +/// A delta value which is the output of step 1 of RTS. +pub type Delta = frost::keys::repairable::Delta; + +/// A sigma value which is the output of step 2 of RTS. +pub type Sigma = frost::keys::repairable::Sigma; /// Step 1 of RTS. /// -/// Generates the "delta" values from `helper_i` to help `participant` recover their share -/// where `helpers` contains the identifiers of all the helpers (including `helper_i`), and `share_i` -/// is the share of `helper_i`. +/// Generates the "delta" values from the helper with `key_package_i` to send to +/// `helpers` (which includes the helper with `key_package_i`), to help +/// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. pub fn repair_share_step_1( helpers: &[Identifier], - share_i: &SecretShare, + key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, -) -> Result, Error> { - frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant) +) -> Result, Error> { + frost::keys::repairable::repair_share_step_1(helpers, key_package_i, rng, participant) } /// Step 2 of RTS. /// -/// Generates the `sigma` values from all `deltas` received from `helpers` -/// to help `participant` recover their share. -/// `sigma` is the sum of all received `delta` and the `delta_i` generated for `helper_i`. -/// -/// Returns a scalar -pub fn repair_share_step_2(deltas_j: &[Scalar]) -> Scalar { - frost::keys::repairable::repair_share_step_2::(deltas_j) +/// Generates the "sigma" value from all `deltas` received from all helpers. +/// The "sigma" value must be sent to the participant repairing their share. +pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { + frost::keys::repairable::repair_share_step_2::(deltas) } -/// Step 3 of RTS +/// Step 3 of RTS. +/// +/// The participant with the given `identifier` recovers their `KeyPackage` +/// with the "sigma" values received from all helpers and the `PublicKeyPackage` +/// of the group (which can be sent by any of the helpers). /// -/// The `participant` sums all `sigma_j` received to compute the `share`. The `SecretShare` -/// is made up of the `identifier`and `commitment` of the `participant` as well as the -/// `value` which is the `SigningShare`. +/// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. +/// This happens for `PublicKeyPackage`s created before the 3.0.0 release; +/// in that case, the user should set the `min_signers` field manually. pub fn repair_share_step_3( - sigmas: &[Scalar], + sigmas: &[Sigma], identifier: Identifier, - commitment: &VerifiableSecretSharingCommitment, -) -> SecretShare { - frost::keys::repairable::repair_share_step_3(sigmas, identifier, commitment) + public_key_package: &PublicKeyPackage, +) -> Result { + frost::keys::repairable::repair_share_step_3(sigmas, identifier, public_key_package) } #[cfg(test)] diff --git a/frost-secp256k1-tr/src/keys/repairable.rs b/frost-secp256k1-tr/src/keys/repairable.rs index 4454335..0db8627 100644 --- a/frost-secp256k1-tr/src/keys/repairable.rs +++ b/frost-secp256k1-tr/src/keys/repairable.rs @@ -6,52 +6,58 @@ use alloc::collections::BTreeMap; +use crate::keys::{KeyPackage, PublicKeyPackage}; // This is imported separately to make `gencode` work. // (if it were below, the position of the import would vary between ciphersuites // after `cargo fmt`) -use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar}; +use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore}; use crate::{Error, Secp256K1Sha256TR}; -use super::{SecretShare, VerifiableSecretSharingCommitment}; +/// A delta value which is the output of step 1 of RTS. +pub type Delta = frost::keys::repairable::Delta; + +/// A sigma value which is the output of step 2 of RTS. +pub type Sigma = frost::keys::repairable::Sigma; /// Step 1 of RTS. /// -/// Generates the "delta" values from `helper_i` to help `participant` recover their share -/// where `helpers` contains the identifiers of all the helpers (including `helper_i`), and `share_i` -/// is the share of `helper_i`. +/// Generates the "delta" values from the helper with `key_package_i` to send to +/// `helpers` (which includes the helper with `key_package_i`), to help +/// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. pub fn repair_share_step_1( helpers: &[Identifier], - share_i: &SecretShare, + key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, -) -> Result, Error> { - frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant) +) -> Result, Error> { + frost::keys::repairable::repair_share_step_1(helpers, key_package_i, rng, participant) } /// Step 2 of RTS. /// -/// Generates the `sigma` values from all `deltas` received from `helpers` -/// to help `participant` recover their share. -/// `sigma` is the sum of all received `delta` and the `delta_i` generated for `helper_i`. -/// -/// Returns a scalar -pub fn repair_share_step_2(deltas_j: &[Scalar]) -> Scalar { - frost::keys::repairable::repair_share_step_2::(deltas_j) +/// Generates the "sigma" value from all `deltas` received from all helpers. +/// The "sigma" value must be sent to the participant repairing their share. +pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { + frost::keys::repairable::repair_share_step_2::(deltas) } -/// Step 3 of RTS +/// Step 3 of RTS. +/// +/// The participant with the given `identifier` recovers their `KeyPackage` +/// with the "sigma" values received from all helpers and the `PublicKeyPackage` +/// of the group (which can be sent by any of the helpers). /// -/// The `participant` sums all `sigma_j` received to compute the `share`. The `SecretShare` -/// is made up of the `identifier`and `commitment` of the `participant` as well as the -/// `value` which is the `SigningShare`. +/// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. +/// This happens for `PublicKeyPackage`s created before the 3.0.0 release; +/// in that case, the user should set the `min_signers` field manually. pub fn repair_share_step_3( - sigmas: &[Scalar], + sigmas: &[Sigma], identifier: Identifier, - commitment: &VerifiableSecretSharingCommitment, -) -> SecretShare { - frost::keys::repairable::repair_share_step_3(sigmas, identifier, commitment) + public_key_package: &PublicKeyPackage, +) -> Result { + frost::keys::repairable::repair_share_step_3(sigmas, identifier, public_key_package) } #[cfg(test)] diff --git a/frost-secp256k1/src/keys/repairable.rs b/frost-secp256k1/src/keys/repairable.rs index 98a2e7c..22ef03d 100644 --- a/frost-secp256k1/src/keys/repairable.rs +++ b/frost-secp256k1/src/keys/repairable.rs @@ -6,52 +6,58 @@ use alloc::collections::BTreeMap; +use crate::keys::{KeyPackage, PublicKeyPackage}; // This is imported separately to make `gencode` work. // (if it were below, the position of the import would vary between ciphersuites // after `cargo fmt`) -use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar}; +use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore}; use crate::{Error, Secp256K1Sha256}; -use super::{SecretShare, VerifiableSecretSharingCommitment}; +/// A delta value which is the output of step 1 of RTS. +pub type Delta = frost::keys::repairable::Delta; + +/// A sigma value which is the output of step 2 of RTS. +pub type Sigma = frost::keys::repairable::Sigma; /// Step 1 of RTS. /// -/// Generates the "delta" values from `helper_i` to help `participant` recover their share -/// where `helpers` contains the identifiers of all the helpers (including `helper_i`), and `share_i` -/// is the share of `helper_i`. +/// Generates the "delta" values from the helper with `key_package_i` to send to +/// `helpers` (which includes the helper with `key_package_i`), to help +/// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. pub fn repair_share_step_1( helpers: &[Identifier], - share_i: &SecretShare, + key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, -) -> Result, Error> { - frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant) +) -> Result, Error> { + frost::keys::repairable::repair_share_step_1(helpers, key_package_i, rng, participant) } /// Step 2 of RTS. /// -/// Generates the `sigma` values from all `deltas` received from `helpers` -/// to help `participant` recover their share. -/// `sigma` is the sum of all received `delta` and the `delta_i` generated for `helper_i`. -/// -/// Returns a scalar -pub fn repair_share_step_2(deltas_j: &[Scalar]) -> Scalar { - frost::keys::repairable::repair_share_step_2::(deltas_j) +/// Generates the "sigma" value from all `deltas` received from all helpers. +/// The "sigma" value must be sent to the participant repairing their share. +pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { + frost::keys::repairable::repair_share_step_2::(deltas) } -/// Step 3 of RTS +/// Step 3 of RTS. +/// +/// The participant with the given `identifier` recovers their `KeyPackage` +/// with the "sigma" values received from all helpers and the `PublicKeyPackage` +/// of the group (which can be sent by any of the helpers). /// -/// The `participant` sums all `sigma_j` received to compute the `share`. The `SecretShare` -/// is made up of the `identifier`and `commitment` of the `participant` as well as the -/// `value` which is the `SigningShare`. +/// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. +/// This happens for `PublicKeyPackage`s created before the 3.0.0 release; +/// in that case, the user should set the `min_signers` field manually. pub fn repair_share_step_3( - sigmas: &[Scalar], + sigmas: &[Sigma], identifier: Identifier, - commitment: &VerifiableSecretSharingCommitment, -) -> SecretShare { - frost::keys::repairable::repair_share_step_3(sigmas, identifier, commitment) + public_key_package: &PublicKeyPackage, +) -> Result { + frost::keys::repairable::repair_share_step_3(sigmas, identifier, public_key_package) } #[cfg(test)] From 5cac0f44979a9913862469f16ee7c242b2dfb8d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:50:46 +0000 Subject: [PATCH 57/86] build(deps): bump reviewdog/action-actionlint from 1.69.0 to 1.69.1 (#965) Bumps [reviewdog/action-actionlint](https://github.com/reviewdog/action-actionlint) from 1.69.0 to 1.69.1. - [Release notes](https://github.com/reviewdog/action-actionlint/releases) - [Commits](https://github.com/reviewdog/action-actionlint/compare/v1.69.0...v1.69.1) --- updated-dependencies: - dependency-name: reviewdog/action-actionlint dependency-version: 1.69.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index edf48e5..6a65047 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -176,7 +176,7 @@ jobs: continue-on-error: true steps: - uses: actions/checkout@v6.0.0 - - uses: reviewdog/action-actionlint@v1.69.0 + - uses: reviewdog/action-actionlint@v1.69.1 with: level: warning fail_level: none From 9637346d3911c67474ca2d75514fe7b1461c2f03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:50:50 +0000 Subject: [PATCH 58/86] build(deps): bump codecov/codecov-action from 5.5.1 to 5.5.2 (#982) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.5.1 to 5.5.2. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v5.5.1...v5.5.2) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 5.5.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/coverage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 200f088..b3325cf 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -41,4 +41,4 @@ jobs: run: cargo llvm-cov report --lcov --ignore-filename-regex '.*(tests).*|benches.rs|gencode|helpers.rs|interoperability_tests.rs' --output-path lcov.info - name: Upload coverage report to Codecov - uses: codecov/codecov-action@v5.5.1 + uses: codecov/codecov-action@v5.5.2 From e6a57b331a568c151cbc3ca1a710487f2bbd7f07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 14:26:49 +0000 Subject: [PATCH 59/86] build(deps): bump actions/checkout from 6.0.0 to 6.0.1 (#966) Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.0 to 6.0.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6.0.0...v6.0.1) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/coverage.yaml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/main.yml | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index b3325cf..8e2786f 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -23,7 +23,7 @@ jobs: RUST_BACKTRACE: full steps: - - uses: actions/checkout@v6.0.0 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index eb62d5d..d5345c2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -36,7 +36,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the source code - uses: actions/checkout@v6.0.0 + uses: actions/checkout@v6.0.2 with: persist-credentials: false diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6a65047..6d2659d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.0 + - uses: actions/checkout@v6.0.2 - uses: dtolnay/rust-toolchain@beta - run: cargo build @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.0 + - uses: actions/checkout@v6.0.2 - uses: dtolnay/rust-toolchain@stable - run: cargo update && cargo build --all-features @@ -32,7 +32,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.0 + - uses: actions/checkout@v6.0.2 # Re-resolve Cargo.lock with minimal versions. # This only works with nightly. We pin to a specific version because # newer versions use lock file version 4, but the MSRV cargo does not @@ -54,7 +54,7 @@ jobs: # runs-on: ubuntu-latest # steps: - # - uses: actions/checkout@v6.0.0 + # - uses: actions/checkout@v6.0.2 # - uses: dtolnay/rust-toolchain@stable # - run: cargo install cargo-all-features # # We check and then test because some test dependencies could help @@ -74,7 +74,7 @@ jobs: matrix: crate: [ristretto255, ed25519, p256, secp256k1, secp256k1-tr, rerandomized] steps: - - uses: actions/checkout@v6.0.0 + - uses: actions/checkout@v6.0.2 - uses: dtolnay/rust-toolchain@master with: toolchain: stable @@ -87,7 +87,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.0 + - uses: actions/checkout@v6.0.2 - uses: dtolnay/rust-toolchain@beta - run: cargo test --release --all-features @@ -96,7 +96,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.0 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false @@ -127,7 +127,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.0 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false @@ -144,7 +144,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.0 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false @@ -163,7 +163,7 @@ jobs: RUSTDOCFLAGS: -D warnings steps: - - uses: actions/checkout@v6.0.0 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false @@ -175,7 +175,7 @@ jobs: runs-on: ubuntu-latest continue-on-error: true steps: - - uses: actions/checkout@v6.0.0 + - uses: actions/checkout@v6.0.2 - uses: reviewdog/action-actionlint@v1.69.1 with: level: warning From bac329797305d7da05528925a5b73b6381eab7e1 Mon Sep 17 00:00:00 2001 From: Conrado Date: Fri, 23 Jan 2026 11:33:56 -0300 Subject: [PATCH 60/86] frost-rerandomized: change Randomizer generation (#762) * frost-rerandomized: change Randomizer generation to take SigningCommitments and not depend on serialization feature * ci: change doc warnings to errors * fix doc warnings * fix `one` variable naming --- frost-core/src/round1.rs | 3 + frost-rerandomized/src/lib.rs | 162 ++++++++++++++++++++++++++++++-- frost-rerandomized/src/tests.rs | 61 ++++++++++-- 3 files changed, 212 insertions(+), 14 deletions(-) diff --git a/frost-core/src/round1.rs b/frost-core/src/round1.rs index b6f2049..e37c249 100644 --- a/frost-core/src/round1.rs +++ b/frost-core/src/round1.rs @@ -396,6 +396,9 @@ impl GroupCommitmentShare { /// commitment list. /// /// [`encode_group_commitment_list()`]: https://datatracker.ietf.org/doc/html/rfc9591#name-list-operations +#[cfg(feature = "internals")] +#[cfg_attr(feature = "internals", visibility::make(pub))] +#[cfg_attr(docsrs, doc(cfg(feature = "internals")))] pub(super) fn encode_group_commitments( signing_commitments: &BTreeMap, SigningCommitments>, ) -> Result, Error> { diff --git a/frost-rerandomized/src/lib.rs b/frost-rerandomized/src/lib.rs index 4099893..a88e93c 100644 --- a/frost-rerandomized/src/lib.rs +++ b/frost-rerandomized/src/lib.rs @@ -3,10 +3,13 @@ //! To sign with re-randomized FROST: //! //! - Do Round 1 the same way as regular FROST; -//! - The Coordinator should call [`RandomizedParams::new()`] and send -//! the [`RandomizedParams::randomizer`] to all participants, using a -//! confidential channel, along with the regular [`frost::SigningPackage`]; -//! - Each participant should call [`sign`] and send the resulting +//! - The Coordinator should call [`RandomizedParams::new_from_commitments()`] +//! and send the generate randomizer seed (the second returned value) to all +//! participants, using a confidential channel, along with the regular +//! [`frost::SigningPackage`]; +//! - Each participant should regenerate the RandomizerParams by calling +//! [`RandomizedParams::regenerate_from_seed_and_commitments()`], which they +//! should pass to [`sign_with_randomizer_seed()`] and send the resulting //! [`frost::round2::SignatureShare`] back to the Coordinator; //! - The Coordinator should then call [`aggregate`]. #![no_std] @@ -27,8 +30,10 @@ use frost_core::SigningPackage; use frost_core::{ self as frost, keys::{KeyPackage, PublicKeyPackage, SigningShare, VerifyingShare}, + round1::encode_group_commitments, + round1::SigningCommitments, serialization::SerializableScalar, - Ciphersuite, Error, Field, Group, Scalar, VerifyingKey, + Ciphersuite, Error, Field, Group, Identifier, Scalar, VerifyingKey, }; #[cfg(feature = "serde")] @@ -36,7 +41,6 @@ use frost_core::serde; // When pulled into `reddsa`, that has its own sibling `rand_core` import. // For the time being, we do not re-export this `rand_core`. -#[cfg(feature = "serialization")] use rand_core::{CryptoRng, RngCore}; /// Randomize the given key type for usage in a FROST signing with re-randomized keys, @@ -123,6 +127,9 @@ impl Randomize for PublicKeyPackage { /// be sent from the Coordinator using a confidential channel. /// /// See [`frost::round2::sign`] for documentation on the other parameters. +#[deprecated( + note = "switch to sign_with_randomizer_seed(), passing a seed generated with RandomizedParams::new_from_commitments()" +)] pub fn sign( signing_package: &frost::SigningPackage, signer_nonces: &frost::round1::SigningNonces, @@ -135,6 +142,25 @@ pub fn sign( frost::round2::sign(signing_package, signer_nonces, &randomized_key_package) } +/// Re-randomized FROST signing using the given `randomizer_seed`, which should +/// be sent from the Coordinator using a confidential channel. +/// +/// See [`frost::round2::sign`] for documentation on the other parameters. +pub fn sign_with_randomizer_seed( + signing_package: &frost::SigningPackage, + signer_nonces: &frost::round1::SigningNonces, + key_package: &frost::keys::KeyPackage, + randomizer_seed: &[u8], +) -> Result, Error> { + let randomized_params = RandomizedParams::regenerate_from_seed_and_commitments( + key_package.verifying_key(), + randomizer_seed, + signing_package.signing_commitments(), + )?; + let randomized_key_package = key_package.randomize(&randomized_params)?; + frost::round2::sign(signing_package, signer_nonces, &randomized_key_package) +} + /// Re-randomized FROST signature share aggregation with the given [`RandomizedParams`], /// which can be computed from the previously generated randomizer using /// [`RandomizedParams::from_randomizer`]. @@ -178,12 +204,15 @@ impl Randomizer where C: RandomizedCiphersuite, { - /// Create a new random Randomizer. + /// Create a new random Randomizer using a SigningPackage for randomness. /// /// The [`SigningPackage`] must be the signing package being used in the /// current FROST signing run. It is hashed into the randomizer calculation, /// which binds it to that specific package. #[cfg(feature = "serialization")] + #[deprecated( + note = "switch to new_from_commitments(), passing the commitments from SigningPackage" + )] pub fn new( mut rng: R, signing_package: &SigningPackage, @@ -212,6 +241,65 @@ where .ok_or(Error::SerializationError)?; Ok(Self(SerializableScalar(randomizer))) } + + /// Create a new random Randomizer using SigningCommitments for randomness. + /// + /// The [`SigningCommitments`] map must be the one being used in the current + /// FROST signing run (built by the Coordinator after receiving from + /// Participants). It is hashed into the randomizer calculation, which binds + /// it to that specific commitments. + /// + /// Returns the Randomizer and the generate randomizer seed. Both can be + /// used to regenerate the Randomizer with + /// [`Self::regenerate_from_seed_and_commitments()`]. + pub fn new_from_commitments( + mut rng: R, + signing_commitments: &BTreeMap, SigningCommitments>, + ) -> Result<(Self, Vec), Error> { + // Generate a dummy scalar to get its encoded size + let zero = <::Field as Field>::zero(); + let ns = <::Field as Field>::serialize(&zero) + .as_ref() + .len(); + let mut randomizer_seed = alloc::vec![0; ns]; + rng.fill_bytes(&mut randomizer_seed); + Ok(( + Self::regenerate_from_seed_and_commitments(&randomizer_seed, signing_commitments)?, + randomizer_seed, + )) + } + + /// Regenerates a Randomizer generated with + /// [`Self::new_from_commitments()`]. This can be used by Participants after + /// receiving the randomizer seed and commitments in Round 2. This is better + /// than the Coordinator simply generating a Randomizer and sending it to + /// Participants, because in this approach the participants don't need to + /// fully trust the Coordinator's random number generator (i.e. even if the + /// randomizer seed was not randomly generated the randomizer will still + /// be). + /// + /// This should be used exclusively with the output of + /// [`Self::new_from_commitments()`]; it is strongly suggested to not + /// attempt generating the randomizer seed yourself (even if the point of + /// this approach is to hedge against issues in the randomizer seed + /// generation). + pub fn regenerate_from_seed_and_commitments( + randomizer_seed: &[u8], + signing_commitments: &BTreeMap, SigningCommitments>, + ) -> Result, Error> + where + C: RandomizedCiphersuite, + { + let randomizer = C::hash_randomizer( + &[ + randomizer_seed, + &encode_group_commitments(signing_commitments)?, + ] + .concat(), + ) + .ok_or(Error::SerializationError)?; + Ok(Self(SerializableScalar(randomizer))) + } } impl Randomizer @@ -267,18 +355,76 @@ where C: RandomizedCiphersuite, { /// Create a new [`RandomizedParams`] for the given [`VerifyingKey`] and - /// the given `participants`. + /// the given [`SigningPackage`]. #[cfg(feature = "serialization")] + #[deprecated( + note = "switch to new_from_commitments(), passing the commitments from SigningPackage" + )] pub fn new( group_verifying_key: &VerifyingKey, signing_package: &SigningPackage, rng: R, ) -> Result> { + #[allow(deprecated)] Ok(Self::from_randomizer( group_verifying_key, Randomizer::new(rng, signing_package)?, )) } + + /// Create a new [`RandomizedParams`] for the given [`VerifyingKey`] and the + /// given signing commitments. + /// + /// The [`SigningCommitments`] map must be the one being used in the current + /// FROST signing run (built by the Coordinator after receiving from + /// Participants). It is hashed into the randomizer calculation, which binds + /// it to that specific commitments. + /// + /// Returns the generated [`RandomizedParams`] and a randomizer seed. Both + /// can be used to regenerate the [`RandomizedParams`] with + /// [`Self::regenerate_from_seed_and_commitments()`]. + pub fn new_from_commitments( + group_verifying_key: &VerifyingKey, + signing_commitments: &BTreeMap, SigningCommitments>, + rng: R, + ) -> Result<(Self, Vec), Error> { + let (randomizer, randomizer_seed) = + Randomizer::new_from_commitments(rng, signing_commitments)?; + Ok(( + Self::from_randomizer(group_verifying_key, randomizer), + randomizer_seed, + )) + } + + /// Regenerate a [`RandomizedParams`] with the given [`VerifyingKey`] from + /// the given given signing commitments. + /// + /// Returns the generated [`RandomizedParams`] and a randomizer seed, which + /// can be used to regenerate the [`RandomizedParams`]. + /// + /// Regenerates a [`RandomizedParams`] generated with + /// [`Self::new_from_commitments()`]. This can be used by Participants after + /// receiving the randomizer seed and commitments in Round 2. This is better + /// than the Coordinator simply generating a [`Randomizer`] and sending it + /// to Participants, because in this approach the participants don't need to + /// fully trust the Coordinator's random number generator (i.e. even if the + /// randomizer seed was not randomly generated the randomizer will still + /// be). + /// + /// This should be used exclusively with the output of + /// [`Self::new_from_commitments()`]; it is strongly suggested to not + /// attempt generating the randomizer seed yourself (even if the point of + /// this approach is to hedge against issues in the randomizer seed + /// generation). + pub fn regenerate_from_seed_and_commitments( + group_verifying_key: &VerifyingKey, + randomizer_seed: &[u8], + signing_commitments: &BTreeMap, SigningCommitments>, + ) -> Result> { + let randomizer = + Randomizer::regenerate_from_seed_and_commitments(randomizer_seed, signing_commitments)?; + Ok(Self::from_randomizer(group_verifying_key, randomizer)) + } } impl RandomizedParams diff --git a/frost-rerandomized/src/tests.rs b/frost-rerandomized/src/tests.rs index f29c91c..0b52649 100644 --- a/frost-rerandomized/src/tests.rs +++ b/frost-rerandomized/src/tests.rs @@ -6,7 +6,9 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; use crate::{frost_core as frost, RandomizedCiphersuite, RandomizedParams, Randomizer}; -use frost_core::{Field, Group, Signature, SigningPackage, VerifyingKey}; +use frost_core::{ + round1::SigningCommitments, Field, Group, Identifier, Signature, SigningPackage, VerifyingKey, +}; use rand_core::{CryptoRng, RngCore}; /// Test re-randomized FROST signing with trusted dealer with a Ciphersuite. @@ -70,9 +72,12 @@ pub fn check_randomized_sign_with_dealer( check_from_randomizer(&mut rng, signing_package, pubkeys); check_from_randomizer_and_signing_package(&mut rng, signing_package); + + check_from_seed_and_signing_commitments(&mut rng, signing_package.signing_commitments()); } fn check_from_randomizer( @@ -138,6 +150,7 @@ fn check_from_randomizer( signing_package: &SigningPackage, pubkeys: &frost::keys::PublicKeyPackage, ) { + #[allow(deprecated)] let randomizer = Randomizer::new(rng, signing_package).unwrap(); let randomizer_params = RandomizedParams::from_randomizer(pubkeys.verifying_key(), randomizer); @@ -176,3 +189,39 @@ fn check_from_randomizer_and_signing_package( + mut rng: &mut R, + signing_commitments: &BTreeMap, SigningCommitments>, +) { + // Make sure regeneration returns the same Randomizer. + let (randomizer1, randomizer_seed1) = + Randomizer::new_from_commitments(&mut rng, signing_commitments).unwrap(); + let randomizer2 = + Randomizer::regenerate_from_seed_and_commitments(&randomizer_seed1, signing_commitments) + .unwrap(); + assert!(randomizer1 == randomizer2); + + let (randomizer2, randomizer_seed2) = + Randomizer::new_from_commitments(&mut rng, signing_commitments).unwrap(); + + // Make sure that different rng_randomizers lead to different randomizers + assert!(randomizer1 != randomizer2); + assert!(randomizer_seed1 != randomizer_seed2); + + // Modify the commitments map, by overwriting the first entry with the value + // of the last entry. + let mut modified_signing_commitments = signing_commitments.clone(); + modified_signing_commitments + .first_entry() + .unwrap() + .insert(*signing_commitments.last_key_value().unwrap().1); + let randomizer2 = Randomizer::regenerate_from_seed_and_commitments( + &randomizer_seed1, + &modified_signing_commitments, + ) + .unwrap(); + + // Make sure that different packages lead to different randomizers + assert!(randomizer1 != randomizer2); +} From 3ed40ad6e2278578a718e7b6053ce10f1c763c93 Mon Sep 17 00:00:00 2001 From: Conrado Date: Fri, 23 Jan 2026 12:06:04 -0300 Subject: [PATCH 61/86] core: simplify compute_refreshing_shares() (#940) --- frost-core/CHANGELOG.md | 6 + frost-core/src/keys/refresh.rs | 47 ++++---- frost-core/src/tests/refresh.rs | 105 ++---------------- frost-ed25519/src/keys/refresh.rs | 10 +- frost-ed25519/tests/integration_tests.rs | 85 +------------- frost-ed448/src/keys/refresh.rs | 10 +- frost-ed448/tests/integration_tests.rs | 85 +------------- frost-p256/src/keys/refresh.rs | 10 +- frost-p256/tests/integration_tests.rs | 85 +------------- frost-ristretto255/src/keys/refresh.rs | 10 +- frost-ristretto255/tests/integration_tests.rs | 85 +------------- frost-secp256k1-tr/src/keys/refresh.rs | 10 +- frost-secp256k1-tr/tests/integration_tests.rs | 85 +------------- frost-secp256k1/src/keys/refresh.rs | 10 +- frost-secp256k1/tests/integration_tests.rs | 85 +------------- 15 files changed, 48 insertions(+), 680 deletions(-) diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index be59fdf..87f66e9 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -15,6 +15,12 @@ Entries are listed in reverse chronological order. * The `std` and `nightly` features were removed from all crates * Renamed `frost_core::keys::refresh::refresh_dkg_part_1` to `refresh_dkg_part1`. * Fixed the crate-specific versions of the `refresh` module to be non-generic. +* Removed the `min_signers` and `max_signers` arguments from + `frost_core::keys::refresh::compute_refreshing_shares()`. The former is now + read from the `pub_key_package`; if you pass a pre-3.0.0 generate package, + you will need to fills its `min_signers` field with the original threshold + before calling the function (recreate it with `PublicKeyPackage::new()`). + The latter was simply redundant. * Refactored the `frost_core::keys::repairable` module: * `repair_share_step_1()` now takes a `KeyPackage` and returns a map with a new `Delta` type instead of a raw `Scalar` diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index 4df4e6a..cd80ad0 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -42,38 +42,37 @@ use super::{dkg::round1::Package, KeyPackage, SecretShare, VerifiableSecretShari /// Compute refreshing shares for the Trusted Dealer refresh procedure. /// -/// - `pub_key_package`: the current public key package. -/// - `max_signers`: the number of participants that are refreshing their -/// shares. It can be smaller than the original value, but still equal to or -/// greater than `min_signers`. -/// - `min_signers`: the threshold needed to sign. It must be equal to the -/// original value for the group (i.e. the refresh process can't reduce -/// the threshold). +/// - `pub_key_package`: the current public key package. Note: if a pre-3.0.0 +/// generate package is used, you will need to manually set the `min_signers` +/// field with the theshold that was used in the original share generation. +/// (You can't change the threshold when refreshing shares.) /// - `identifiers`: The identifiers of all participants that want to refresh -/// their shares. Must be the same length as `max_signers`. +/// their shares. Must be a subset of the identifiers in `pub_key_package`. If +/// not all identifiers are passed, the refresh procedure will effectively +/// remove the missing participants. The length must be equal to or greater +/// than the threshold of the group. /// -/// It returns a vectors of [`SecretShare`] that must be sent to the participants -/// in the same order as `identifiers`, and the refreshed [`PublicKeyPackage`]. +/// It returns a vectors of [`SecretShare`] that must be sent to the +/// participants in the same order as `identifiers`, and the refreshed +/// [`PublicKeyPackage`]. pub fn compute_refreshing_shares( pub_key_package: PublicKeyPackage, - max_signers: u16, - min_signers: u16, identifiers: &[Identifier], rng: &mut R, ) -> Result<(Vec>, PublicKeyPackage), Error> { - // Validate min_signers. It's OK if the min_signers is missing, because - // we validate it again in `refresh_share()`. - if let Some(package_min_signers) = pub_key_package.min_signers { - if min_signers != package_min_signers { - return Err(Error::InvalidMinSigners); - } - } + let min_signers = pub_key_package + .min_signers + .ok_or(Error::InvalidMinSigners)?; + + let signers = identifiers.len() as u16; + validate_num_of_signers(min_signers, signers)?; - // Validate inputs - if identifiers.len() != max_signers as usize { - return Err(Error::IncorrectNumberOfIdentifiers); + if identifiers + .iter() + .any(|i| !pub_key_package.verifying_shares().contains_key(i)) + { + return Err(Error::UnknownIdentifier); } - validate_num_of_signers(min_signers, max_signers)?; // Build refreshing shares let refreshing_key = SigningKey { @@ -83,7 +82,7 @@ pub fn compute_refreshing_shares( let coefficients = generate_coefficients::(min_signers as usize - 1, rng); let refreshing_shares = generate_secret_shares( &refreshing_key, - max_signers, + signers, min_signers, coefficients, identifiers, diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index a8fdfb5..3d50d59 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -57,18 +57,10 @@ pub fn check_refresh_shares_with_dealer( Identifier::try_from(5).unwrap(), ]; - const NEW_MAX_SIGNERS: u16 = 4; - // Trusted Dealer generates zero keys and new public key package - let (zero_shares, new_pub_key_package) = compute_refreshing_shares( - pub_key_package, - NEW_MAX_SIGNERS, - MIN_SIGNERS, - &remaining_ids, - &mut rng, - ) - .unwrap(); + let (zero_shares, new_pub_key_package) = + compute_refreshing_shares(pub_key_package, &remaining_ids, &mut rng).unwrap(); // Simulate serialization / deserialization to ensure it works let new_pub_key_package = frost::keys::PublicKeyPackage::deserialize(&new_pub_key_package.serialize().unwrap()) @@ -102,21 +94,13 @@ pub fn check_refresh_shares_with_dealer_fails_with_invalid_signers< C: Ciphersuite, R: RngCore + CryptoRng, >( - new_max_signers: u16, - min_signers: u16, identifiers: &[Identifier], error: Error, mut rng: R, ) { let (_old_shares, pub_key_package) = generate_with_dealer::(5, 2, frost::keys::IdentifierList::Default, &mut rng).unwrap(); - let out = compute_refreshing_shares( - pub_key_package, - new_max_signers, - min_signers, - identifiers, - &mut rng, - ); + let out = compute_refreshing_shares(pub_key_package, identifiers, &mut rng); assert!(out.is_err()); assert!(out == Err(error)) @@ -166,18 +150,10 @@ pub fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package< Identifier::try_from(5).unwrap(), ]; - const NEW_MAX_SIGNERS: u16 = 4; - // Trusted Dealer generates zero keys and new public key package - let e = compute_refreshing_shares( - incorrect_pub_key_package, - NEW_MAX_SIGNERS, - MIN_SIGNERS, - &remaining_ids, - &mut rng, - ) - .unwrap_err(); + let e = + compute_refreshing_shares(incorrect_pub_key_package, &remaining_ids, &mut rng).unwrap_err(); assert_eq!(e, Error::UnknownIdentifier) } @@ -215,16 +191,8 @@ pub fn check_refresh_shares_with_dealer_serialisation( - mut rng: R, -) { - // Compute shares - - //////////////////////////////////////////////////////////////////////////// - // Old Key generation - //////////////////////////////////////////////////////////////////////////// - - const MAX_SIGNERS: u16 = 5; - const MIN_SIGNERS: u16 = 3; - let (old_shares, pub_key_package) = generate_with_dealer( - MAX_SIGNERS, - MIN_SIGNERS, - frost::keys::IdentifierList::Default, - &mut rng, - ) - .unwrap(); - - let mut old_key_packages: BTreeMap, KeyPackage> = BTreeMap::new(); - - for (k, v) in old_shares { - let key_package = KeyPackage::try_from(v).unwrap(); - old_key_packages.insert(k, key_package); - } - - //////////////////////////////////////////////////////////////////////////// - // New Key generation - //////////////////////////////////////////////////////////////////////////// - - // Signer 2 will be removed and Signers 1, 3, 4 & 5 will remain - - let remaining_ids = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - - const NEW_MAX_SIGNERS: u16 = 4; - const NEW_MIN_SIGNERS: u16 = 2; - - // Trusted Dealer generates zero keys and new public key package - - let r = compute_refreshing_shares( - pub_key_package, - NEW_MAX_SIGNERS, - NEW_MIN_SIGNERS, - &remaining_ids, - &mut rng, - ); - - assert_eq!(r, Err(Error::InvalidMinSigners)); -} - /// Test FROST signing with DKG with a Ciphersuite. pub fn check_refresh_shares_with_dkg( mut rng: R, diff --git a/frost-ed25519/src/keys/refresh.rs b/frost-ed25519/src/keys/refresh.rs index 36797d9..bb4b6dd 100644 --- a/frost-ed25519/src/keys/refresh.rs +++ b/frost-ed25519/src/keys/refresh.rs @@ -14,18 +14,10 @@ use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, - max_signers: u16, - min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, ) -> Result<(Vec, PublicKeyPackage), Error> { - frost::keys::refresh::compute_refreshing_shares( - old_pub_key_package, - max_signers, - min_signers, - identifiers, - &mut rng, - ) + frost::keys::refresh::compute_refreshing_shares(old_pub_key_package, identifiers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_share`]. diff --git a/frost-ed25519/tests/integration_tests.rs b/frost-ed25519/tests/integration_tests.rs index d43664a..2cc6620 100644 --- a/frost-ed25519/tests/integration_tests.rs +++ b/frost-ed25519/tests/integration_tests.rs @@ -89,77 +89,6 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { >(rng); } -#[test] -fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 1; - let max_signers = 4; - let error = Error::InvalidMinSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Ed25519Sha512, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 2; - let max_signers = 3; - let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Ed25519Sha512, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 6; - let max_signers = 4; - let error: frost_core::Error = Error::InvalidMinSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Ed25519Sha512, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 2; - let max_signers = 1; - let error = Error::InvalidMaxSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Ed25519Sha512, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { let rng = rand::rngs::OsRng; @@ -169,24 +98,12 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { Identifier::try_from(4).unwrap(), Identifier::try_from(6).unwrap(), ]; - let min_signers = 2; - let max_signers = 4; let error = Error::UnknownIdentifier; frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< Ed25519Sha512, _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_different_min_signers() { - let rng = rand::rngs::OsRng; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_different_min_signers::< - Ed25519Sha512, - _, - >(rng); + >(&identifiers, error, rng); } #[test] diff --git a/frost-ed448/src/keys/refresh.rs b/frost-ed448/src/keys/refresh.rs index 36797d9..bb4b6dd 100644 --- a/frost-ed448/src/keys/refresh.rs +++ b/frost-ed448/src/keys/refresh.rs @@ -14,18 +14,10 @@ use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, - max_signers: u16, - min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, ) -> Result<(Vec, PublicKeyPackage), Error> { - frost::keys::refresh::compute_refreshing_shares( - old_pub_key_package, - max_signers, - min_signers, - identifiers, - &mut rng, - ) + frost::keys::refresh::compute_refreshing_shares(old_pub_key_package, identifiers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_share`]. diff --git a/frost-ed448/tests/integration_tests.rs b/frost-ed448/tests/integration_tests.rs index d22705c..28bd22a 100644 --- a/frost-ed448/tests/integration_tests.rs +++ b/frost-ed448/tests/integration_tests.rs @@ -89,77 +89,6 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { >(rng); } -#[test] -fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 1; - let max_signers = 4; - let error = Error::InvalidMinSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Ed448Shake256, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 2; - let max_signers = 3; - let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Ed448Shake256, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 6; - let max_signers = 4; - let error: frost_core::Error = Error::InvalidMinSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Ed448Shake256, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 2; - let max_signers = 1; - let error = Error::InvalidMaxSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Ed448Shake256, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { let rng = rand::rngs::OsRng; @@ -169,24 +98,12 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { Identifier::try_from(4).unwrap(), Identifier::try_from(6).unwrap(), ]; - let min_signers = 2; - let max_signers = 4; let error = Error::UnknownIdentifier; frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< Ed448Shake256, _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_different_min_signers() { - let rng = rand::rngs::OsRng; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_different_min_signers::< - Ed448Shake256, - _, - >(rng); + >(&identifiers, error, rng); } #[test] diff --git a/frost-p256/src/keys/refresh.rs b/frost-p256/src/keys/refresh.rs index 36797d9..bb4b6dd 100644 --- a/frost-p256/src/keys/refresh.rs +++ b/frost-p256/src/keys/refresh.rs @@ -14,18 +14,10 @@ use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, - max_signers: u16, - min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, ) -> Result<(Vec, PublicKeyPackage), Error> { - frost::keys::refresh::compute_refreshing_shares( - old_pub_key_package, - max_signers, - min_signers, - identifiers, - &mut rng, - ) + frost::keys::refresh::compute_refreshing_shares(old_pub_key_package, identifiers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_share`]. diff --git a/frost-p256/tests/integration_tests.rs b/frost-p256/tests/integration_tests.rs index 8a9a92e..d1d27f4 100644 --- a/frost-p256/tests/integration_tests.rs +++ b/frost-p256/tests/integration_tests.rs @@ -89,77 +89,6 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { >(rng); } -#[test] -fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 1; - let max_signers = 4; - let error = Error::InvalidMinSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - P256Sha256, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 2; - let max_signers = 3; - let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - P256Sha256, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 6; - let max_signers = 4; - let error: frost_core::Error = Error::InvalidMinSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - P256Sha256, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 2; - let max_signers = 1; - let error = Error::InvalidMaxSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - P256Sha256, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { let rng = rand::rngs::OsRng; @@ -169,24 +98,12 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { Identifier::try_from(4).unwrap(), Identifier::try_from(6).unwrap(), ]; - let min_signers = 2; - let max_signers = 4; let error = Error::UnknownIdentifier; frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< P256Sha256, _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_different_min_signers() { - let rng = rand::rngs::OsRng; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_different_min_signers::< - P256Sha256, - _, - >(rng); + >(&identifiers, error, rng); } #[test] diff --git a/frost-ristretto255/src/keys/refresh.rs b/frost-ristretto255/src/keys/refresh.rs index 36797d9..bb4b6dd 100644 --- a/frost-ristretto255/src/keys/refresh.rs +++ b/frost-ristretto255/src/keys/refresh.rs @@ -14,18 +14,10 @@ use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, - max_signers: u16, - min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, ) -> Result<(Vec, PublicKeyPackage), Error> { - frost::keys::refresh::compute_refreshing_shares( - old_pub_key_package, - max_signers, - min_signers, - identifiers, - &mut rng, - ) + frost::keys::refresh::compute_refreshing_shares(old_pub_key_package, identifiers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_share`]. diff --git a/frost-ristretto255/tests/integration_tests.rs b/frost-ristretto255/tests/integration_tests.rs index 8ff8af1..c4ef3be 100644 --- a/frost-ristretto255/tests/integration_tests.rs +++ b/frost-ristretto255/tests/integration_tests.rs @@ -90,77 +90,6 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { >(rng); } -#[test] -fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 1; - let max_signers = 4; - let error = Error::InvalidMinSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Ristretto255Sha512, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 2; - let max_signers = 3; - let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Ristretto255Sha512, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 6; - let max_signers = 4; - let error: frost_core::Error = Error::InvalidMinSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Ristretto255Sha512, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 2; - let max_signers = 1; - let error = Error::InvalidMaxSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Ristretto255Sha512, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { let rng = rand::rngs::OsRng; @@ -170,24 +99,12 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { Identifier::try_from(4).unwrap(), Identifier::try_from(6).unwrap(), ]; - let min_signers = 2; - let max_signers = 4; let error = Error::UnknownIdentifier; frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< Ristretto255Sha512, _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_different_min_signers() { - let rng = rand::rngs::OsRng; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_different_min_signers::< - Ristretto255Sha512, - _, - >(rng); + >(&identifiers, error, rng); } #[test] diff --git a/frost-secp256k1-tr/src/keys/refresh.rs b/frost-secp256k1-tr/src/keys/refresh.rs index 36797d9..bb4b6dd 100644 --- a/frost-secp256k1-tr/src/keys/refresh.rs +++ b/frost-secp256k1-tr/src/keys/refresh.rs @@ -14,18 +14,10 @@ use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, - max_signers: u16, - min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, ) -> Result<(Vec, PublicKeyPackage), Error> { - frost::keys::refresh::compute_refreshing_shares( - old_pub_key_package, - max_signers, - min_signers, - identifiers, - &mut rng, - ) + frost::keys::refresh::compute_refreshing_shares(old_pub_key_package, identifiers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_share`]. diff --git a/frost-secp256k1-tr/tests/integration_tests.rs b/frost-secp256k1-tr/tests/integration_tests.rs index 11db8b2..682db37 100644 --- a/frost-secp256k1-tr/tests/integration_tests.rs +++ b/frost-secp256k1-tr/tests/integration_tests.rs @@ -90,77 +90,6 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { >(rng); } -#[test] -fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 1; - let max_signers = 4; - let error = Error::InvalidMinSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Secp256K1Sha256TR, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 2; - let max_signers = 3; - let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Secp256K1Sha256TR, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 6; - let max_signers = 4; - let error: frost_core::Error = Error::InvalidMinSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Secp256K1Sha256TR, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 2; - let max_signers = 1; - let error = Error::InvalidMaxSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Secp256K1Sha256TR, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { let rng = rand::rngs::OsRng; @@ -170,24 +99,12 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { Identifier::try_from(4).unwrap(), Identifier::try_from(6).unwrap(), ]; - let min_signers = 2; - let max_signers = 4; let error = Error::UnknownIdentifier; frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< Secp256K1Sha256TR, _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_different_min_signers() { - let rng = rand::rngs::OsRng; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_different_min_signers::< - Secp256K1Sha256TR, - _, - >(rng); + >(&identifiers, error, rng); } #[test] diff --git a/frost-secp256k1/src/keys/refresh.rs b/frost-secp256k1/src/keys/refresh.rs index 36797d9..bb4b6dd 100644 --- a/frost-secp256k1/src/keys/refresh.rs +++ b/frost-secp256k1/src/keys/refresh.rs @@ -14,18 +14,10 @@ use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, - max_signers: u16, - min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, ) -> Result<(Vec, PublicKeyPackage), Error> { - frost::keys::refresh::compute_refreshing_shares( - old_pub_key_package, - max_signers, - min_signers, - identifiers, - &mut rng, - ) + frost::keys::refresh::compute_refreshing_shares(old_pub_key_package, identifiers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_share`]. diff --git a/frost-secp256k1/tests/integration_tests.rs b/frost-secp256k1/tests/integration_tests.rs index e2c111c..343214e 100644 --- a/frost-secp256k1/tests/integration_tests.rs +++ b/frost-secp256k1/tests/integration_tests.rs @@ -89,77 +89,6 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { >(rng); } -#[test] -fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 1; - let max_signers = 4; - let error = Error::InvalidMinSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Secp256K1Sha256, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 2; - let max_signers = 3; - let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Secp256K1Sha256, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { - let rng = rand::rngs::OsRng; - let identifiers = vec![ - Identifier::try_from(1).unwrap(), - Identifier::try_from(3).unwrap(), - Identifier::try_from(4).unwrap(), - Identifier::try_from(5).unwrap(), - ]; - let min_signers = 6; - let max_signers = 4; - let error: frost_core::Error = Error::InvalidMinSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Secp256K1Sha256, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { - let rng = rand::rngs::OsRng; - let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 2; - let max_signers = 1; - let error = Error::InvalidMaxSigners; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< - Secp256K1Sha256, - _, - >(max_signers, min_signers, &identifiers, error, rng); -} - #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { let rng = rand::rngs::OsRng; @@ -169,24 +98,12 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { Identifier::try_from(4).unwrap(), Identifier::try_from(6).unwrap(), ]; - let min_signers = 2; - let max_signers = 4; let error = Error::UnknownIdentifier; frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< Secp256K1Sha256, _, - >(max_signers, min_signers, &identifiers, error, rng); -} - -#[test] -fn check_refresh_shares_with_dealer_fails_with_different_min_signers() { - let rng = rand::rngs::OsRng; - - frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_different_min_signers::< - Secp256K1Sha256, - _, - >(rng); + >(&identifiers, error, rng); } #[test] From 8f974985f3cb03ca0ccd00f864d93a46dee2fa01 Mon Sep 17 00:00:00 2001 From: Conrado Date: Tue, 27 Jan 2026 12:58:22 -0300 Subject: [PATCH 62/86] use partX() for repair share functions (#1001) --- frost-core/CHANGELOG.md | 16 +++++---- frost-core/src/keys/repairable.rs | 22 ++++++------- frost-core/src/tests/repairable.rs | 40 +++++++++++------------ frost-ed25519/src/keys/repairable.rs | 38 ++++++++++----------- frost-ed448/src/keys/repairable.rs | 38 ++++++++++----------- frost-p256/src/keys/repairable.rs | 38 ++++++++++----------- frost-ristretto255/src/keys/repairable.rs | 38 ++++++++++----------- frost-secp256k1-tr/src/keys/repairable.rs | 40 +++++++++++------------ frost-secp256k1/src/keys/repairable.rs | 38 ++++++++++----------- 9 files changed, 154 insertions(+), 154 deletions(-) diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index 87f66e9..057b406 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -22,13 +22,15 @@ Entries are listed in reverse chronological order. before calling the function (recreate it with `PublicKeyPackage::new()`). The latter was simply redundant. * Refactored the `frost_core::keys::repairable` module: - * `repair_share_step_1()` now takes a `KeyPackage` and returns a map with - a new `Delta` type instead of a raw `Scalar` - * `repair_share_step_2()` now takes the `Delta` type and returns a new `Sigma` - type instead of a raw `Scalar` - * `repair_share_step_3()` now takes the `Sigma` type and a `PublicKeyPackage` - instead of `VerifiableSecretSharingCommitment`; and returns a `KeyPackage` - instead of `SecretShare`. + * `repair_share_step_1()` was renamed to `repair_share_part1()` and now takes + a `KeyPackage` and returns a map with a new `Delta` type instead of a raw + `Scalar` + * `repair_share_step_2()` was renamed to `repair_share_part2()` and now takes + the `Delta` type and returns a new `Sigma` type instead of a raw `Scalar` + * `repair_share_step_3()` was renamed to `repair_share_part3()` and now takes + the `Sigma` type and a `PublicKeyPackage` instead of + `VerifiableSecretSharingCommitment`; and returns a `KeyPackage` instead of + `SecretShare`. * These changes provide more type safety and are make it more useful since `SecretPackage`s are not expected to be stored diff --git a/frost-core/src/keys/repairable.rs b/frost-core/src/keys/repairable.rs index 6acd52e..da4f5f0 100644 --- a/frost-core/src/keys/repairable.rs +++ b/frost-core/src/keys/repairable.rs @@ -9,12 +9,12 @@ //! //! - Participants need to agree somehow on who are going to be the `helpers` //! for the repair, and which participant is going to repair their share. -//! - Each helper runs `repair_share_step_1`, generating a set of `delta` values +//! - Each helper runs `repair_share_part1`, generating a set of `delta` values //! to be sent to each helper (including themselves). -//! - Each helper runs `repair_share_step_2`, passing the received `delta` +//! - Each helper runs `repair_share_part2`, passing the received `delta` //! values, generating a `sigma` value to be sent to the participant repairing //! their share. -//! - The participant repairing their share runs `repair_share_step_3`, passing +//! - The participant repairing their share runs `repair_share_part3`, passing //! all the received `sigma` values, recovering their lost `KeyPackage`. (They //! will also need the `PublicKeyPackage` for this step which could be //! provided by any of the helpers). @@ -32,7 +32,7 @@ use crate::{ use super::{generate_coefficients, SigningShare}; -/// A delta value which is the output of step 1 of RTS. +/// A delta value which is the output of part 1 of RTS. #[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] @@ -68,7 +68,7 @@ where } } -/// A sigma value which is the output of step 2 of RTS. +/// A sigma value which is the output of part 2 of RTS. #[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] @@ -104,14 +104,14 @@ where } } -/// Step 1 of RTS. +/// Part 1 of RTS. /// /// Generates the "delta" values from the helper with `key_package_i` to send to /// `helpers` (which includes the helper with `key_package_i`), to help /// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. -pub fn repair_share_step_1( +pub fn repair_share_part1( helpers: &[Identifier], key_package_i: &KeyPackage, rng: &mut R, @@ -169,11 +169,11 @@ fn compute_last_random_value( Ok(out) } -/// Step 2 of RTS. +/// Part 2 of RTS. /// /// Generates the "sigma" value from all `deltas` received from all helpers. /// The "sigma" value must be sent to the participant repairing their share. -pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { +pub fn repair_share_part2(deltas: &[Delta]) -> Sigma { let mut sigma_j = <::Field>::zero(); for d in deltas { @@ -183,7 +183,7 @@ pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { Sigma::new(sigma_j) } -/// Step 3 of RTS. +/// Part 3 of RTS. /// /// The participant with the given `identifier` recovers their `KeyPackage` /// with the "sigma" values received from all helpers and the `PublicKeyPackage` @@ -192,7 +192,7 @@ pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { /// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. /// This happens for `PublicKeyPackage`s created before the 3.0.0 release; /// in that case, the user should set the `min_signers` field manually. -pub fn repair_share_step_3( +pub fn repair_share_part3( sigmas: &[Sigma], identifier: Identifier, public_key_package: &PublicKeyPackage, diff --git a/frost-core/src/tests/repairable.rs b/frost-core/src/tests/repairable.rs index 4dd5986..e437973 100644 --- a/frost-core/src/tests/repairable.rs +++ b/frost-core/src/tests/repairable.rs @@ -12,7 +12,7 @@ use crate::keys::KeyPackage; use crate::{ compute_lagrange_coefficient, keys::{ - repairable::{repair_share_step_1, repair_share_step_2, repair_share_step_3}, + repairable::{repair_share_part1, repair_share_part2, repair_share_part3}, PublicKeyPackage, SecretShare, }, Ciphersuite, Error, Field, Group, Identifier, @@ -62,25 +62,25 @@ pub fn check_rts(mut rng: R) { // Each helper generates random values for each helper let helper_1_deltas = - repair_share_step_1(&helpers, helper_1, &mut rng, participant.identifier).unwrap(); + repair_share_part1(&helpers, helper_1, &mut rng, participant.identifier).unwrap(); let helper_4_deltas = - repair_share_step_1(&helpers, helper_4, &mut rng, participant.identifier).unwrap(); + repair_share_part1(&helpers, helper_4, &mut rng, participant.identifier).unwrap(); let helper_5_deltas = - repair_share_step_1(&helpers, helper_5, &mut rng, participant.identifier).unwrap(); + repair_share_part1(&helpers, helper_5, &mut rng, participant.identifier).unwrap(); // Each helper calculates their sigma from the random values received from the other helpers - let helper_1_sigma: Sigma = repair_share_step_2::(&[ + let helper_1_sigma: Sigma = repair_share_part2::(&[ helper_1_deltas[&helpers[0]], helper_4_deltas[&helpers[0]], helper_5_deltas[&helpers[0]], ]); - let helper_4_sigma: Sigma = repair_share_step_2::(&[ + let helper_4_sigma: Sigma = repair_share_part2::(&[ helper_1_deltas[&helpers[1]], helper_4_deltas[&helpers[1]], helper_5_deltas[&helpers[1]], ]); - let helper_5_sigma: Sigma = repair_share_step_2::(&[ + let helper_5_sigma: Sigma = repair_share_part2::(&[ helper_1_deltas[&helpers[2]], helper_4_deltas[&helpers[2]], helper_5_deltas[&helpers[2]], @@ -88,7 +88,7 @@ pub fn check_rts(mut rng: R) { // The participant wishing to recover their share sums the sigmas sent from all helpers - let participant_recovered_share = repair_share_step_3( + let participant_recovered_share = repair_share_part3( &[helper_1_sigma, helper_4_sigma, helper_5_sigma], participant.identifier, &public_key_package, @@ -108,8 +108,8 @@ fn generate_scalar_from_byte_string( out.unwrap() } -/// Test repair_share_step_1 -pub fn check_repair_share_step_1(mut rng: R) { +/// Test repair_share_part1 +pub fn check_repair_share_part1(mut rng: R) { // Compute shares let max_signers = 5; @@ -142,7 +142,7 @@ pub fn check_repair_share_step_1(mut rng ]; // Generate deltas for helper 4 - let deltas = repair_share_step_1(&helpers, helper_4, &mut rng, participant.identifier).unwrap(); + let deltas = repair_share_part1(&helpers, helper_4, &mut rng, participant.identifier).unwrap(); let lagrange_coefficient = compute_lagrange_coefficient( &helpers.iter().cloned().collect(), @@ -161,8 +161,8 @@ pub fn check_repair_share_step_1(mut rng assert!(lhs == rhs) } -/// Test repair_share_step_2 -pub fn check_repair_share_step_2(repair_share_helpers: &Value) { +/// Test repair_share_part2 +pub fn check_repair_share_part2(repair_share_helpers: &Value) { let values = &repair_share_helpers["scalar_generation"]; let value_1 = Delta::new(generate_scalar_from_byte_string::( @@ -174,7 +174,7 @@ pub fn check_repair_share_step_2(repair_share_helpers: &Value) { let value_3 = Delta::new(generate_scalar_from_byte_string::( values["random_scalar_3"].as_str().unwrap(), )); - let expected = repair_share_step_2::(&[value_1, value_2, value_3]); + let expected = repair_share_part2::(&[value_1, value_2, value_3]); let actual = Sigma::new(generate_scalar_from_byte_string::( values["random_scalar_sum"].as_str().unwrap(), @@ -183,8 +183,8 @@ pub fn check_repair_share_step_2(repair_share_helpers: &Value) { assert!(actual == expected); } -/// Test repair_share -pub fn check_repair_share_step_3( +/// Test repair_share_part3 +pub fn check_repair_share_part3( mut rng: R, repair_share_helpers: &Value, ) { @@ -217,7 +217,7 @@ pub fn check_repair_share_step_3( sigmas["sigma_4"].as_str().unwrap(), )); - let actual = repair_share_step_3::( + let actual = repair_share_part3::( &[sigma_1, sigma_2, sigma_3, sigma_4], Identifier::try_from(2).unwrap(), &public_key_package, @@ -230,8 +230,8 @@ pub fn check_repair_share_step_3( assert!(expected == actual.signing_share().to_scalar()); } -/// Test repair share step 1 fails with invalid numbers of signers. -pub fn check_repair_share_step_1_fails_with_invalid_min_signers< +/// Test repair share part 1 fails with invalid numbers of signers. +pub fn check_repair_share_part1_fails_with_invalid_min_signers< C: Ciphersuite, R: RngCore + CryptoRng, >( @@ -255,7 +255,7 @@ pub fn check_repair_share_step_1_fails_with_invalid_min_signers< let helper = Identifier::try_from(3).unwrap(); - let out = repair_share_step_1( + let out = repair_share_part1( &[helper], &key_packages[&helper], &mut rng, diff --git a/frost-ed25519/src/keys/repairable.rs b/frost-ed25519/src/keys/repairable.rs index 0d9930f..9e809ed 100644 --- a/frost-ed25519/src/keys/repairable.rs +++ b/frost-ed25519/src/keys/repairable.rs @@ -13,37 +13,37 @@ use crate::keys::{KeyPackage, PublicKeyPackage}; use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore}; use crate::{Ed25519Sha512, Error}; -/// A delta value which is the output of step 1 of RTS. +/// A delta value which is the output of part 1 of RTS. pub type Delta = frost::keys::repairable::Delta; -/// A sigma value which is the output of step 2 of RTS. +/// A sigma value which is the output of part 2 of RTS. pub type Sigma = frost::keys::repairable::Sigma; -/// Step 1 of RTS. +/// Part 1 of RTS. /// /// Generates the "delta" values from the helper with `key_package_i` to send to /// `helpers` (which includes the helper with `key_package_i`), to help /// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. -pub fn repair_share_step_1( +pub fn repair_share_part1( helpers: &[Identifier], key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, ) -> Result, Error> { - frost::keys::repairable::repair_share_step_1(helpers, key_package_i, rng, participant) + frost::keys::repairable::repair_share_part1(helpers, key_package_i, rng, participant) } -/// Step 2 of RTS. +/// Part 2 of RTS. /// /// Generates the "sigma" value from all `deltas` received from all helpers. /// The "sigma" value must be sent to the participant repairing their share. -pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { - frost::keys::repairable::repair_share_step_2::(deltas) +pub fn repair_share_part2(deltas: &[Delta]) -> Sigma { + frost::keys::repairable::repair_share_part2::(deltas) } -/// Step 3 of RTS. +/// Part 3 of RTS. /// /// The participant with the given `identifier` recovers their `KeyPackage` /// with the "sigma" values received from all helpers and the `PublicKeyPackage` @@ -52,12 +52,12 @@ pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { /// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. /// This happens for `PublicKeyPackage`s created before the 3.0.0 release; /// in that case, the user should set the `min_signers` field manually. -pub fn repair_share_step_3( +pub fn repair_share_part3( sigmas: &[Sigma], identifier: Identifier, public_key_package: &PublicKeyPackage, ) -> Result { - frost::keys::repairable::repair_share_step_3(sigmas, identifier, public_key_package) + frost::keys::repairable::repair_share_part3(sigmas, identifier, public_key_package) } #[cfg(test)] @@ -76,30 +76,30 @@ mod tests { } #[test] - fn check_repair_share_step_1() { + fn check_repair_share_part1() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_1::(rng); + frost_core::tests::repairable::check_repair_share_part1::(rng); } #[test] - fn check_repair_share_step_2() { - frost_core::tests::repairable::check_repair_share_step_2::(&REPAIR_SHARE); + fn check_repair_share_part2() { + frost_core::tests::repairable::check_repair_share_part2::(&REPAIR_SHARE); } #[test] - fn check_repair_share_step_3() { + fn check_repair_share_part3() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_3::( + frost_core::tests::repairable::check_repair_share_part3::( rng, &REPAIR_SHARE, ); } #[test] - fn check_repair_share_step_1_fails_with_invalid_min_signers() { + fn check_repair_share_part1_fails_with_invalid_min_signers() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< + frost_core::tests::repairable::check_repair_share_part1_fails_with_invalid_min_signers::< Ed25519Sha512, _, >(rng); diff --git a/frost-ed448/src/keys/repairable.rs b/frost-ed448/src/keys/repairable.rs index 2aa9366..f1e21d4 100644 --- a/frost-ed448/src/keys/repairable.rs +++ b/frost-ed448/src/keys/repairable.rs @@ -13,37 +13,37 @@ use crate::keys::{KeyPackage, PublicKeyPackage}; use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore}; use crate::{Ed448Shake256, Error}; -/// A delta value which is the output of step 1 of RTS. +/// A delta value which is the output of part 1 of RTS. pub type Delta = frost::keys::repairable::Delta; -/// A sigma value which is the output of step 2 of RTS. +/// A sigma value which is the output of part 2 of RTS. pub type Sigma = frost::keys::repairable::Sigma; -/// Step 1 of RTS. +/// Part 1 of RTS. /// /// Generates the "delta" values from the helper with `key_package_i` to send to /// `helpers` (which includes the helper with `key_package_i`), to help /// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. -pub fn repair_share_step_1( +pub fn repair_share_part1( helpers: &[Identifier], key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, ) -> Result, Error> { - frost::keys::repairable::repair_share_step_1(helpers, key_package_i, rng, participant) + frost::keys::repairable::repair_share_part1(helpers, key_package_i, rng, participant) } -/// Step 2 of RTS. +/// Part 2 of RTS. /// /// Generates the "sigma" value from all `deltas` received from all helpers. /// The "sigma" value must be sent to the participant repairing their share. -pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { - frost::keys::repairable::repair_share_step_2::(deltas) +pub fn repair_share_part2(deltas: &[Delta]) -> Sigma { + frost::keys::repairable::repair_share_part2::(deltas) } -/// Step 3 of RTS. +/// Part 3 of RTS. /// /// The participant with the given `identifier` recovers their `KeyPackage` /// with the "sigma" values received from all helpers and the `PublicKeyPackage` @@ -52,12 +52,12 @@ pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { /// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. /// This happens for `PublicKeyPackage`s created before the 3.0.0 release; /// in that case, the user should set the `min_signers` field manually. -pub fn repair_share_step_3( +pub fn repair_share_part3( sigmas: &[Sigma], identifier: Identifier, public_key_package: &PublicKeyPackage, ) -> Result { - frost::keys::repairable::repair_share_step_3(sigmas, identifier, public_key_package) + frost::keys::repairable::repair_share_part3(sigmas, identifier, public_key_package) } #[cfg(test)] @@ -76,30 +76,30 @@ mod tests { } #[test] - fn check_repair_share_step_1() { + fn check_repair_share_part1() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_1::(rng); + frost_core::tests::repairable::check_repair_share_part1::(rng); } #[test] - fn check_repair_share_step_2() { - frost_core::tests::repairable::check_repair_share_step_2::(&REPAIR_SHARE); + fn check_repair_share_part2() { + frost_core::tests::repairable::check_repair_share_part2::(&REPAIR_SHARE); } #[test] - fn check_repair_share_step_3() { + fn check_repair_share_part3() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_3::( + frost_core::tests::repairable::check_repair_share_part3::( rng, &REPAIR_SHARE, ); } #[test] - fn check_repair_share_step_1_fails_with_invalid_min_signers() { + fn check_repair_share_part1_fails_with_invalid_min_signers() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< + frost_core::tests::repairable::check_repair_share_part1_fails_with_invalid_min_signers::< Ed448Shake256, _, >(rng); diff --git a/frost-p256/src/keys/repairable.rs b/frost-p256/src/keys/repairable.rs index c691b94..98db9d1 100644 --- a/frost-p256/src/keys/repairable.rs +++ b/frost-p256/src/keys/repairable.rs @@ -13,37 +13,37 @@ use crate::keys::{KeyPackage, PublicKeyPackage}; use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore}; use crate::{Error, P256Sha256}; -/// A delta value which is the output of step 1 of RTS. +/// A delta value which is the output of part 1 of RTS. pub type Delta = frost::keys::repairable::Delta; -/// A sigma value which is the output of step 2 of RTS. +/// A sigma value which is the output of part 2 of RTS. pub type Sigma = frost::keys::repairable::Sigma; -/// Step 1 of RTS. +/// Part 1 of RTS. /// /// Generates the "delta" values from the helper with `key_package_i` to send to /// `helpers` (which includes the helper with `key_package_i`), to help /// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. -pub fn repair_share_step_1( +pub fn repair_share_part1( helpers: &[Identifier], key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, ) -> Result, Error> { - frost::keys::repairable::repair_share_step_1(helpers, key_package_i, rng, participant) + frost::keys::repairable::repair_share_part1(helpers, key_package_i, rng, participant) } -/// Step 2 of RTS. +/// Part 2 of RTS. /// /// Generates the "sigma" value from all `deltas` received from all helpers. /// The "sigma" value must be sent to the participant repairing their share. -pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { - frost::keys::repairable::repair_share_step_2::(deltas) +pub fn repair_share_part2(deltas: &[Delta]) -> Sigma { + frost::keys::repairable::repair_share_part2::(deltas) } -/// Step 3 of RTS. +/// Part 3 of RTS. /// /// The participant with the given `identifier` recovers their `KeyPackage` /// with the "sigma" values received from all helpers and the `PublicKeyPackage` @@ -52,12 +52,12 @@ pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { /// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. /// This happens for `PublicKeyPackage`s created before the 3.0.0 release; /// in that case, the user should set the `min_signers` field manually. -pub fn repair_share_step_3( +pub fn repair_share_part3( sigmas: &[Sigma], identifier: Identifier, public_key_package: &PublicKeyPackage, ) -> Result { - frost::keys::repairable::repair_share_step_3(sigmas, identifier, public_key_package) + frost::keys::repairable::repair_share_part3(sigmas, identifier, public_key_package) } #[cfg(test)] @@ -76,30 +76,30 @@ mod tests { } #[test] - fn check_repair_share_step_1() { + fn check_repair_share_part1() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_1::(rng); + frost_core::tests::repairable::check_repair_share_part1::(rng); } #[test] - fn check_repair_share_step_2() { - frost_core::tests::repairable::check_repair_share_step_2::(&REPAIR_SHARE); + fn check_repair_share_part2() { + frost_core::tests::repairable::check_repair_share_part2::(&REPAIR_SHARE); } #[test] - fn check_repair_share_step_3() { + fn check_repair_share_part3() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_3::( + frost_core::tests::repairable::check_repair_share_part3::( rng, &REPAIR_SHARE, ); } #[test] - fn check_repair_share_step_1_fails_with_invalid_min_signers() { + fn check_repair_share_part1_fails_with_invalid_min_signers() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< + frost_core::tests::repairable::check_repair_share_part1_fails_with_invalid_min_signers::< P256Sha256, _, >(rng); diff --git a/frost-ristretto255/src/keys/repairable.rs b/frost-ristretto255/src/keys/repairable.rs index 45bc27e..3c316bc 100644 --- a/frost-ristretto255/src/keys/repairable.rs +++ b/frost-ristretto255/src/keys/repairable.rs @@ -13,37 +13,37 @@ use crate::keys::{KeyPackage, PublicKeyPackage}; use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore}; use crate::{Error, Ristretto255Sha512}; -/// A delta value which is the output of step 1 of RTS. +/// A delta value which is the output of part 1 of RTS. pub type Delta = frost::keys::repairable::Delta; -/// A sigma value which is the output of step 2 of RTS. +/// A sigma value which is the output of part 2 of RTS. pub type Sigma = frost::keys::repairable::Sigma; -/// Step 1 of RTS. +/// Part 1 of RTS. /// /// Generates the "delta" values from the helper with `key_package_i` to send to /// `helpers` (which includes the helper with `key_package_i`), to help /// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. -pub fn repair_share_step_1( +pub fn repair_share_part1( helpers: &[Identifier], key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, ) -> Result, Error> { - frost::keys::repairable::repair_share_step_1(helpers, key_package_i, rng, participant) + frost::keys::repairable::repair_share_part1(helpers, key_package_i, rng, participant) } -/// Step 2 of RTS. +/// Part 2 of RTS. /// /// Generates the "sigma" value from all `deltas` received from all helpers. /// The "sigma" value must be sent to the participant repairing their share. -pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { - frost::keys::repairable::repair_share_step_2::(deltas) +pub fn repair_share_part2(deltas: &[Delta]) -> Sigma { + frost::keys::repairable::repair_share_part2::(deltas) } -/// Step 3 of RTS. +/// Part 3 of RTS. /// /// The participant with the given `identifier` recovers their `KeyPackage` /// with the "sigma" values received from all helpers and the `PublicKeyPackage` @@ -52,12 +52,12 @@ pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { /// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. /// This happens for `PublicKeyPackage`s created before the 3.0.0 release; /// in that case, the user should set the `min_signers` field manually. -pub fn repair_share_step_3( +pub fn repair_share_part3( sigmas: &[Sigma], identifier: Identifier, public_key_package: &PublicKeyPackage, ) -> Result { - frost::keys::repairable::repair_share_step_3(sigmas, identifier, public_key_package) + frost::keys::repairable::repair_share_part3(sigmas, identifier, public_key_package) } #[cfg(test)] @@ -76,32 +76,32 @@ mod tests { } #[test] - fn check_repair_share_step_1() { + fn check_repair_share_part1() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_1::(rng); + frost_core::tests::repairable::check_repair_share_part1::(rng); } #[test] - fn check_repair_share_step_2() { - frost_core::tests::repairable::check_repair_share_step_2::( + fn check_repair_share_part2() { + frost_core::tests::repairable::check_repair_share_part2::( &REPAIR_SHARE, ); } #[test] - fn check_repair_share_step_3() { + fn check_repair_share_part3() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_3::( + frost_core::tests::repairable::check_repair_share_part3::( rng, &REPAIR_SHARE, ); } #[test] - fn check_repair_share_step_1_fails_with_invalid_min_signers() { + fn check_repair_share_part1_fails_with_invalid_min_signers() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< + frost_core::tests::repairable::check_repair_share_part1_fails_with_invalid_min_signers::< Ristretto255Sha512, _, >(rng); diff --git a/frost-secp256k1-tr/src/keys/repairable.rs b/frost-secp256k1-tr/src/keys/repairable.rs index 0db8627..3ae930d 100644 --- a/frost-secp256k1-tr/src/keys/repairable.rs +++ b/frost-secp256k1-tr/src/keys/repairable.rs @@ -13,37 +13,37 @@ use crate::keys::{KeyPackage, PublicKeyPackage}; use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore}; use crate::{Error, Secp256K1Sha256TR}; -/// A delta value which is the output of step 1 of RTS. +/// A delta value which is the output of part 1 of RTS. pub type Delta = frost::keys::repairable::Delta; -/// A sigma value which is the output of step 2 of RTS. +/// A sigma value which is the output of part 2 of RTS. pub type Sigma = frost::keys::repairable::Sigma; -/// Step 1 of RTS. +/// Part 1 of RTS. /// /// Generates the "delta" values from the helper with `key_package_i` to send to /// `helpers` (which includes the helper with `key_package_i`), to help /// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. -pub fn repair_share_step_1( +pub fn repair_share_part1( helpers: &[Identifier], key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, ) -> Result, Error> { - frost::keys::repairable::repair_share_step_1(helpers, key_package_i, rng, participant) + frost::keys::repairable::repair_share_part1(helpers, key_package_i, rng, participant) } -/// Step 2 of RTS. +/// Part 2 of RTS. /// /// Generates the "sigma" value from all `deltas` received from all helpers. /// The "sigma" value must be sent to the participant repairing their share. -pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { - frost::keys::repairable::repair_share_step_2::(deltas) +pub fn repair_share_part2(deltas: &[Delta]) -> Sigma { + frost::keys::repairable::repair_share_part2::(deltas) } -/// Step 3 of RTS. +/// Part 3 of RTS. /// /// The participant with the given `identifier` recovers their `KeyPackage` /// with the "sigma" values received from all helpers and the `PublicKeyPackage` @@ -52,12 +52,12 @@ pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { /// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. /// This happens for `PublicKeyPackage`s created before the 3.0.0 release; /// in that case, the user should set the `min_signers` field manually. -pub fn repair_share_step_3( +pub fn repair_share_part3( sigmas: &[Sigma], identifier: Identifier, public_key_package: &PublicKeyPackage, ) -> Result { - frost::keys::repairable::repair_share_step_3(sigmas, identifier, public_key_package) + frost::keys::repairable::repair_share_part3(sigmas, identifier, public_key_package) } #[cfg(test)] @@ -76,32 +76,30 @@ mod tests { } #[test] - fn check_repair_share_step_1() { + fn check_repair_share_part1() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_1::(rng); + frost_core::tests::repairable::check_repair_share_part1::(rng); } #[test] - fn check_repair_share_step_2() { - frost_core::tests::repairable::check_repair_share_step_2::( - &REPAIR_SHARE, - ); + fn check_repair_share_part2() { + frost_core::tests::repairable::check_repair_share_part2::(&REPAIR_SHARE); } #[test] - fn check_repair_share_step_3() { + fn check_repair_share_part3() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_3::( + frost_core::tests::repairable::check_repair_share_part3::( rng, &REPAIR_SHARE, ); } #[test] - fn check_repair_share_step_1_fails_with_invalid_min_signers() { + fn check_repair_share_part1_fails_with_invalid_min_signers() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< + frost_core::tests::repairable::check_repair_share_part1_fails_with_invalid_min_signers::< Secp256K1Sha256TR, _, >(rng); diff --git a/frost-secp256k1/src/keys/repairable.rs b/frost-secp256k1/src/keys/repairable.rs index 22ef03d..48b1506 100644 --- a/frost-secp256k1/src/keys/repairable.rs +++ b/frost-secp256k1/src/keys/repairable.rs @@ -13,37 +13,37 @@ use crate::keys::{KeyPackage, PublicKeyPackage}; use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore}; use crate::{Error, Secp256K1Sha256}; -/// A delta value which is the output of step 1 of RTS. +/// A delta value which is the output of part 1 of RTS. pub type Delta = frost::keys::repairable::Delta; -/// A sigma value which is the output of step 2 of RTS. +/// A sigma value which is the output of part 2 of RTS. pub type Sigma = frost::keys::repairable::Sigma; -/// Step 1 of RTS. +/// Part 1 of RTS. /// /// Generates the "delta" values from the helper with `key_package_i` to send to /// `helpers` (which includes the helper with `key_package_i`), to help /// `participant` recover their share. /// /// Returns a BTreeMap mapping which value should be sent to which participant. -pub fn repair_share_step_1( +pub fn repair_share_part1( helpers: &[Identifier], key_package_i: &KeyPackage, rng: &mut R, participant: Identifier, ) -> Result, Error> { - frost::keys::repairable::repair_share_step_1(helpers, key_package_i, rng, participant) + frost::keys::repairable::repair_share_part1(helpers, key_package_i, rng, participant) } -/// Step 2 of RTS. +/// Part 2 of RTS. /// /// Generates the "sigma" value from all `deltas` received from all helpers. /// The "sigma" value must be sent to the participant repairing their share. -pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { - frost::keys::repairable::repair_share_step_2::(deltas) +pub fn repair_share_part2(deltas: &[Delta]) -> Sigma { + frost::keys::repairable::repair_share_part2::(deltas) } -/// Step 3 of RTS. +/// Part 3 of RTS. /// /// The participant with the given `identifier` recovers their `KeyPackage` /// with the "sigma" values received from all helpers and the `PublicKeyPackage` @@ -52,12 +52,12 @@ pub fn repair_share_step_2(deltas: &[Delta]) -> Sigma { /// Returns an error if the `min_signers` field is not set in the `PublicKeyPackage`. /// This happens for `PublicKeyPackage`s created before the 3.0.0 release; /// in that case, the user should set the `min_signers` field manually. -pub fn repair_share_step_3( +pub fn repair_share_part3( sigmas: &[Sigma], identifier: Identifier, public_key_package: &PublicKeyPackage, ) -> Result { - frost::keys::repairable::repair_share_step_3(sigmas, identifier, public_key_package) + frost::keys::repairable::repair_share_part3(sigmas, identifier, public_key_package) } #[cfg(test)] @@ -76,30 +76,30 @@ mod tests { } #[test] - fn check_repair_share_step_1() { + fn check_repair_share_part1() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_1::(rng); + frost_core::tests::repairable::check_repair_share_part1::(rng); } #[test] - fn check_repair_share_step_2() { - frost_core::tests::repairable::check_repair_share_step_2::(&REPAIR_SHARE); + fn check_repair_share_part2() { + frost_core::tests::repairable::check_repair_share_part2::(&REPAIR_SHARE); } #[test] - fn check_repair_share_step_3() { + fn check_repair_share_part3() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_3::( + frost_core::tests::repairable::check_repair_share_part3::( rng, &REPAIR_SHARE, ); } #[test] - fn check_repair_share_step_1_fails_with_invalid_min_signers() { + fn check_repair_share_part1_fails_with_invalid_min_signers() { let rng = rand::rngs::OsRng; - frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< + frost_core::tests::repairable::check_repair_share_part1_fails_with_invalid_min_signers::< Secp256K1Sha256, _, >(rng); From 68b1f572e9cd4df5c01306af7edcf3217c249cb2 Mon Sep 17 00:00:00 2001 From: Conrado Date: Tue, 27 Jan 2026 12:58:26 -0300 Subject: [PATCH 63/86] reexport rerandomized API in ciphersuite crates (#998) --- frost-core/CHANGELOG.md | 2 + frost-ed25519/src/rerandomized.rs | 65 ++++++++++++++++++++++++++ frost-ed448/src/rerandomized.rs | 65 ++++++++++++++++++++++++++ frost-p256/src/rerandomized.rs | 65 ++++++++++++++++++++++++++ frost-rerandomized/src/lib.rs | 33 ++++++++++--- frost-ristretto255/src/lib.rs | 2 + frost-ristretto255/src/rerandomized.rs | 65 ++++++++++++++++++++++++++ frost-secp256k1-tr/src/rerandomized.rs | 65 ++++++++++++++++++++++++++ frost-secp256k1/src/rerandomized.rs | 65 ++++++++++++++++++++++++++ gencode/src/main.rs | 1 + 10 files changed, 422 insertions(+), 6 deletions(-) create mode 100644 frost-ed25519/src/rerandomized.rs create mode 100644 frost-ed448/src/rerandomized.rs create mode 100644 frost-p256/src/rerandomized.rs create mode 100644 frost-ristretto255/src/rerandomized.rs create mode 100644 frost-secp256k1-tr/src/rerandomized.rs create mode 100644 frost-secp256k1/src/rerandomized.rs diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index 057b406..3e85019 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -37,6 +37,8 @@ Entries are listed in reverse chronological order. ### Additional changes * Added DKG refresh functions to the crate-specific `refresh` modules. +* Re-exported the `frost-rerandomized` crate in the ciphersuite functions, e.g. + you can call `frost_ristretto255::rerandomized::sign_with_randomizer_seed()`. ## 2.2.0 diff --git a/frost-ed25519/src/rerandomized.rs b/frost-ed25519/src/rerandomized.rs new file mode 100644 index 0000000..7a90a0d --- /dev/null +++ b/frost-ed25519/src/rerandomized.rs @@ -0,0 +1,65 @@ +//! FROST implementation supporting re-randomizable keys. + +use alloc::collections::btree_map::BTreeMap; + +/// Re-randomized FROST signing using the given `randomizer_seed`, which should +/// be sent from the Coordinator using a confidential channel. +/// +/// See [`crate::round2::sign`] for documentation on the other parameters. +pub fn sign_with_randomizer_seed( + signing_package: &crate::SigningPackage, + signer_nonces: &crate::round1::SigningNonces, + key_package: &crate::keys::KeyPackage, + randomizer_seed: &[u8], +) -> Result { + frost_rerandomized::sign_with_randomizer_seed::( + signing_package, + signer_nonces, + key_package, + randomizer_seed, + ) +} + +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`]. +/// +/// See [`frost_core::aggregate`] for documentation on the other parameters. +pub fn aggregate( + signing_package: &crate::SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &crate::keys::PublicKeyPackage, + randomized_params: &RandomizedParams, +) -> Result { + frost_rerandomized::aggregate::( + signing_package, + signature_shares, + pubkeys, + randomized_params, + ) +} + +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`] using the given cheater detection strategy. +/// +/// See [`frost_core::aggregate_custom`] for documentation on the other parameters. +pub fn aggregate_custom( + signing_package: &crate::SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &crate::keys::PublicKeyPackage, + cheater_detection: crate::CheaterDetection, + randomized_params: &RandomizedParams, +) -> Result { + frost_rerandomized::aggregate_custom::( + signing_package, + signature_shares, + pubkeys, + cheater_detection, + randomized_params, + ) +} + +/// A randomizer. A random scalar which is used to randomize the key. +pub type Randomizer = frost_rerandomized::Randomizer; + +/// Randomized parameters for a signing instance of randomized FROST. +pub type RandomizedParams = frost_rerandomized::RandomizedParams; diff --git a/frost-ed448/src/rerandomized.rs b/frost-ed448/src/rerandomized.rs new file mode 100644 index 0000000..10d5d01 --- /dev/null +++ b/frost-ed448/src/rerandomized.rs @@ -0,0 +1,65 @@ +//! FROST implementation supporting re-randomizable keys. + +use alloc::collections::btree_map::BTreeMap; + +/// Re-randomized FROST signing using the given `randomizer_seed`, which should +/// be sent from the Coordinator using a confidential channel. +/// +/// See [`crate::round2::sign`] for documentation on the other parameters. +pub fn sign_with_randomizer_seed( + signing_package: &crate::SigningPackage, + signer_nonces: &crate::round1::SigningNonces, + key_package: &crate::keys::KeyPackage, + randomizer_seed: &[u8], +) -> Result { + frost_rerandomized::sign_with_randomizer_seed::( + signing_package, + signer_nonces, + key_package, + randomizer_seed, + ) +} + +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`]. +/// +/// See [`frost_core::aggregate`] for documentation on the other parameters. +pub fn aggregate( + signing_package: &crate::SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &crate::keys::PublicKeyPackage, + randomized_params: &RandomizedParams, +) -> Result { + frost_rerandomized::aggregate::( + signing_package, + signature_shares, + pubkeys, + randomized_params, + ) +} + +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`] using the given cheater detection strategy. +/// +/// See [`frost_core::aggregate_custom`] for documentation on the other parameters. +pub fn aggregate_custom( + signing_package: &crate::SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &crate::keys::PublicKeyPackage, + cheater_detection: crate::CheaterDetection, + randomized_params: &RandomizedParams, +) -> Result { + frost_rerandomized::aggregate_custom::( + signing_package, + signature_shares, + pubkeys, + cheater_detection, + randomized_params, + ) +} + +/// A randomizer. A random scalar which is used to randomize the key. +pub type Randomizer = frost_rerandomized::Randomizer; + +/// Randomized parameters for a signing instance of randomized FROST. +pub type RandomizedParams = frost_rerandomized::RandomizedParams; diff --git a/frost-p256/src/rerandomized.rs b/frost-p256/src/rerandomized.rs new file mode 100644 index 0000000..e5e8b44 --- /dev/null +++ b/frost-p256/src/rerandomized.rs @@ -0,0 +1,65 @@ +//! FROST implementation supporting re-randomizable keys. + +use alloc::collections::btree_map::BTreeMap; + +/// Re-randomized FROST signing using the given `randomizer_seed`, which should +/// be sent from the Coordinator using a confidential channel. +/// +/// See [`crate::round2::sign`] for documentation on the other parameters. +pub fn sign_with_randomizer_seed( + signing_package: &crate::SigningPackage, + signer_nonces: &crate::round1::SigningNonces, + key_package: &crate::keys::KeyPackage, + randomizer_seed: &[u8], +) -> Result { + frost_rerandomized::sign_with_randomizer_seed::( + signing_package, + signer_nonces, + key_package, + randomizer_seed, + ) +} + +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`]. +/// +/// See [`frost_core::aggregate`] for documentation on the other parameters. +pub fn aggregate( + signing_package: &crate::SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &crate::keys::PublicKeyPackage, + randomized_params: &RandomizedParams, +) -> Result { + frost_rerandomized::aggregate::( + signing_package, + signature_shares, + pubkeys, + randomized_params, + ) +} + +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`] using the given cheater detection strategy. +/// +/// See [`frost_core::aggregate_custom`] for documentation on the other parameters. +pub fn aggregate_custom( + signing_package: &crate::SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &crate::keys::PublicKeyPackage, + cheater_detection: crate::CheaterDetection, + randomized_params: &RandomizedParams, +) -> Result { + frost_rerandomized::aggregate_custom::( + signing_package, + signature_shares, + pubkeys, + cheater_detection, + randomized_params, + ) +} + +/// A randomizer. A random scalar which is used to randomize the key. +pub type Randomizer = frost_rerandomized::Randomizer; + +/// Randomized parameters for a signing instance of randomized FROST. +pub type RandomizedParams = frost_rerandomized::RandomizedParams; diff --git a/frost-rerandomized/src/lib.rs b/frost-rerandomized/src/lib.rs index a88e93c..0b5e057 100644 --- a/frost-rerandomized/src/lib.rs +++ b/frost-rerandomized/src/lib.rs @@ -30,10 +30,9 @@ use frost_core::SigningPackage; use frost_core::{ self as frost, keys::{KeyPackage, PublicKeyPackage, SigningShare, VerifyingShare}, - round1::encode_group_commitments, - round1::SigningCommitments, + round1::{encode_group_commitments, SigningCommitments}, serialization::SerializableScalar, - Ciphersuite, Error, Field, Group, Identifier, Scalar, VerifyingKey, + CheaterDetection, Ciphersuite, Error, Field, Group, Identifier, Scalar, VerifyingKey, }; #[cfg(feature = "serde")] @@ -161,9 +160,8 @@ pub fn sign_with_randomizer_seed( frost::round2::sign(signing_package, signer_nonces, &randomized_key_package) } -/// Re-randomized FROST signature share aggregation with the given [`RandomizedParams`], -/// which can be computed from the previously generated randomizer using -/// [`RandomizedParams::from_randomizer`]. +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`]. /// /// See [`frost::aggregate`] for documentation on the other parameters. pub fn aggregate( @@ -183,6 +181,29 @@ where ) } +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`] using the given cheater detection strategy. +/// +/// See [`frost::aggregate_custom`] for documentation on the other parameters. +pub fn aggregate_custom( + signing_package: &frost::SigningPackage, + signature_shares: &BTreeMap, frost::round2::SignatureShare>, + pubkeys: &frost::keys::PublicKeyPackage, + cheater_detection: CheaterDetection, + randomized_params: &RandomizedParams, +) -> Result, Error> +where + C: Ciphersuite, +{ + let randomized_public_key_package = pubkeys.randomize(randomized_params)?; + frost::aggregate_custom( + signing_package, + signature_shares, + &randomized_public_key_package, + cheater_detection, + ) +} + /// A randomizer. A random scalar which is used to randomize the key. #[derive(Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/frost-ristretto255/src/lib.rs b/frost-ristretto255/src/lib.rs index 9951aad..bb13732 100644 --- a/frost-ristretto255/src/lib.rs +++ b/frost-ristretto255/src/lib.rs @@ -22,6 +22,8 @@ use frost_core as frost; #[cfg(test)] mod tests; +pub mod rerandomized; + // Re-exports in our public API #[cfg(feature = "serde")] pub use frost_core::serde; diff --git a/frost-ristretto255/src/rerandomized.rs b/frost-ristretto255/src/rerandomized.rs new file mode 100644 index 0000000..21410f4 --- /dev/null +++ b/frost-ristretto255/src/rerandomized.rs @@ -0,0 +1,65 @@ +//! FROST implementation supporting re-randomizable keys. + +use alloc::collections::btree_map::BTreeMap; + +/// Re-randomized FROST signing using the given `randomizer_seed`, which should +/// be sent from the Coordinator using a confidential channel. +/// +/// See [`crate::round2::sign`] for documentation on the other parameters. +pub fn sign_with_randomizer_seed( + signing_package: &crate::SigningPackage, + signer_nonces: &crate::round1::SigningNonces, + key_package: &crate::keys::KeyPackage, + randomizer_seed: &[u8], +) -> Result { + frost_rerandomized::sign_with_randomizer_seed::( + signing_package, + signer_nonces, + key_package, + randomizer_seed, + ) +} + +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`]. +/// +/// See [`frost_core::aggregate`] for documentation on the other parameters. +pub fn aggregate( + signing_package: &crate::SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &crate::keys::PublicKeyPackage, + randomized_params: &RandomizedParams, +) -> Result { + frost_rerandomized::aggregate::( + signing_package, + signature_shares, + pubkeys, + randomized_params, + ) +} + +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`] using the given cheater detection strategy. +/// +/// See [`frost_core::aggregate_custom`] for documentation on the other parameters. +pub fn aggregate_custom( + signing_package: &crate::SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &crate::keys::PublicKeyPackage, + cheater_detection: crate::CheaterDetection, + randomized_params: &RandomizedParams, +) -> Result { + frost_rerandomized::aggregate_custom::( + signing_package, + signature_shares, + pubkeys, + cheater_detection, + randomized_params, + ) +} + +/// A randomizer. A random scalar which is used to randomize the key. +pub type Randomizer = frost_rerandomized::Randomizer; + +/// Randomized parameters for a signing instance of randomized FROST. +pub type RandomizedParams = frost_rerandomized::RandomizedParams; diff --git a/frost-secp256k1-tr/src/rerandomized.rs b/frost-secp256k1-tr/src/rerandomized.rs new file mode 100644 index 0000000..7135cdb --- /dev/null +++ b/frost-secp256k1-tr/src/rerandomized.rs @@ -0,0 +1,65 @@ +//! FROST implementation supporting re-randomizable keys. + +use alloc::collections::btree_map::BTreeMap; + +/// Re-randomized FROST signing using the given `randomizer_seed`, which should +/// be sent from the Coordinator using a confidential channel. +/// +/// See [`crate::round2::sign`] for documentation on the other parameters. +pub fn sign_with_randomizer_seed( + signing_package: &crate::SigningPackage, + signer_nonces: &crate::round1::SigningNonces, + key_package: &crate::keys::KeyPackage, + randomizer_seed: &[u8], +) -> Result { + frost_rerandomized::sign_with_randomizer_seed::( + signing_package, + signer_nonces, + key_package, + randomizer_seed, + ) +} + +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`]. +/// +/// See [`frost_core::aggregate`] for documentation on the other parameters. +pub fn aggregate( + signing_package: &crate::SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &crate::keys::PublicKeyPackage, + randomized_params: &RandomizedParams, +) -> Result { + frost_rerandomized::aggregate::( + signing_package, + signature_shares, + pubkeys, + randomized_params, + ) +} + +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`] using the given cheater detection strategy. +/// +/// See [`frost_core::aggregate_custom`] for documentation on the other parameters. +pub fn aggregate_custom( + signing_package: &crate::SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &crate::keys::PublicKeyPackage, + cheater_detection: crate::CheaterDetection, + randomized_params: &RandomizedParams, +) -> Result { + frost_rerandomized::aggregate_custom::( + signing_package, + signature_shares, + pubkeys, + cheater_detection, + randomized_params, + ) +} + +/// A randomizer. A random scalar which is used to randomize the key. +pub type Randomizer = frost_rerandomized::Randomizer; + +/// Randomized parameters for a signing instance of randomized FROST. +pub type RandomizedParams = frost_rerandomized::RandomizedParams; diff --git a/frost-secp256k1/src/rerandomized.rs b/frost-secp256k1/src/rerandomized.rs new file mode 100644 index 0000000..7f7fc93 --- /dev/null +++ b/frost-secp256k1/src/rerandomized.rs @@ -0,0 +1,65 @@ +//! FROST implementation supporting re-randomizable keys. + +use alloc::collections::btree_map::BTreeMap; + +/// Re-randomized FROST signing using the given `randomizer_seed`, which should +/// be sent from the Coordinator using a confidential channel. +/// +/// See [`crate::round2::sign`] for documentation on the other parameters. +pub fn sign_with_randomizer_seed( + signing_package: &crate::SigningPackage, + signer_nonces: &crate::round1::SigningNonces, + key_package: &crate::keys::KeyPackage, + randomizer_seed: &[u8], +) -> Result { + frost_rerandomized::sign_with_randomizer_seed::( + signing_package, + signer_nonces, + key_package, + randomizer_seed, + ) +} + +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`]. +/// +/// See [`frost_core::aggregate`] for documentation on the other parameters. +pub fn aggregate( + signing_package: &crate::SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &crate::keys::PublicKeyPackage, + randomized_params: &RandomizedParams, +) -> Result { + frost_rerandomized::aggregate::( + signing_package, + signature_shares, + pubkeys, + randomized_params, + ) +} + +/// Re-randomized FROST signature share aggregation with the given +/// [`RandomizedParams`] using the given cheater detection strategy. +/// +/// See [`frost_core::aggregate_custom`] for documentation on the other parameters. +pub fn aggregate_custom( + signing_package: &crate::SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &crate::keys::PublicKeyPackage, + cheater_detection: crate::CheaterDetection, + randomized_params: &RandomizedParams, +) -> Result { + frost_rerandomized::aggregate_custom::( + signing_package, + signature_shares, + pubkeys, + cheater_detection, + randomized_params, + ) +} + +/// A randomizer. A random scalar which is used to randomize the key. +pub type Randomizer = frost_rerandomized::Randomizer; + +/// Randomized parameters for a signing instance of randomized FROST. +pub type RandomizedParams = frost_rerandomized::RandomizedParams; diff --git a/gencode/src/main.rs b/gencode/src/main.rs index 5f92f38..f88cd7a 100644 --- a/gencode/src/main.rs +++ b/gencode/src/main.rs @@ -355,6 +355,7 @@ fn main() -> ExitCode { for filename in [ "README.md", "dkg.md", + "src/rerandomized.rs", "src/keys/dkg.rs", "src/keys/refresh.rs", "src/keys/repairable.rs", From ee89da3624d55ee1f8d4b08ab5bc98da92963d42 Mon Sep 17 00:00:00 2001 From: Conrado Date: Tue, 27 Jan 2026 13:02:51 -0300 Subject: [PATCH 64/86] change PublicKeyPackage::new() to take min_signers as an Option (#1000) --- frost-core/src/keys.rs | 9 ++++++--- frost-core/src/tests/vectors.rs | 7 +++++-- frost-ed25519/tests/helpers/samples.rs | 2 +- frost-ed25519/tests/recreation_tests.rs | 2 +- frost-ed448/tests/helpers/samples.rs | 2 +- frost-ed448/tests/recreation_tests.rs | 2 +- frost-p256/tests/helpers/samples.rs | 2 +- frost-p256/tests/recreation_tests.rs | 2 +- frost-ristretto255/tests/helpers/samples.rs | 2 +- frost-ristretto255/tests/recreation_tests.rs | 2 +- frost-secp256k1-tr/tests/helpers/samples.rs | 2 +- frost-secp256k1-tr/tests/recreation_tests.rs | 2 +- frost-secp256k1/tests/helpers/samples.rs | 2 +- frost-secp256k1/tests/recreation_tests.rs | 2 +- 14 files changed, 23 insertions(+), 17 deletions(-) diff --git a/frost-core/src/keys.rs b/frost-core/src/keys.rs index cc6fe7a..5389f5e 100644 --- a/frost-core/src/keys.rs +++ b/frost-core/src/keys.rs @@ -747,12 +747,15 @@ where C: Ciphersuite, { /// Create a new [`PublicKeyPackage`] instance. + /// + /// `min_signers` is an `Option` for compatibility with pre-3.0.0 packages. + /// In normal usage, it should always be `Some(value)`. pub fn new( verifying_shares: BTreeMap, VerifyingShare>, verifying_key: VerifyingKey, - min_signers: u16, + min_signers: Option, ) -> Self { - Self::new_internal(verifying_shares, verifying_key, Some(min_signers)) + Self::new_internal(verifying_shares, verifying_key, min_signers) } /// Create a new [`PublicKeyPackage`] instance, allowing not specifying the @@ -788,7 +791,7 @@ where Ok(PublicKeyPackage::new( verifying_keys, VerifyingKey::from_commitment(commitment)?, - commitment.min_signers(), + Some(commitment.min_signers()), )) } diff --git a/frost-core/src/tests/vectors.rs b/frost-core/src/tests/vectors.rs index b3bfd7f..1ca8a97 100644 --- a/frost-core/src/tests/vectors.rs +++ b/frost-core/src/tests/vectors.rs @@ -293,8 +293,11 @@ pub fn check_sign_with_test_vectors(json_vectors: &Value) { .map(|(i, key_package)| (i, *key_package.verifying_share())) .collect(); - let pubkey_package = - frost::keys::PublicKeyPackage::new(verifying_shares, verifying_key, min_signers as u16); + let pubkey_package = frost::keys::PublicKeyPackage::new( + verifying_shares, + verifying_key, + Some(min_signers as u16), + ); //////////////////////////////////////////////////////////////////////////// // Aggregation: collects the signing shares from all participants, diff --git a/frost-ed25519/tests/helpers/samples.rs b/frost-ed25519/tests/helpers/samples.rs index 3ce2f8a..2c6c9c5 100644 --- a/frost-ed25519/tests/helpers/samples.rs +++ b/frost-ed25519/tests/helpers/samples.rs @@ -116,7 +116,7 @@ pub fn public_key_package_new() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key, 2) + PublicKeyPackage::new(verifying_shares, verifying_key, Some(2)) } /// Generate a sample round1::SecretPackage. diff --git a/frost-ed25519/tests/recreation_tests.rs b/frost-ed25519/tests/recreation_tests.rs index 4261cb4..479b7d9 100644 --- a/frost-ed25519/tests/recreation_tests.rs +++ b/frost-ed25519/tests/recreation_tests.rs @@ -116,7 +116,7 @@ fn check_public_key_package_new_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); - let min_signers = public_key_package.min_signers().unwrap(); + let min_signers = public_key_package.min_signers(); let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); diff --git a/frost-ed448/tests/helpers/samples.rs b/frost-ed448/tests/helpers/samples.rs index d4e0452..529633b 100644 --- a/frost-ed448/tests/helpers/samples.rs +++ b/frost-ed448/tests/helpers/samples.rs @@ -116,7 +116,7 @@ pub fn public_key_package_new() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key, 2) + PublicKeyPackage::new(verifying_shares, verifying_key, Some(2)) } /// Generate a sample round1::SecretPackage. diff --git a/frost-ed448/tests/recreation_tests.rs b/frost-ed448/tests/recreation_tests.rs index a0a457a..e206ccf 100644 --- a/frost-ed448/tests/recreation_tests.rs +++ b/frost-ed448/tests/recreation_tests.rs @@ -116,7 +116,7 @@ fn check_public_key_package_new_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); - let min_signers = public_key_package.min_signers().unwrap(); + let min_signers = public_key_package.min_signers(); let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); diff --git a/frost-p256/tests/helpers/samples.rs b/frost-p256/tests/helpers/samples.rs index df9407b..e6a2fd2 100644 --- a/frost-p256/tests/helpers/samples.rs +++ b/frost-p256/tests/helpers/samples.rs @@ -116,7 +116,7 @@ pub fn public_key_package_new() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key, 2) + PublicKeyPackage::new(verifying_shares, verifying_key, Some(2)) } /// Generate a sample round1::SecretPackage. diff --git a/frost-p256/tests/recreation_tests.rs b/frost-p256/tests/recreation_tests.rs index e5816bf..99c39a2 100644 --- a/frost-p256/tests/recreation_tests.rs +++ b/frost-p256/tests/recreation_tests.rs @@ -116,7 +116,7 @@ fn check_public_key_package_new_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); - let min_signers = public_key_package.min_signers().unwrap(); + let min_signers = public_key_package.min_signers(); let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); diff --git a/frost-ristretto255/tests/helpers/samples.rs b/frost-ristretto255/tests/helpers/samples.rs index 0b0a6e3..b0bf586 100644 --- a/frost-ristretto255/tests/helpers/samples.rs +++ b/frost-ristretto255/tests/helpers/samples.rs @@ -116,7 +116,7 @@ pub fn public_key_package_new() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key, 2) + PublicKeyPackage::new(verifying_shares, verifying_key, Some(2)) } /// Generate a sample round1::SecretPackage. diff --git a/frost-ristretto255/tests/recreation_tests.rs b/frost-ristretto255/tests/recreation_tests.rs index fc6947b..0d8e516 100644 --- a/frost-ristretto255/tests/recreation_tests.rs +++ b/frost-ristretto255/tests/recreation_tests.rs @@ -116,7 +116,7 @@ fn check_public_key_package_new_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); - let min_signers = public_key_package.min_signers().unwrap(); + let min_signers = public_key_package.min_signers(); let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); diff --git a/frost-secp256k1-tr/tests/helpers/samples.rs b/frost-secp256k1-tr/tests/helpers/samples.rs index e94ae01..8c39a9e 100644 --- a/frost-secp256k1-tr/tests/helpers/samples.rs +++ b/frost-secp256k1-tr/tests/helpers/samples.rs @@ -116,7 +116,7 @@ pub fn public_key_package_new() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key, 2) + PublicKeyPackage::new(verifying_shares, verifying_key, Some(2)) } /// Generate a sample round1::SecretPackage. diff --git a/frost-secp256k1-tr/tests/recreation_tests.rs b/frost-secp256k1-tr/tests/recreation_tests.rs index bcdd3df..56cd8f4 100644 --- a/frost-secp256k1-tr/tests/recreation_tests.rs +++ b/frost-secp256k1-tr/tests/recreation_tests.rs @@ -116,7 +116,7 @@ fn check_public_key_package_new_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); - let min_signers = public_key_package.min_signers().unwrap(); + let min_signers = public_key_package.min_signers(); let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); diff --git a/frost-secp256k1/tests/helpers/samples.rs b/frost-secp256k1/tests/helpers/samples.rs index 2b1bb8e..d9cbf5b 100644 --- a/frost-secp256k1/tests/helpers/samples.rs +++ b/frost-secp256k1/tests/helpers/samples.rs @@ -116,7 +116,7 @@ pub fn public_key_package_new() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key, 2) + PublicKeyPackage::new(verifying_shares, verifying_key, Some(2)) } /// Generate a sample round1::SecretPackage. diff --git a/frost-secp256k1/tests/recreation_tests.rs b/frost-secp256k1/tests/recreation_tests.rs index f72c154..1e8eb20 100644 --- a/frost-secp256k1/tests/recreation_tests.rs +++ b/frost-secp256k1/tests/recreation_tests.rs @@ -116,7 +116,7 @@ fn check_public_key_package_new_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); - let min_signers = public_key_package.min_signers().unwrap(); + let min_signers = public_key_package.min_signers(); let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); From 893afc7cc7c248df00fb9cb44914ccfd9a6d2fb8 Mon Sep 17 00:00:00 2001 From: Conrado Date: Tue, 27 Jan 2026 18:04:54 -0300 Subject: [PATCH 65/86] Release 3.0.0 (#995) * Release 3.0.0 * Apply suggestions from code review Co-authored-by: natalie * update frost-rerandomized changelog * use 3.0.0-rc.0 instead --------- Co-authored-by: natalie --- Cargo.lock | 16 +++++++------- Cargo.toml | 6 +++--- book/src/dev/release-checklist.md | 11 +++++----- book/src/tutorial/importing.md | 4 ++-- frost-core/CHANGELOG.md | 23 ++++++++++++++++++-- frost-core/README.md | 2 +- frost-ed25519/CHANGELOG.md | 4 ++++ frost-ed448/CHANGELOG.md | 4 ++++ frost-p256/CHANGELOG.md | 4 ++++ frost-rerandomized/CHANGELOG.md | 36 +++++++++++++++++++++++++++++++ frost-ristretto255/CHANGELOG.md | 4 ++++ frost-secp256k1-tr/CHANGELOG.md | 4 ++++ frost-secp256k1/CHANGELOG.md | 4 ++++ gencode/src/main.rs | 1 + 14 files changed, 101 insertions(+), 22 deletions(-) create mode 100644 frost-ed25519/CHANGELOG.md create mode 100644 frost-ed448/CHANGELOG.md create mode 100644 frost-p256/CHANGELOG.md create mode 100644 frost-rerandomized/CHANGELOG.md create mode 100644 frost-ristretto255/CHANGELOG.md create mode 100644 frost-secp256k1-tr/CHANGELOG.md create mode 100644 frost-secp256k1/CHANGELOG.md diff --git a/Cargo.lock b/Cargo.lock index d808235..d49aafc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -527,7 +527,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "frost-core" -version = "2.2.0" +version = "3.0.0-rc.0" dependencies = [ "byteorder", "const-crc32-nostd", @@ -555,7 +555,7 @@ dependencies = [ [[package]] name = "frost-ed25519" -version = "2.2.0" +version = "3.0.0-rc.0" dependencies = [ "criterion", "curve25519-dalek", @@ -577,7 +577,7 @@ dependencies = [ [[package]] name = "frost-ed448" -version = "2.2.0" +version = "3.0.0-rc.0" dependencies = [ "criterion", "document-features", @@ -598,7 +598,7 @@ dependencies = [ [[package]] name = "frost-p256" -version = "2.2.0" +version = "3.0.0-rc.0" dependencies = [ "criterion", "document-features", @@ -619,7 +619,7 @@ dependencies = [ [[package]] name = "frost-rerandomized" -version = "2.2.0" +version = "3.0.0-rc.0" dependencies = [ "derive-getters", "document-features", @@ -630,7 +630,7 @@ dependencies = [ [[package]] name = "frost-ristretto255" -version = "2.2.0" +version = "3.0.0-rc.0" dependencies = [ "criterion", "curve25519-dalek", @@ -652,7 +652,7 @@ dependencies = [ [[package]] name = "frost-secp256k1" -version = "2.2.0" +version = "3.0.0-rc.0" dependencies = [ "criterion", "document-features", @@ -673,7 +673,7 @@ dependencies = [ [[package]] name = "frost-secp256k1-tr" -version = "2.2.0" +version = "3.0.0-rc.0" dependencies = [ "criterion", "document-features", diff --git a/Cargo.toml b/Cargo.toml index dc52fea..9ed7aac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ members = [ # in gencode/src/main.rs. edition = "2021" rust-version = "1.81" -version = "2.2.0" +version = "3.0.0-rc.0" authors = [ "Deirdre Connolly ", "Chelsea Komlo ", @@ -43,8 +43,8 @@ rand_core = "0.6" serde_json = "1.0" tokio = { version = "1.0", features = ["rt", "time", "macros"] } -frost-core = { path = "frost-core", version = "2.2.0", default-features = false } -frost-rerandomized = { path = "frost-rerandomized", version = "2.2.0", default-features = false } +frost-core = { path = "frost-core", version = "3.0.0-rc.0", default-features = false } +frost-rerandomized = { path = "frost-rerandomized", version = "3.0.0-rc.0", default-features = false } [profile.test.package."*"] opt-level = 3 diff --git a/book/src/dev/release-checklist.md b/book/src/dev/release-checklist.md index 79aac86..5005edf 100644 --- a/book/src/dev/release-checklist.md +++ b/book/src/dev/release-checklist.md @@ -24,7 +24,11 @@ releases easier. versions, update those separately as required.) - Decide which version to tag the release with (e.g. v0.3.0), considering - [SemVer](https://doc.rust-lang.org/cargo/reference/semver.html). + [SemVer](https://doc.rust-lang.org/cargo/reference/semver.html). Run `cargo + semver-checks` to check if there are no API changes that break SemVer + compatibility. ([Installation + instructions](https://crates.io/crates/cargo-semver-checks)) Fix issues if + any (i.e. change the version, or revert/adapt the API change). - Create new issue. E.g. [Release v0.4.0](https://github.com/ZcashFoundation/frost/issues/377) @@ -34,11 +38,6 @@ releases easier. - Bump the version of the crates in the root Cargo.toml file. (If they ever get out of sync, you will need to bump in each crate Cargo.toml file.) -- Run `cargo semver-checks` to check if there are no API changes that break - SemVer compatibility. ([Installation - instructions](https://crates.io/crates/cargo-semver-checks)) Fix issues if - any (i.e. change the version, or revert/adapt the API change). - - Bump the version used in the tutorial (importing.md) - Check if the [changelog](https://github.com/ZcashFoundation/frost/blob/main/frost-core/CHANGELOG.md) is up to date and update if required (we’re only keeping the one in frost-core for now). Double check using [FROST releases](https://github.com/ZcashFoundation/frost/releases) which will have a list of all the PRs that have been closed since the last release. Things to include in the changelog will be anything that impacts production code and big documentation changes. I.e. script and test changes should not be included. NOTE: Please add to the changelog whenever you make changes to the library as this will make things simpler for the person in charge of the release. diff --git a/book/src/tutorial/importing.md b/book/src/tutorial/importing.md index 23be045..ca33d6a 100644 --- a/book/src/tutorial/importing.md +++ b/book/src/tutorial/importing.md @@ -6,7 +6,7 @@ Add to your `Cargo.toml` file: ``` [dependencies] -frost-ristretto255 = "2.2.0" +frost-ristretto255 = "3.0.0-rc.0" ``` ## Handling errors @@ -38,7 +38,7 @@ needs to be transmitted. The importing would look like: ``` [dependencies] -frost-ristretto255 = { version = "2.2.0", features = ["serde"] } +frost-ristretto255 = { version = "3.0.0-rc.0", features = ["serde"] } ``` Note that serde usage is optional. Applications can use different encodings, and diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index 3e85019..a1fce40 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -2,8 +2,12 @@ Entries are listed in reverse chronological order. + ## Unreleased + +## 3.0.0-rc.0 + ### Breaking Changes * The `cheater-detection` feature was removed. If you relied on it (either by @@ -12,7 +16,10 @@ Entries are listed in reverse chronological order. the default behaviour is now as if `cheater-detection` was enabled. If you explicitly *did not enable* it, you can avoid cheater detection by calling `aggregate_custom()` with `CheaterDetection::Disabled`. -* The `std` and `nightly` features were removed from all crates +* Changed `InvalidSignatureShare::culprit` to `culprits`; it is now a `Vec`. +* Changed `Error::culprit()` to `culprits()`; is is now a `Vec`. +* Added a `min_signers` argument to `PublicKeyPackage::new()`. +* The `std` and `nightly` features were removed from all crates. * Renamed `frost_core::keys::refresh::refresh_dkg_part_1` to `refresh_dkg_part1`. * Fixed the crate-specific versions of the `refresh` module to be non-generic. * Removed the `min_signers` and `max_signers` arguments from @@ -32,13 +39,25 @@ Entries are listed in reverse chronological order. `VerifiableSecretSharingCommitment`; and returns a `KeyPackage` instead of `SecretShare`. * These changes provide more type safety and are make it more useful since - `SecretPackage`s are not expected to be stored + `SecretPackage`s are not expected to be stored. +* The `Ciphersuite`, `Scalar` and `Element` traits now must implement `Send` and + `Sync`. This should be trivial in most cases. +* The `SignatureSerialization`, `Field::Serialization` and + `Element::Serialization` traits do not need to implement `TryFrom>` + anymore; instead, they must implement `AsMut<[u8]>` and `TryFrom<&[u8]>`. This + should be trivial in most cases since they are often implemented by arrays. ### Additional changes * Added DKG refresh functions to the crate-specific `refresh` modules. * Re-exported the `frost-rerandomized` crate in the ciphersuite functions, e.g. you can call `frost_ristretto255::rerandomized::sign_with_randomizer_seed()`. +* Added the `pre_commitment_aggregate()` and `pre_commitment_sign()` hooks + to the `Ciphersuite` trait. +* Added `aggregate_custom()` function to allow specifying which cheater + detection strategy to use. The original `aggregate()` behaviour is to use + the `CheaterDetection::FirstCheater` strategy. + ## 2.2.0 diff --git a/frost-core/README.md b/frost-core/README.md index e54be2f..2fe7e68 100644 --- a/frost-core/README.md +++ b/frost-core/README.md @@ -8,4 +8,4 @@ For more details, refer to [The ZF FROST Book](https://frost.zfnd.org/). ## Example -See ciphersuite-specific crates, e.g. [`frost_ristretto255`]([../frost_ristretto255](https://crates.io/crates/frost-ristretto255)). +See ciphersuite-specific crates, e.g. [`frost_ristretto255`](https://crates.io/crates/frost-ristretto255). diff --git a/frost-ed25519/CHANGELOG.md b/frost-ed25519/CHANGELOG.md new file mode 100644 index 0000000..b1dedcb --- /dev/null +++ b/frost-ed25519/CHANGELOG.md @@ -0,0 +1,4 @@ +# Changelog + +Refer to the [`frost-core` +changelog](https://github.com/ZcashFoundation/frost/blob/main/frost-core/CHANGELOG.md). \ No newline at end of file diff --git a/frost-ed448/CHANGELOG.md b/frost-ed448/CHANGELOG.md new file mode 100644 index 0000000..b1dedcb --- /dev/null +++ b/frost-ed448/CHANGELOG.md @@ -0,0 +1,4 @@ +# Changelog + +Refer to the [`frost-core` +changelog](https://github.com/ZcashFoundation/frost/blob/main/frost-core/CHANGELOG.md). \ No newline at end of file diff --git a/frost-p256/CHANGELOG.md b/frost-p256/CHANGELOG.md new file mode 100644 index 0000000..b1dedcb --- /dev/null +++ b/frost-p256/CHANGELOG.md @@ -0,0 +1,4 @@ +# Changelog + +Refer to the [`frost-core` +changelog](https://github.com/ZcashFoundation/frost/blob/main/frost-core/CHANGELOG.md). \ No newline at end of file diff --git a/frost-rerandomized/CHANGELOG.md b/frost-rerandomized/CHANGELOG.md new file mode 100644 index 0000000..5a18657 --- /dev/null +++ b/frost-rerandomized/CHANGELOG.md @@ -0,0 +1,36 @@ +# Changelog + +Entries are listed in reverse chronological order. + + +## Unreleased + + +## 3.0.0-rc.0 + +### Breaking Changes + +* The `cheater-detection` feature was removed. If you relied on it (either by + using the default features, or by explicitly enabling it), then you don't have + to do anything (other than not enabling it explicitly if you were doing so); + the default behaviour is now as if `cheater-detection` was enabled. If you + explicitly *did not enable* it, you can avoid cheater detection by calling + `aggregate_custom()` with `CheaterDetection::Disabled`. + +### Added + +There is a new revamped API which was motivated by integration with Zcash +but should have broader application. + +- Added `RandomizedParams::new_from_commitments()` which will generate the + randomizer based on the signing commitments and on some fresh random data. + This is better since all parties will contribute to the randomness of the + randomizer. The random data ("seed") will be returned along with the + `RandomizedParams`. +- Added `RandomizedParams::regenerate_from_seed_and_commitments()` which will + redo the procedure above with a given seed. +- Added `sign_with_randomizer_seed()` which is a helper function that will + rebuild the `RandomizedParams` with a given seed and proceed with the + signing. +- Added `Randomizer::{new_from_commitments(), regenerate_from_seed_and_commitments()}` + which are used by the above and will probably not need to be called directly. diff --git a/frost-ristretto255/CHANGELOG.md b/frost-ristretto255/CHANGELOG.md new file mode 100644 index 0000000..b1dedcb --- /dev/null +++ b/frost-ristretto255/CHANGELOG.md @@ -0,0 +1,4 @@ +# Changelog + +Refer to the [`frost-core` +changelog](https://github.com/ZcashFoundation/frost/blob/main/frost-core/CHANGELOG.md). \ No newline at end of file diff --git a/frost-secp256k1-tr/CHANGELOG.md b/frost-secp256k1-tr/CHANGELOG.md new file mode 100644 index 0000000..b1dedcb --- /dev/null +++ b/frost-secp256k1-tr/CHANGELOG.md @@ -0,0 +1,4 @@ +# Changelog + +Refer to the [`frost-core` +changelog](https://github.com/ZcashFoundation/frost/blob/main/frost-core/CHANGELOG.md). \ No newline at end of file diff --git a/frost-secp256k1/CHANGELOG.md b/frost-secp256k1/CHANGELOG.md new file mode 100644 index 0000000..b1dedcb --- /dev/null +++ b/frost-secp256k1/CHANGELOG.md @@ -0,0 +1,4 @@ +# Changelog + +Refer to the [`frost-core` +changelog](https://github.com/ZcashFoundation/frost/blob/main/frost-core/CHANGELOG.md). \ No newline at end of file diff --git a/gencode/src/main.rs b/gencode/src/main.rs index f88cd7a..9dee2d5 100644 --- a/gencode/src/main.rs +++ b/gencode/src/main.rs @@ -354,6 +354,7 @@ fn main() -> ExitCode { // Generate files based on a template with simple search & replace. for filename in [ "README.md", + "CHANGELOG.md", "dkg.md", "src/rerandomized.rs", "src/keys/dkg.rs", From 179242740b57731eee7f423bf2d8e662282ad6d9 Mon Sep 17 00:00:00 2001 From: Conrado Date: Tue, 27 Jan 2026 18:53:07 -0300 Subject: [PATCH 66/86] book, docs: mention channel requirements for DKG; remove redundancy; expand (#989) * book, docs: mention channel requirements for DKG; remove redundancy; expand * Apply suggestions from code review Co-authored-by: natalie * improve clarity * fix typo * Apply suggestions from code review Co-authored-by: natalie * gencode --------- Co-authored-by: natalie --- book/src/terminology.md | 71 +++++++++++++++++++++++++---------- book/src/tutorial/dkg.md | 9 +++++ frost-core/src/keys/dkg.rs | 6 ++- frost-core/src/lib.rs | 1 - frost-ed25519/dkg.md | 45 +++++++++++----------- frost-ed25519/src/lib.rs | 1 - frost-ed448/dkg.md | 45 +++++++++++----------- frost-ed448/src/lib.rs | 1 - frost-p256/dkg.md | 45 +++++++++++----------- frost-p256/src/lib.rs | 1 - frost-ristretto255/dkg.md | 45 +++++++++++----------- frost-secp256k1-tr/dkg.md | 45 +++++++++++----------- frost-secp256k1-tr/src/lib.rs | 1 - frost-secp256k1/dkg.md | 45 +++++++++++----------- frost-secp256k1/src/lib.rs | 1 - 15 files changed, 204 insertions(+), 158 deletions(-) diff --git a/book/src/terminology.md b/book/src/terminology.md index 1b93053..686dfd0 100644 --- a/book/src/terminology.md +++ b/book/src/terminology.md @@ -3,18 +3,40 @@ ### _Broadcast channel_ A secure broadcast channel in the context of multi-party computation protocols -such as FROST has the following properties: - -1. Consistent. Each participant has the same view of the message sent over the channel. -2. Authenticated. Players know that the message was in fact sent by the claimed sender. In practice, this -requirement is often fulfilled by a PKI. -3. Reliable Delivery. Player i knows that the message it sent was in fact received by the intended participants. -4. Unordered. The channel does not guarantee ordering of messages. - -Possible deployment options: -- Echo-broadcast (Goldwasser-Lindell) -- Posting commitments to an authenticated centralized server that is trusted to - provide a single view to all participants (also known as 'public bulletin board') +such as FROST must have a set of theoretical properties which can be a bit subtle +and depend on the specific protocol being implemented. However, most real +deployments use the protocol from the [Secure Computation Without +Agreement](https://eprint.iacr.org/2002/040) paper, which we describe below, and +which is also referred to as "echo broadcast". It has the following properties: +agreement (if an honest party outputs x, then all honest parties output x or +abort), validity (if the broadcaster is honest, then all honest parties output +the broadcast value) and non-triviality (if all parties are honest, they all +output the broadcast value). + +The echo broadcast works as follows, for a party `P[1]` which wants to broadcast +a value `x` to the other `P[i]` parties for `1 < i <= n` where `n` is the number +of participants: + +1. `P[1]` sends `x` to all other `n-1` parties. +2. For each `P[i]` other than `P[1]`: + 1. Set `x1` to the value received from `P[1]` in step 1, or to `null` if no + value was received. + 2. Send `x1` to the other `n-2` parties (excluding `1` and `i` themselves). + 3. Set `r[j]` to the value that `i` will receive from the other `n-2` parties, + indexed by their index `j`. + 4. Output `x1` if it is equal to every value in `r[j]` for all `j` in the + other `n-2` parties. Otherwise, output `null`. + +In the specific context of FROST, you will need to use the echo broadcast for +each participant to send their round 1 package to the other participants. This +means that you will need to run `n` instances of the echo-broadcast protocol +in parallel! + +As an alternative to using echo-broadcast, other mechanisms are possible +depending on the application. For example, posting commitments (round 1 +packages) to an authenticated centralized server. This server needs to be +trusted to provide a single view to all participants (also known as "public +bulletin board"). ### _Identifier_ @@ -32,15 +54,28 @@ This allows deriving identifiers from usernames or emails, for example. ### _Peer to peer channel_ -Peer-to-peer channels are authenticated, reliable, and unordered, per the -definitions above. Additionally, peer-to-peer channels are _confidential_; i.e., -only participants `i` and `j` are allowed to know the contents of -a message `msg_i,j`. +Peer-to-peer channels are required to send data back and forth between +participants (during DKG) and between coordinator and participants (during +signing) in order to use FROST. These channels have different requirements +in different scenarios: + +- They need to be authenticated when sending DKG messages, and when sending + signing messages if cheater detection is required. In this context, + "authenticated" means that the recipient must have assurance on who is the + sender of a message, using e.g. digital signatures. +- They need to be confidential when sending DKG messages, and when sending + signing messages if the messages being signed are confidential. In this + context, "confidential" means that no other party listening to the + communication can have access to the contents, using e.g. encryption. + +In practice there are multiple possible deployment options to achieve +authentication and confidentiality: -Possible deployment options: - Mutually authenticated TLS +- Noise protocol - Wireguard + ### _Threshold secret sharing_ Threshold secret sharing does not require a broadcast channel because the dealer is fully trusted. @@ -51,5 +86,3 @@ Verifiable secret sharing requires a broadcast channel because the dealer is _not_ fully trusted: keygen participants verify the VSS commitment which is transmitted over the broadcast channel before accepting the shares distributed from the dealer, to ensure all participants have the same view of the commitment. - - diff --git a/book/src/tutorial/dkg.md b/book/src/tutorial/dkg.md index 4bbd543..91f0a4d 100644 --- a/book/src/tutorial/dkg.md +++ b/book/src/tutorial/dkg.md @@ -1,5 +1,9 @@ # Distributed Key Generation +ZF FROST supports a variant of the DKG described in the [original FROST +paper](https://eprint.iacr.org/2020/852.pdf) (the only difference is the absence +of the context string which was deemed unnecessary after further analysis). + The diagram below shows the distributed key generation process. Dashed lines represent data being sent through an [authenticated and confidential communication @@ -47,6 +51,11 @@ the protocol is aborted. Check the linked [Terminology section](https://frost.zfnd.org/terminology.html#broadcast-channel) for more details. +In the context of the DKG, `n` broadcast channels will need to be set up; one +for each participant. So each participant will broadcast their round 1 package +to the other participants, and each participant needs to handle the broadcast +from the other `n-1` participants. + **Failure in using a proper broadcast channel will make the key generation insecure.** ``` diff --git a/frost-core/src/keys/dkg.rs b/frost-core/src/keys/dkg.rs index 5f56256..ad9d9a7 100644 --- a/frost-core/src/keys/dkg.rs +++ b/frost-core/src/keys/dkg.rs @@ -2,7 +2,11 @@ //! //! The DKG module supports generating FROST key shares in a distributed manner, //! without a trusted dealer, via two rounds of communication between all -//! participants. +//! participants (with one round requiring a broadcast channel, so totalling +//! three rounds of communication if using echo broadcast). +//! +//! For a higher level tutorial on how to use it, refer to the [ZF FROST +//! Book](https://frost.zfnd.org/tutorial/dkg.html). //! //! This implements FROST KeyGen from the original [FROST paper], specifically //! Figure 1. This protocol is a variant of [Pedersen's DKG] that additionally diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index 8c1b54f..e0766d6 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -6,7 +6,6 @@ #![forbid(unsafe_code)] #![deny(clippy::indexing_slicing)] #![deny(clippy::unwrap_used)] -#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("../README.md")] #![doc = document_features::document_features!()] diff --git a/frost-ed25519/dkg.md b/frost-ed25519/dkg.md index de3f6ae..244d9ba 100644 --- a/frost-ed25519/dkg.md +++ b/frost-ed25519/dkg.md @@ -3,27 +3,20 @@ The DKG module supports generating FROST key shares in a distributed manner, without a trusted dealer. -Before starting, each participant needs a unique identifier, which can be built from -a `u16`. The process in which these identifiers are allocated is up to the application. - -The distributed key generation process has 3 parts, with 2 communication rounds -between them, in which each participant needs to send a "package" to every other -participant. In the first round, each participant sends the same package -(a [`round1::Package`]) to every other. In the second round, each receiver gets -their own package (a [`round2::Package`]). - -Between part 1 and 2, each participant needs to hold onto a [`round1::SecretPackage`] -that MUST be kept secret. Between part 2 and 3, each participant needs to hold -onto a [`round2::SecretPackage`]. - -After the third part, each participant will get a [`KeyPackage`] with their -long-term secret share that must be kept secret, and a [`PublicKeyPackage`] -that is public (and will be the same between all participants). With those -they can proceed to sign messages with FROST. - +For a higher level tutorial on how to use it, refer to the [ZF FROST +Book](https://frost.zfnd.org/tutorial/dkg.html). ## Example +This example shows the whole procedure in a single program. Of course, in +practice, each participant will run their own part in their own devices and +packages will need to be sent between them, respecting the DKG requirements of +using [authenticated and confidential communication +channels](https://frost.zfnd.org/terminology.html#peer-to-peer-channel), +additionally with a [**broadcast +channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) for the +first round of communication to ensure all participants have the same value. + ```rust # // ANCHOR: dkg_import use std::collections::BTreeMap; @@ -47,7 +40,10 @@ let mut round1_secret_packages = BTreeMap::new(); // Keep track of all round 1 packages sent to the given participant. // This is used to simulate the broadcast; in practice the packages -// will be sent through some communication channel. +// will be sent through a [**broadcast +// channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) +// on top of an [authenticated and confidential communication +// channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). let mut received_round1_packages = BTreeMap::new(); // For each participant, perform the first part of the DKG protocol. @@ -69,7 +65,10 @@ for participant_index in 1..=max_signers { // "Send" the round 1 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be - // sent through some communication channel. + // sent through a [**broadcast + // channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) + // on top of an [authenticated and confidential communication + // channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). for receiver_participant_index in 1..=max_signers { if receiver_participant_index == participant_index { continue; @@ -95,7 +94,8 @@ let mut round2_secret_packages = BTreeMap::new(); // Keep track of all round 2 packages sent to the given participant. // This is used to simulate the broadcast; in practice the packages -// will be sent through some communication channel. +// will be sent through an [authenticated and confidential communication +// channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). let mut received_round2_packages = BTreeMap::new(); // For each participant, perform the second part of the DKG protocol. @@ -117,7 +117,8 @@ for participant_index in 1..=max_signers { // "Send" the round 2 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be - // sent through some communication channel. + // sent through an [authenticated and confidential communication + // channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). // Note that, in contrast to the previous part, here each other participant // gets its own specific package. for (receiver_identifier, round2_package) in round2_packages { diff --git a/frost-ed25519/src/lib.rs b/frost-ed25519/src/lib.rs index 6d75602..f3bdae7 100644 --- a/frost-ed25519/src/lib.rs +++ b/frost-ed25519/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] #![allow(non_snake_case)] #![deny(missing_docs)] -#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("../README.md")] #![doc = document_features::document_features!()] diff --git a/frost-ed448/dkg.md b/frost-ed448/dkg.md index 0399378..ff304b9 100644 --- a/frost-ed448/dkg.md +++ b/frost-ed448/dkg.md @@ -3,27 +3,20 @@ The DKG module supports generating FROST key shares in a distributed manner, without a trusted dealer. -Before starting, each participant needs a unique identifier, which can be built from -a `u16`. The process in which these identifiers are allocated is up to the application. - -The distributed key generation process has 3 parts, with 2 communication rounds -between them, in which each participant needs to send a "package" to every other -participant. In the first round, each participant sends the same package -(a [`round1::Package`]) to every other. In the second round, each receiver gets -their own package (a [`round2::Package`]). - -Between part 1 and 2, each participant needs to hold onto a [`round1::SecretPackage`] -that MUST be kept secret. Between part 2 and 3, each participant needs to hold -onto a [`round2::SecretPackage`]. - -After the third part, each participant will get a [`KeyPackage`] with their -long-term secret share that must be kept secret, and a [`PublicKeyPackage`] -that is public (and will be the same between all participants). With those -they can proceed to sign messages with FROST. - +For a higher level tutorial on how to use it, refer to the [ZF FROST +Book](https://frost.zfnd.org/tutorial/dkg.html). ## Example +This example shows the whole procedure in a single program. Of course, in +practice, each participant will run their own part in their own devices and +packages will need to be sent between them, respecting the DKG requirements of +using [authenticated and confidential communication +channels](https://frost.zfnd.org/terminology.html#peer-to-peer-channel), +additionally with a [**broadcast +channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) for the +first round of communication to ensure all participants have the same value. + ```rust # // ANCHOR: dkg_import use std::collections::BTreeMap; @@ -47,7 +40,10 @@ let mut round1_secret_packages = BTreeMap::new(); // Keep track of all round 1 packages sent to the given participant. // This is used to simulate the broadcast; in practice the packages -// will be sent through some communication channel. +// will be sent through a [**broadcast +// channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) +// on top of an [authenticated and confidential communication +// channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). let mut received_round1_packages = BTreeMap::new(); // For each participant, perform the first part of the DKG protocol. @@ -69,7 +65,10 @@ for participant_index in 1..=max_signers { // "Send" the round 1 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be - // sent through some communication channel. + // sent through a [**broadcast + // channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) + // on top of an [authenticated and confidential communication + // channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). for receiver_participant_index in 1..=max_signers { if receiver_participant_index == participant_index { continue; @@ -95,7 +94,8 @@ let mut round2_secret_packages = BTreeMap::new(); // Keep track of all round 2 packages sent to the given participant. // This is used to simulate the broadcast; in practice the packages -// will be sent through some communication channel. +// will be sent through an [authenticated and confidential communication +// channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). let mut received_round2_packages = BTreeMap::new(); // For each participant, perform the second part of the DKG protocol. @@ -117,7 +117,8 @@ for participant_index in 1..=max_signers { // "Send" the round 2 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be - // sent through some communication channel. + // sent through an [authenticated and confidential communication + // channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). // Note that, in contrast to the previous part, here each other participant // gets its own specific package. for (receiver_identifier, round2_package) in round2_packages { diff --git a/frost-ed448/src/lib.rs b/frost-ed448/src/lib.rs index 3c45775..154f445 100644 --- a/frost-ed448/src/lib.rs +++ b/frost-ed448/src/lib.rs @@ -1,6 +1,5 @@ #![allow(non_snake_case)] #![deny(missing_docs)] -#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("../README.md")] #![doc = document_features::document_features!()] diff --git a/frost-p256/dkg.md b/frost-p256/dkg.md index afb4bd4..cd64fd9 100644 --- a/frost-p256/dkg.md +++ b/frost-p256/dkg.md @@ -3,27 +3,20 @@ The DKG module supports generating FROST key shares in a distributed manner, without a trusted dealer. -Before starting, each participant needs a unique identifier, which can be built from -a `u16`. The process in which these identifiers are allocated is up to the application. - -The distributed key generation process has 3 parts, with 2 communication rounds -between them, in which each participant needs to send a "package" to every other -participant. In the first round, each participant sends the same package -(a [`round1::Package`]) to every other. In the second round, each receiver gets -their own package (a [`round2::Package`]). - -Between part 1 and 2, each participant needs to hold onto a [`round1::SecretPackage`] -that MUST be kept secret. Between part 2 and 3, each participant needs to hold -onto a [`round2::SecretPackage`]. - -After the third part, each participant will get a [`KeyPackage`] with their -long-term secret share that must be kept secret, and a [`PublicKeyPackage`] -that is public (and will be the same between all participants). With those -they can proceed to sign messages with FROST. - +For a higher level tutorial on how to use it, refer to the [ZF FROST +Book](https://frost.zfnd.org/tutorial/dkg.html). ## Example +This example shows the whole procedure in a single program. Of course, in +practice, each participant will run their own part in their own devices and +packages will need to be sent between them, respecting the DKG requirements of +using [authenticated and confidential communication +channels](https://frost.zfnd.org/terminology.html#peer-to-peer-channel), +additionally with a [**broadcast +channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) for the +first round of communication to ensure all participants have the same value. + ```rust # // ANCHOR: dkg_import use std::collections::BTreeMap; @@ -47,7 +40,10 @@ let mut round1_secret_packages = BTreeMap::new(); // Keep track of all round 1 packages sent to the given participant. // This is used to simulate the broadcast; in practice the packages -// will be sent through some communication channel. +// will be sent through a [**broadcast +// channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) +// on top of an [authenticated and confidential communication +// channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). let mut received_round1_packages = BTreeMap::new(); // For each participant, perform the first part of the DKG protocol. @@ -69,7 +65,10 @@ for participant_index in 1..=max_signers { // "Send" the round 1 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be - // sent through some communication channel. + // sent through a [**broadcast + // channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) + // on top of an [authenticated and confidential communication + // channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). for receiver_participant_index in 1..=max_signers { if receiver_participant_index == participant_index { continue; @@ -95,7 +94,8 @@ let mut round2_secret_packages = BTreeMap::new(); // Keep track of all round 2 packages sent to the given participant. // This is used to simulate the broadcast; in practice the packages -// will be sent through some communication channel. +// will be sent through an [authenticated and confidential communication +// channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). let mut received_round2_packages = BTreeMap::new(); // For each participant, perform the second part of the DKG protocol. @@ -117,7 +117,8 @@ for participant_index in 1..=max_signers { // "Send" the round 2 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be - // sent through some communication channel. + // sent through an [authenticated and confidential communication + // channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). // Note that, in contrast to the previous part, here each other participant // gets its own specific package. for (receiver_identifier, round2_package) in round2_packages { diff --git a/frost-p256/src/lib.rs b/frost-p256/src/lib.rs index c99a26f..1a0b3c8 100644 --- a/frost-p256/src/lib.rs +++ b/frost-p256/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] #![allow(non_snake_case)] #![deny(missing_docs)] -#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("../README.md")] #![doc = document_features::document_features!()] diff --git a/frost-ristretto255/dkg.md b/frost-ristretto255/dkg.md index 86995d7..fcba23b 100644 --- a/frost-ristretto255/dkg.md +++ b/frost-ristretto255/dkg.md @@ -3,27 +3,20 @@ The DKG module supports generating FROST key shares in a distributed manner, without a trusted dealer. -Before starting, each participant needs a unique identifier, which can be built from -a `u16`. The process in which these identifiers are allocated is up to the application. - -The distributed key generation process has 3 parts, with 2 communication rounds -between them, in which each participant needs to send a "package" to every other -participant. In the first round, each participant sends the same package -(a [`round1::Package`]) to every other. In the second round, each receiver gets -their own package (a [`round2::Package`]). - -Between part 1 and 2, each participant needs to hold onto a [`round1::SecretPackage`] -that MUST be kept secret. Between part 2 and 3, each participant needs to hold -onto a [`round2::SecretPackage`]. - -After the third part, each participant will get a [`KeyPackage`] with their -long-term secret share that must be kept secret, and a [`PublicKeyPackage`] -that is public (and will be the same between all participants). With those -they can proceed to sign messages with FROST. - +For a higher level tutorial on how to use it, refer to the [ZF FROST +Book](https://frost.zfnd.org/tutorial/dkg.html). ## Example +This example shows the whole procedure in a single program. Of course, in +practice, each participant will run their own part in their own devices and +packages will need to be sent between them, respecting the DKG requirements of +using [authenticated and confidential communication +channels](https://frost.zfnd.org/terminology.html#peer-to-peer-channel), +additionally with a [**broadcast +channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) for the +first round of communication to ensure all participants have the same value. + ```rust # // ANCHOR: dkg_import use std::collections::BTreeMap; @@ -47,7 +40,10 @@ let mut round1_secret_packages = BTreeMap::new(); // Keep track of all round 1 packages sent to the given participant. // This is used to simulate the broadcast; in practice the packages -// will be sent through some communication channel. +// will be sent through a [**broadcast +// channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) +// on top of an [authenticated and confidential communication +// channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). let mut received_round1_packages = BTreeMap::new(); // For each participant, perform the first part of the DKG protocol. @@ -69,7 +65,10 @@ for participant_index in 1..=max_signers { // "Send" the round 1 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be - // sent through some communication channel. + // sent through a [**broadcast + // channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) + // on top of an [authenticated and confidential communication + // channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). for receiver_participant_index in 1..=max_signers { if receiver_participant_index == participant_index { continue; @@ -95,7 +94,8 @@ let mut round2_secret_packages = BTreeMap::new(); // Keep track of all round 2 packages sent to the given participant. // This is used to simulate the broadcast; in practice the packages -// will be sent through some communication channel. +// will be sent through an [authenticated and confidential communication +// channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). let mut received_round2_packages = BTreeMap::new(); // For each participant, perform the second part of the DKG protocol. @@ -117,7 +117,8 @@ for participant_index in 1..=max_signers { // "Send" the round 2 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be - // sent through some communication channel. + // sent through an [authenticated and confidential communication + // channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). // Note that, in contrast to the previous part, here each other participant // gets its own specific package. for (receiver_identifier, round2_package) in round2_packages { diff --git a/frost-secp256k1-tr/dkg.md b/frost-secp256k1-tr/dkg.md index 31a96ac..e0be393 100644 --- a/frost-secp256k1-tr/dkg.md +++ b/frost-secp256k1-tr/dkg.md @@ -3,27 +3,20 @@ The DKG module supports generating FROST key shares in a distributed manner, without a trusted dealer. -Before starting, each participant needs a unique identifier, which can be built from -a `u16`. The process in which these identifiers are allocated is up to the application. - -The distributed key generation process has 3 parts, with 2 communication rounds -between them, in which each participant needs to send a "package" to every other -participant. In the first round, each participant sends the same package -(a [`round1::Package`]) to every other. In the second round, each receiver gets -their own package (a [`round2::Package`]). - -Between part 1 and 2, each participant needs to hold onto a [`round1::SecretPackage`] -that MUST be kept secret. Between part 2 and 3, each participant needs to hold -onto a [`round2::SecretPackage`]. - -After the third part, each participant will get a [`KeyPackage`] with their -long-term secret share that must be kept secret, and a [`PublicKeyPackage`] -that is public (and will be the same between all participants). With those -they can proceed to sign messages with FROST. - +For a higher level tutorial on how to use it, refer to the [ZF FROST +Book](https://frost.zfnd.org/tutorial/dkg.html). ## Example +This example shows the whole procedure in a single program. Of course, in +practice, each participant will run their own part in their own devices and +packages will need to be sent between them, respecting the DKG requirements of +using [authenticated and confidential communication +channels](https://frost.zfnd.org/terminology.html#peer-to-peer-channel), +additionally with a [**broadcast +channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) for the +first round of communication to ensure all participants have the same value. + ```rust # // ANCHOR: dkg_import use std::collections::BTreeMap; @@ -47,7 +40,10 @@ let mut round1_secret_packages = BTreeMap::new(); // Keep track of all round 1 packages sent to the given participant. // This is used to simulate the broadcast; in practice the packages -// will be sent through some communication channel. +// will be sent through a [**broadcast +// channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) +// on top of an [authenticated and confidential communication +// channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). let mut received_round1_packages = BTreeMap::new(); // For each participant, perform the first part of the DKG protocol. @@ -69,7 +65,10 @@ for participant_index in 1..=max_signers { // "Send" the round 1 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be - // sent through some communication channel. + // sent through a [**broadcast + // channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) + // on top of an [authenticated and confidential communication + // channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). for receiver_participant_index in 1..=max_signers { if receiver_participant_index == participant_index { continue; @@ -95,7 +94,8 @@ let mut round2_secret_packages = BTreeMap::new(); // Keep track of all round 2 packages sent to the given participant. // This is used to simulate the broadcast; in practice the packages -// will be sent through some communication channel. +// will be sent through an [authenticated and confidential communication +// channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). let mut received_round2_packages = BTreeMap::new(); // For each participant, perform the second part of the DKG protocol. @@ -117,7 +117,8 @@ for participant_index in 1..=max_signers { // "Send" the round 2 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be - // sent through some communication channel. + // sent through an [authenticated and confidential communication + // channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). // Note that, in contrast to the previous part, here each other participant // gets its own specific package. for (receiver_identifier, round2_package) in round2_packages { diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs index d4e5f7e..a376fbe 100644 --- a/frost-secp256k1-tr/src/lib.rs +++ b/frost-secp256k1-tr/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] #![allow(non_snake_case)] #![deny(missing_docs)] -#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("../README.md")] #![doc = document_features::document_features!()] diff --git a/frost-secp256k1/dkg.md b/frost-secp256k1/dkg.md index 24c6a53..48d7681 100644 --- a/frost-secp256k1/dkg.md +++ b/frost-secp256k1/dkg.md @@ -3,27 +3,20 @@ The DKG module supports generating FROST key shares in a distributed manner, without a trusted dealer. -Before starting, each participant needs a unique identifier, which can be built from -a `u16`. The process in which these identifiers are allocated is up to the application. - -The distributed key generation process has 3 parts, with 2 communication rounds -between them, in which each participant needs to send a "package" to every other -participant. In the first round, each participant sends the same package -(a [`round1::Package`]) to every other. In the second round, each receiver gets -their own package (a [`round2::Package`]). - -Between part 1 and 2, each participant needs to hold onto a [`round1::SecretPackage`] -that MUST be kept secret. Between part 2 and 3, each participant needs to hold -onto a [`round2::SecretPackage`]. - -After the third part, each participant will get a [`KeyPackage`] with their -long-term secret share that must be kept secret, and a [`PublicKeyPackage`] -that is public (and will be the same between all participants). With those -they can proceed to sign messages with FROST. - +For a higher level tutorial on how to use it, refer to the [ZF FROST +Book](https://frost.zfnd.org/tutorial/dkg.html). ## Example +This example shows the whole procedure in a single program. Of course, in +practice, each participant will run their own part in their own devices and +packages will need to be sent between them, respecting the DKG requirements of +using [authenticated and confidential communication +channels](https://frost.zfnd.org/terminology.html#peer-to-peer-channel), +additionally with a [**broadcast +channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) for the +first round of communication to ensure all participants have the same value. + ```rust # // ANCHOR: dkg_import use std::collections::BTreeMap; @@ -47,7 +40,10 @@ let mut round1_secret_packages = BTreeMap::new(); // Keep track of all round 1 packages sent to the given participant. // This is used to simulate the broadcast; in practice the packages -// will be sent through some communication channel. +// will be sent through a [**broadcast +// channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) +// on top of an [authenticated and confidential communication +// channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). let mut received_round1_packages = BTreeMap::new(); // For each participant, perform the first part of the DKG protocol. @@ -69,7 +65,10 @@ for participant_index in 1..=max_signers { // "Send" the round 1 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be - // sent through some communication channel. + // sent through a [**broadcast + // channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) + // on top of an [authenticated and confidential communication + // channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). for receiver_participant_index in 1..=max_signers { if receiver_participant_index == participant_index { continue; @@ -95,7 +94,8 @@ let mut round2_secret_packages = BTreeMap::new(); // Keep track of all round 2 packages sent to the given participant. // This is used to simulate the broadcast; in practice the packages -// will be sent through some communication channel. +// will be sent through an [authenticated and confidential communication +// channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). let mut received_round2_packages = BTreeMap::new(); // For each participant, perform the second part of the DKG protocol. @@ -117,7 +117,8 @@ for participant_index in 1..=max_signers { // "Send" the round 2 package to all other participants. In this // test this is simulated using a BTreeMap; in practice this will be - // sent through some communication channel. + // sent through an [authenticated and confidential communication + // channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). // Note that, in contrast to the previous part, here each other participant // gets its own specific package. for (receiver_identifier, round2_package) in round2_packages { diff --git a/frost-secp256k1/src/lib.rs b/frost-secp256k1/src/lib.rs index ee9b87a..991b745 100644 --- a/frost-secp256k1/src/lib.rs +++ b/frost-secp256k1/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] #![allow(non_snake_case)] #![deny(missing_docs)] -#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("../README.md")] #![doc = document_features::document_features!()] From 3fa76ef6fb0d1b05af838b54d109df688762603f Mon Sep 17 00:00:00 2001 From: natalie Date: Wed, 28 Jan 2026 14:03:52 +0000 Subject: [PATCH 67/86] Clippy fixes (#1006) * Clippy fixes * fix cargo package warnings --------- Co-authored-by: Conrado Gouvea --- Cargo.lock | 171 +++++++++++++++++----------------- frost-core/src/lib.rs | 4 +- frost-core/src/round1.rs | 1 - frost-ed25519/src/lib.rs | 2 +- frost-p256/src/lib.rs | 2 +- frost-ristretto255/src/lib.rs | 2 +- frost-secp256k1-tr/src/lib.rs | 2 +- frost-secp256k1/src/lib.rs | 2 +- 8 files changed, 92 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d49aafc..2ee992f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,9 +52,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bit-set" @@ -73,15 +73,15 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitcoin-io" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" [[package]] name = "bitcoin_hashes" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" dependencies = [ "bitcoin-io", "hex-conservative", @@ -104,9 +104,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byteorder" @@ -122,9 +122,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.46" +version = "1.2.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" dependencies = [ "find-msvc-tools", "shlex", @@ -165,18 +165,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.53" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "3e34525d5bbbd55da2bb745d34b36121baac88d07619a9a09cfcf4a6c0832785" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "59a20016a20a3da95bef50ec7238dbd09baeef4311dcdd38ec15aba69812fb61" dependencies = [ "anstyle", "clap_lex", @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "cobs" @@ -314,9 +314,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -515,9 +515,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "fnv" @@ -703,9 +703,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -714,9 +714,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "libc", @@ -788,23 +788,24 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" dependencies = [ "arrayvec", ] [[package]] name = "insta" -version = "1.44.0" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b246c455fbf8e7bdda56a226b525b24b601c0bbe15458beb72412678319cda5a" +checksum = "248b42847813a1550dafd15296fd9748c651d0c32194559dbc05d804d54b21e8" dependencies = [ "console", "once_cell", "serde", "similar", + "tempfile", ] [[package]] @@ -836,15 +837,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -877,9 +878,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "linux-raw-sys" @@ -1016,9 +1017,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1050,9 +1051,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.42" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -1081,7 +1082,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -1101,7 +1102,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -1110,14 +1111,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", ] @@ -1128,7 +1129,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -1191,9 +1192,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags", "errno", @@ -1220,12 +1221,6 @@ dependencies = [ "wait-timeout", ] -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - [[package]] name = "same-file" version = "1.0.6" @@ -1312,15 +1307,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -1408,9 +1403,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.110" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -1419,9 +1414,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom 0.3.4", @@ -1432,18 +1427,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -1462,9 +1457,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "pin-project-lite", "tokio-macros", @@ -1543,18 +1538,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -1565,9 +1560,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1575,9 +1570,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", @@ -1588,18 +1583,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -1704,24 +1699,24 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22" dependencies = [ "proc-macro2", "quote", @@ -1739,11 +1734,17 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index e0766d6..a0774e1 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -215,9 +215,7 @@ where /// A list of binding factors and their associated identifiers. #[derive(Clone)] -#[cfg_attr(feature = "internals", visibility::make(pub))] -#[cfg_attr(docsrs, doc(cfg(feature = "internals")))] -pub(crate) struct BindingFactorList(BTreeMap, BindingFactor>); +pub struct BindingFactorList(BTreeMap, BindingFactor>); impl BindingFactorList where diff --git a/frost-core/src/round1.rs b/frost-core/src/round1.rs index e37c249..d2468fd 100644 --- a/frost-core/src/round1.rs +++ b/frost-core/src/round1.rs @@ -396,7 +396,6 @@ impl GroupCommitmentShare { /// commitment list. /// /// [`encode_group_commitment_list()`]: https://datatracker.ietf.org/doc/html/rfc9591#name-list-operations -#[cfg(feature = "internals")] #[cfg_attr(feature = "internals", visibility::make(pub))] #[cfg_attr(docsrs, doc(cfg(feature = "internals")))] pub(super) fn encode_group_commitments( diff --git a/frost-ed25519/src/lib.rs b/frost-ed25519/src/lib.rs index f3bdae7..cb7a818 100644 --- a/frost-ed25519/src/lib.rs +++ b/frost-ed25519/src/lib.rs @@ -144,7 +144,7 @@ fn hash_to_array(inputs: &[&[u8]]) -> [u8; 64] { h.update(i); } let mut output = [0u8; 64]; - output.copy_from_slice(h.finalize().as_slice()); + output.copy_from_slice(h.finalize().as_ref()); output } diff --git a/frost-p256/src/lib.rs b/frost-p256/src/lib.rs index 1a0b3c8..42aa94f 100644 --- a/frost-p256/src/lib.rs +++ b/frost-p256/src/lib.rs @@ -153,7 +153,7 @@ fn hash_to_array(inputs: &[&[u8]]) -> [u8; 32] { h.update(i); } let mut output = [0u8; 32]; - output.copy_from_slice(h.finalize().as_slice()); + output.copy_from_slice(h.finalize().as_ref()); output } diff --git a/frost-ristretto255/src/lib.rs b/frost-ristretto255/src/lib.rs index bb13732..490e34c 100644 --- a/frost-ristretto255/src/lib.rs +++ b/frost-ristretto255/src/lib.rs @@ -133,7 +133,7 @@ fn hash_to_array(inputs: &[&[u8]]) -> [u8; 64] { h.update(i); } let mut output = [0u8; 64]; - output.copy_from_slice(h.finalize().as_slice()); + output.copy_from_slice(h.finalize().as_ref()); output } diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs index a376fbe..7800d14 100644 --- a/frost-secp256k1-tr/src/lib.rs +++ b/frost-secp256k1-tr/src/lib.rs @@ -162,7 +162,7 @@ fn hash_to_array(inputs: &[&[u8]]) -> [u8; 32] { h.update(i); } let mut output = [0u8; 32]; - output.copy_from_slice(h.finalize().as_slice()); + output.copy_from_slice(h.finalize().as_ref()); output } diff --git a/frost-secp256k1/src/lib.rs b/frost-secp256k1/src/lib.rs index 991b745..bebcd1f 100644 --- a/frost-secp256k1/src/lib.rs +++ b/frost-secp256k1/src/lib.rs @@ -153,7 +153,7 @@ fn hash_to_array(inputs: &[&[u8]]) -> [u8; 32] { h.update(i); } let mut output = [0u8; 32]; - output.copy_from_slice(h.finalize().as_slice()); + output.copy_from_slice(h.finalize().as_ref()); output } From 8315231e61462ccaa2ea2ce96552063408c6d306 Mon Sep 17 00:00:00 2001 From: Conrado Date: Wed, 28 Jan 2026 11:13:29 -0300 Subject: [PATCH 68/86] fix issues from specific feature combinations (#1008) --- frost-core/Cargo.toml | 1 + frost-core/src/serialization.rs | 3 +-- frost-core/src/tests/ciphersuite_generic.rs | 1 + frost-core/src/tests/refresh.rs | 8 +++++--- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/frost-core/Cargo.toml b/frost-core/Cargo.toml index a514782..05aa3ad 100644 --- a/frost-core/Cargo.toml +++ b/frost-core/Cargo.toml @@ -48,6 +48,7 @@ proptest.workspace = true rand.workspace = true rand_chacha.workspace = true serde_json.workspace = true +tokio.workspace = true [features] diff --git a/frost-core/src/serialization.rs b/frost-core/src/serialization.rs index 01ca843..8e310fd 100644 --- a/frost-core/src/serialization.rs +++ b/frost-core/src/serialization.rs @@ -2,7 +2,6 @@ #[cfg(feature = "serde")] use alloc::collections::BTreeMap; -use alloc::string::String; use alloc::vec::Vec; #[cfg(feature = "serde")] use core::fmt::Formatter; @@ -326,7 +325,7 @@ where b"verifying_key" => Ok(Field::Field2), b"min_signers" => Ok(Field::Field3), _ => { - let __value = &String::from_utf8_lossy(__value); + let __value = &alloc::string::String::from_utf8_lossy(__value); Err(serde::de::Error::unknown_field(__value, FIELDS)) } } diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs index a15bd25..e769e32 100644 --- a/frost-core/src/tests/ciphersuite_generic.rs +++ b/frost-core/src/tests/ciphersuite_generic.rs @@ -1,5 +1,6 @@ //! Ciphersuite-generic test functions. #![allow(clippy::type_complexity)] +#![cfg(feature = "serialization")] use alloc::{borrow::ToOwned, collections::BTreeMap, vec::Vec}; use rand_core::{CryptoRng, RngCore}; diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index 3d50d59..8f2d56f 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -1,4 +1,5 @@ //! Test for Refreshing shares +#![cfg(feature = "serialization")] use rand_core::{CryptoRng, RngCore}; @@ -7,10 +8,11 @@ use crate::keys::generate_with_dealer; use crate::keys::refresh::{ compute_refreshing_shares, refresh_dkg_part1, refresh_dkg_part2, refresh_share, }; -#[cfg(feature = "serialization")] -use crate::keys::{PublicKeyPackage, SecretShare}; use crate::{self as frost}; -use crate::{keys::KeyPackage, Ciphersuite, Error, Identifier, Signature, VerifyingKey}; +use crate::{ + keys::{KeyPackage, PublicKeyPackage, SecretShare}, + Ciphersuite, Error, Identifier, Signature, VerifyingKey, +}; use crate::tests::ciphersuite_generic::check_part3_different_participants; From 6e2ed050014527181d51a2cda1b5bbf2e7d74cad Mon Sep 17 00:00:00 2001 From: Conrado Date: Wed, 28 Jan 2026 11:18:26 -0300 Subject: [PATCH 69/86] actually remove the std feature (#1010) --- frost-core/Cargo.toml | 4 +--- frost-ed25519/Cargo.toml | 4 +--- frost-ed448/Cargo.toml | 4 +--- frost-p256/Cargo.toml | 4 +--- frost-rerandomized/Cargo.toml | 4 +--- frost-ristretto255/Cargo.toml | 4 +--- frost-secp256k1-tr/Cargo.toml | 4 +--- frost-secp256k1/Cargo.toml | 4 +--- 8 files changed, 8 insertions(+), 24 deletions(-) diff --git a/frost-core/Cargo.toml b/frost-core/Cargo.toml index 05aa3ad..27642bf 100644 --- a/frost-core/Cargo.toml +++ b/frost-core/Cargo.toml @@ -52,9 +52,7 @@ tokio.workspace = true [features] -default = ["serialization", "std"] -# No longer needed. Kept for retrocompatibility until 3.0.0 -std = [] +default = ["serialization"] #! ## Features ## Expose internal types, which do not have SemVer guarantees. This is an advanced ## feature which can be useful if you need to build a modified version of FROST. diff --git a/frost-ed25519/Cargo.toml b/frost-ed25519/Cargo.toml index e16cda6..8a07396 100644 --- a/frost-ed25519/Cargo.toml +++ b/frost-ed25519/Cargo.toml @@ -38,9 +38,7 @@ serde_json.workspace = true tokio.workspace = true [features] -default = ["serialization", "std"] -# No longer needed. Kept for retrocompatibility until 3.0.0 -std = [] +default = ["serialization"] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-ed448/Cargo.toml b/frost-ed448/Cargo.toml index d9a0bed..5c26e30 100644 --- a/frost-ed448/Cargo.toml +++ b/frost-ed448/Cargo.toml @@ -37,9 +37,7 @@ serde_json.workspace = true tokio.workspace = true [features] -default = ["serialization", "std"] -# No longer needed. Kept for retrocompatibility until 3.0.0 -std = [] +default = ["serialization"] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-p256/Cargo.toml b/frost-p256/Cargo.toml index 42c429b..68e63b3 100644 --- a/frost-p256/Cargo.toml +++ b/frost-p256/Cargo.toml @@ -37,9 +37,7 @@ serde_json.workspace = true tokio.workspace = true [features] -default = ["serialization", "std"] -# No longer needed. Kept for retrocompatibility until 3.0.0 -std = [] +default = ["serialization"] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-rerandomized/Cargo.toml b/frost-rerandomized/Cargo.toml index c255253..2579900 100644 --- a/frost-rerandomized/Cargo.toml +++ b/frost-rerandomized/Cargo.toml @@ -25,9 +25,7 @@ rand_core.workspace = true [dev-dependencies] [features] -default = ["serialization", "std"] -# No longer needed. Kept for retrocompatibility until 3.0.0 -std = [] +default = ["serialization"] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-ristretto255/Cargo.toml b/frost-ristretto255/Cargo.toml index d02077c..3233dc4 100644 --- a/frost-ristretto255/Cargo.toml +++ b/frost-ristretto255/Cargo.toml @@ -38,9 +38,7 @@ serde_json.workspace = true tokio.workspace = true [features] -default = ["serialization", "std"] -# No longer needed. Kept for retrocompatibility until 3.0.0 -std = [] +default = ["serialization"] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-secp256k1-tr/Cargo.toml b/frost-secp256k1-tr/Cargo.toml index 55f83ee..ad45c8f 100644 --- a/frost-secp256k1-tr/Cargo.toml +++ b/frost-secp256k1-tr/Cargo.toml @@ -38,9 +38,7 @@ serde_json.workspace = true tokio.workspace = true [features] -default = ["serialization", "std"] -# No longer needed. Kept for retrocompatibility until 3.0.0 -std = [] +default = ["serialization"] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports diff --git a/frost-secp256k1/Cargo.toml b/frost-secp256k1/Cargo.toml index ab20dab..8d94271 100644 --- a/frost-secp256k1/Cargo.toml +++ b/frost-secp256k1/Cargo.toml @@ -37,9 +37,7 @@ serde_json.workspace = true tokio.workspace = true [features] -default = ["serialization", "std"] -# No longer needed. Kept for retrocompatibility until 3.0.0 -std = [] +default = ["serialization"] #! ## Features ## Enable `serde` support for types that need to be communicated. You ## can use `serde` to serialize structs with any encoder that supports From 1399dc0f071fe9e75c5f495273d841ae5a99c2d5 Mon Sep 17 00:00:00 2001 From: scaraven <34778974+scaraven@users.noreply.github.com> Date: Fri, 6 Feb 2026 22:43:45 +0000 Subject: [PATCH 70/86] feat(frost-core): add pre_commitment_aggregate hook to `verify_signature_share()` (#1016) feat(frost-core): add pre_commitment_aggregate hook to verify_signature_share --- frost-core/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index a0774e1..5482018 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -783,6 +783,8 @@ pub fn verify_signature_share( let binding_factor_list: BindingFactorList = compute_binding_factor_list(&signing_package, verifying_key, &[])?; + let signing_package = ::pre_commitment_aggregate(&signing_package, &binding_factor_list)?; + // Compute the group commitment from signing commitments produced in round one. let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?; From f7954c388169f380aac602c5093405908e43ae2d Mon Sep 17 00:00:00 2001 From: Conrado Date: Fri, 20 Feb 2026 06:15:57 -0300 Subject: [PATCH 71/86] ci: migrate book to GitHub pages (#1020) --- .github/workflows/docs.yml | 46 +++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d5345c2..0e05b17 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -15,14 +15,20 @@ on: paths: # doc source files - 'book/**' - - '**/firebase.json' - - 'katex-header.html' + # source files; some md files include code snippets that we want to keep up to date + - 'frost-*/**' # workflow definitions - '.github/workflows/docs.yml' push: branches: - main +# Sets permissions for GitHub Pages deployment +permissions: + contents: read + pages: write + id-token: write + env: RUST_LOG: info RUST_BACKTRACE: full @@ -31,7 +37,7 @@ env: jobs: build: - name: Build and Deploy Docs (+beta) + name: Build Docs (+beta) timeout-minutes: 45 runs-on: ubuntu-latest steps: @@ -60,23 +66,23 @@ jobs: run: | mdbook build book/ - - name: Deploy FROST book to Firebase preview channel - uses: FirebaseExtended/action-hosting-deploy@v0 - if: ${{ github.event_name == 'pull_request' && github.actor != 'dependabot[bot]' }} - with: - entrypoint: "book/" - expires: 14d - firebaseServiceAccount: ${{ secrets.GCP_SA_KEY }} - repoToken: ${{ secrets.GITHUB_TOKEN }} - projectId: ${{ vars.FIREBASE_PROJECT_ID }} - - - name: Deploy FROST book to Firebase live channel - uses: FirebaseExtended/action-hosting-deploy@v0 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 if: ${{ github.event_name == 'push' && github.ref_name == 'main' }} with: - channelId: live - entrypoint: "book/" - firebaseServiceAccount: ${{ secrets.GCP_SA_KEY }} - repoToken: ${{ secrets.GITHUB_TOKEN }} - projectId: ${{ vars.FIREBASE_PROJECT_ID }} + path: 'book/book' + + deploy: + name: Deploy to GitHub Pages + if: ${{ github.event_name == 'push' && github.ref_name == 'main' }} + needs: build + timeout-minutes: 10 + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From ff5ec8d1528f7aa95e5cbcecccfc7e824018518f Mon Sep 17 00:00:00 2001 From: Conrado Date: Wed, 1 Apr 2026 19:12:21 -0300 Subject: [PATCH 72/86] core: misc fixes (#1042) * core: misc fixes * core: remove Copy from SigningKey; add ZeroizeOnDrop * cargo fmt --- frost-core/CHANGELOG.md | 1 + frost-core/src/keys/dkg.rs | 14 +++++++++++--- frost-core/src/keys/repairable.rs | 2 +- frost-core/src/lib.rs | 10 ++++++++-- frost-core/src/signing_key.rs | 16 ++++++++++++++-- frost-core/src/tests/ciphersuite_generic.rs | 15 +++++++-------- frost-secp256k1-tr/src/lib.rs | 2 +- .../tests/interoperability_tests.rs | 2 +- 8 files changed, 44 insertions(+), 18 deletions(-) diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index a1fce40..373eb44 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -28,6 +28,7 @@ Entries are listed in reverse chronological order. you will need to fills its `min_signers` field with the original threshold before calling the function (recreate it with `PublicKeyPackage::new()`). The latter was simply redundant. +* `SigningKey` is no longer `Copy`; and now it implements `ZeroizeOnDrop`. * Refactored the `frost_core::keys::repairable` module: * `repair_share_step_1()` was renamed to `repair_share_part1()` and now takes a `KeyPackage` and returns a map with a new `Delta` type instead of a raw diff --git a/frost-core/src/keys/dkg.rs b/frost-core/src/keys/dkg.rs index ad9d9a7..0c4d77d 100644 --- a/frost-core/src/keys/dkg.rs +++ b/frost-core/src/keys/dkg.rs @@ -434,9 +434,7 @@ pub(crate) fn compute_proof_of_knowledge // > a context string to prevent replay attacks. let (k, R_i) = ::generate_nonce(&mut rng); let c_i = challenge::(identifier, &commitment.verifying_key()?, &R_i)?; - let a_i0 = *coefficients - .first() - .expect("coefficients must have at least one element"); + let a_i0 = *coefficients.first().ok_or(Error::InvalidCoefficients)?; let mu_i = k + a_i0 * c_i.0; Ok(Signature { R: R_i, z: mu_i }) } @@ -494,6 +492,10 @@ pub fn part2( return Err(Error::IncorrectNumberOfPackages); } + if round1_packages.contains_key(&secret_package.identifier) { + return Err(Error::UnknownIdentifier); + } + for package in round1_packages.values() { if package.commitment.min_signers() != secret_package.min_signers { return Err(Error::IncorrectNumberOfCommitments); @@ -564,6 +566,12 @@ pub fn part3( if round1_packages.len() != (round2_secret_package.max_signers - 1) as usize { return Err(Error::IncorrectNumberOfPackages); } + if round1_packages.contains_key(&round2_secret_package.identifier) { + return Err(Error::UnknownIdentifier); + } + if round2_packages.contains_key(&round2_secret_package.identifier) { + return Err(Error::UnknownIdentifier); + } if round1_packages.len() != round2_packages.len() { return Err(Error::IncorrectNumberOfPackages); } diff --git a/frost-core/src/keys/repairable.rs b/frost-core/src/keys/repairable.rs index da4f5f0..0d62636 100644 --- a/frost-core/src/keys/repairable.rs +++ b/frost-core/src/keys/repairable.rs @@ -117,7 +117,7 @@ pub fn repair_share_part1( rng: &mut R, participant: Identifier, ) -> Result, Delta>, Error> { - if helpers.len() < 2 { + if helpers.len() < *key_package_i.min_signers() as usize { return Err(Error::IncorrectNumberOfIdentifiers); } if !helpers.contains(&key_package_i.identifier) { diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index 5482018..364beab 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -608,6 +608,12 @@ where return Err(Error::UnknownIdentifier); } + if let Some(min) = pubkeys.min_signers() { + if signature_shares.len() < min as usize { + return Err(Error::IncorrectNumberOfShares); + } + } + if !signing_package .signing_commitments() .keys() @@ -772,11 +778,11 @@ pub fn verify_signature_share( let verifying_share = pubkeys .verifying_shares() .get(&identifier) - .expect("pre_aggregate() must keep the identifiers"); + .ok_or(Error::UnknownIdentifier)?; let verifying_key = pubkeys.verifying_key(); let signature_share = signature_shares .get(&identifier) - .expect("pre_aggregate() must keep the identifiers"); + .ok_or(Error::UnknownIdentifier)?; // Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the // binding factor. diff --git a/frost-core/src/signing_key.rs b/frost-core/src/signing_key.rs index 574bf96..20aa634 100644 --- a/frost-core/src/signing_key.rs +++ b/frost-core/src/signing_key.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use rand_core::{CryptoRng, RngCore}; +use zeroize::ZeroizeOnDrop; use crate::{ random_nonzero, serialization::SerializableScalar, Challenge, Ciphersuite, Error, Field, Group, @@ -10,7 +11,7 @@ use crate::{ }; /// A signing key for a Schnorr signature on a FROST [`Ciphersuite::Group`]. -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub struct SigningKey where C: Ciphersuite, @@ -52,7 +53,7 @@ where mut rng: R, message: &[u8], ) -> Signature { - let public = VerifyingKey::::from(*self); + let public = VerifyingKey::::from(self.clone()); let (k, R) = ::generate_nonce(&mut rng); @@ -80,6 +81,17 @@ where } } +impl ZeroizeOnDrop for SigningKey where C: Ciphersuite {} + +impl Drop for SigningKey +where + C: Ciphersuite, +{ + fn drop(&mut self) { + self.scalar = <::Field as Field>::zero(); + } +} + impl core::fmt::Debug for SigningKey where C: Ciphersuite, diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs index e769e32..57349f8 100644 --- a/frost-core/src/tests/ciphersuite_generic.rs +++ b/frost-core/src/tests/ciphersuite_generic.rs @@ -138,13 +138,12 @@ pub fn check_sign_with_dealer( frost::keys::KeyPackage::deserialize(&key_package.serialize().unwrap()).unwrap(); key_packages.insert(k, key_package); } - // Check if it fails with not enough signers. Usually this would return an - // error before even running the signing procedure, because `KeyPackage` - // contains the correct `min_signers` value and the signing procedure checks - // if the number of shares is at least `min_signers`. To bypass the check - // and test if the protocol itself fails with not enough signers, we modify - // the `KeyPackages`s, decrementing their saved `min_signers` value before - // running the signing procedure. + // Check if it fails with not enough signers. Both the KeyPackages and + // the PublicKeyPackage have their min_signers decremented so that the + // early validation check in aggregate() passes, and we can verify that + // the cryptographic aggregation itself fails when too few shares are used. + let mut pub_key_package_insufficient = pub_key_package.clone(); + pub_key_package_insufficient.min_signers = Some(min_signers - 1); let r = check_sign( min_signers - 1, key_packages @@ -158,7 +157,7 @@ pub fn check_sign_with_dealer( }) .collect(), &mut rng, - pub_key_package.clone(), + pub_key_package_insufficient, ); assert_eq!(r, Err(Error::InvalidSignature)); diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs index 7800d14..daa1960 100644 --- a/frost-secp256k1-tr/src/lib.rs +++ b/frost-secp256k1-tr/src/lib.rs @@ -299,7 +299,7 @@ impl Ciphersuite for Secp256K1Sha256TR { rng: R, message: &[u8], ) -> Signature { - let signing_key = signing_key.into_even_y(None); + let signing_key = signing_key.clone().into_even_y(None); signing_key.default_sign(rng, message) } diff --git a/frost-secp256k1-tr/tests/interoperability_tests.rs b/frost-secp256k1-tr/tests/interoperability_tests.rs index 5be7f62..3cf68f2 100644 --- a/frost-secp256k1-tr/tests/interoperability_tests.rs +++ b/frost-secp256k1-tr/tests/interoperability_tests.rs @@ -10,7 +10,7 @@ fn check_interoperability_in_regular_sign() { for _ in 0..256 { let signing_key = SigningKey::new(&mut rng); - let verifying_key = signing_key.into(); + let verifying_key = (&signing_key).into(); let signature = signing_key.sign(rng, b"message"); helpers::verify_signature(b"message", &signature, &verifying_key); } From 3c2f25f9b83e976e83e83231bfd4598f23c3601d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:15:10 +0000 Subject: [PATCH 73/86] build(deps): bump keccak from 0.1.5 to 0.1.6 (#1023) Bumps [keccak](https://github.com/RustCrypto/sponges) from 0.1.5 to 0.1.6. - [Commits](https://github.com/RustCrypto/sponges/compare/keccak-v0.1.5...keccak-v0.1.6) --- updated-dependencies: - dependency-name: keccak dependency-version: 0.1.6 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2ee992f..5809e10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -863,9 +863,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ "cpufeatures", ] From 75d14b093a81b20343c3daaad2977481e8142657 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:15:14 +0000 Subject: [PATCH 74/86] build(deps): bump actions/upload-pages-artifact from 3 to 4 (#1026) Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-pages-artifact/releases) - [Commits](https://github.com/actions/upload-pages-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-pages-artifact dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0e05b17..d64cfac 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -67,7 +67,7 @@ jobs: mdbook build book/ - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v4 if: ${{ github.event_name == 'push' && github.ref_name == 'main' }} with: path: 'book/book' From 7967016563d5181d5358ef78fc1a9159e48939b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:15:44 +0000 Subject: [PATCH 75/86] build(deps): bump regex from 1.12.2 to 1.12.3 (#1028) Bumps [regex](https://github.com/rust-lang/regex) from 1.12.2 to 1.12.3. - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.12.2...1.12.3) --- updated-dependencies: - dependency-name: regex dependency-version: 1.12.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5809e10..c7000db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1154,9 +1154,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", From 5c0deb3e5a56357271cdf5d215ce00a720b66af0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:15:49 +0000 Subject: [PATCH 76/86] build(deps): bump codecov/codecov-action from 5.5.2 to 6.0.0 (#1043) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.5.2 to 6.0.0. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v5.5.2...v6.0.0) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/coverage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 8e2786f..55ce037 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -41,4 +41,4 @@ jobs: run: cargo llvm-cov report --lcov --ignore-filename-regex '.*(tests).*|benches.rs|gencode|helpers.rs|interoperability_tests.rs' --output-path lcov.info - name: Upload coverage report to Codecov - uses: codecov/codecov-action@v5.5.2 + uses: codecov/codecov-action@v6.0.0 From d94090a00f699b56970b79656dc8ca51c7078b7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:16:14 +0000 Subject: [PATCH 77/86] build(deps): bump reviewdog/action-actionlint from 1.69.1 to 1.72.0 (#1044) Bumps [reviewdog/action-actionlint](https://github.com/reviewdog/action-actionlint) from 1.69.1 to 1.72.0. - [Release notes](https://github.com/reviewdog/action-actionlint/releases) - [Commits](https://github.com/reviewdog/action-actionlint/compare/v1.69.1...v1.72.0) --- updated-dependencies: - dependency-name: reviewdog/action-actionlint dependency-version: 1.72.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6d2659d..c263aa9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -176,7 +176,7 @@ jobs: continue-on-error: true steps: - uses: actions/checkout@v6.0.2 - - uses: reviewdog/action-actionlint@v1.69.1 + - uses: reviewdog/action-actionlint@v1.72.0 with: level: warning fail_level: none From c48449920ff82eb4bc6be37fb1c7383afcf35a1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:16:17 +0000 Subject: [PATCH 78/86] build(deps): bump release-drafter/release-drafter from 6 to 7 (#1045) Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 6 to 7. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/v6...v7) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index a110f6d..23952d0 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into main - - uses: release-drafter/release-drafter@v6 + - uses: release-drafter/release-drafter@v7 with: # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml config-name: release-drafter.yml From 4ea9c8657da316422b89cd2e9f5c95035a664497 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:16:40 +0000 Subject: [PATCH 79/86] build(deps): bump insta from 1.46.1 to 1.47.2 (#1046) Bumps [insta](https://github.com/mitsuhiko/insta) from 1.46.1 to 1.47.2. - [Release notes](https://github.com/mitsuhiko/insta/releases) - [Changelog](https://github.com/mitsuhiko/insta/blob/master/CHANGELOG.md) - [Commits](https://github.com/mitsuhiko/insta/compare/1.46.1...1.47.2) --- updated-dependencies: - dependency-name: insta dependency-version: 1.47.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 92 ++++++------------------------------------------------ 1 file changed, 9 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7000db..56ee550 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -199,14 +199,13 @@ dependencies = [ [[package]] name = "console" -version = "0.15.11" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" dependencies = [ "encode_unicode", "libc", - "once_cell", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -482,7 +481,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -797,9 +796,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.46.1" +version = "1.47.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248b42847813a1550dafd15296fd9748c651d0c32194559dbc05d804d54b21e8" +checksum = "7b4a6248eb93a4401ed2f37dfe8ea592d3cf05b7cf4f8efa867b6895af7e094e" dependencies = [ "console", "once_cell", @@ -1200,7 +1199,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -1422,7 +1421,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -1606,7 +1605,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -1615,15 +1614,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -1633,70 +1623,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "wit-bindgen" version = "0.51.0" From fb36ac91665bf69e1a1fb330ed66761a8c32aeb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:16:43 +0000 Subject: [PATCH 80/86] build(deps): bump actions/deploy-pages from 4 to 5 (#1047) Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 4 to 5. - [Release notes](https://github.com/actions/deploy-pages/releases) - [Commits](https://github.com/actions/deploy-pages/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/deploy-pages dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d64cfac..da14f62 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -84,5 +84,5 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 From 7bc107e1c3e7be3e4e1d38f8c43873acd3ef843a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:17:05 +0000 Subject: [PATCH 81/86] build(deps): bump proptest from 1.9.0 to 1.11.0 (#1048) Bumps [proptest](https://github.com/proptest-rs/proptest) from 1.9.0 to 1.11.0. - [Release notes](https://github.com/proptest-rs/proptest/releases) - [Changelog](https://github.com/proptest-rs/proptest/blob/main/CHANGELOG.md) - [Commits](https://github.com/proptest-rs/proptest/compare/v1.9.0...v1.11.0) --- updated-dependencies: - dependency-name: proptest dependency-version: 1.11.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56ee550..c250b51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1025,9 +1025,9 @@ dependencies = [ [[package]] name = "proptest" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set", "bit-vec", From 28a9b270e066c021391f587b7f6415067a86189f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:17:08 +0000 Subject: [PATCH 82/86] build(deps): bump tokio from 1.49.0 to 1.50.0 (#1049) Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.49.0 to 1.50.0. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.49.0...tokio-1.50.0) --- updated-dependencies: - dependency-name: tokio dependency-version: 1.50.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c250b51..9f69237 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1456,9 +1456,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.49.0" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" dependencies = [ "pin-project-lite", "tokio-macros", From 7dcfd30bad387733e17bd807d38d0b659bd7919d Mon Sep 17 00:00:00 2001 From: Conrado Date: Thu, 16 Apr 2026 09:57:09 -0300 Subject: [PATCH 83/86] core: add ZeroizeOnDrop for dkg::round2::Package (#1040) --- frost-core/src/keys/dkg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frost-core/src/keys/dkg.rs b/frost-core/src/keys/dkg.rs index 0c4d77d..016490f 100644 --- a/frost-core/src/keys/dkg.rs +++ b/frost-core/src/keys/dkg.rs @@ -223,7 +223,7 @@ pub mod round2 { /// # Security /// /// The package must be sent on an *confidential* and *authenticated* channel. - #[derive(Clone, Debug, PartialEq, Eq, Getters)] + #[derive(Clone, Debug, PartialEq, Eq, Getters, Zeroize, ZeroizeOnDrop)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] From 4705e794e685b048617ac0c13d2628cab4cdfdb9 Mon Sep 17 00:00:00 2001 From: Conrado Date: Thu, 16 Apr 2026 09:57:12 -0300 Subject: [PATCH 84/86] book: update to mdbook 0.5 (#1022) * book: update to mdbook 0.5 * Fix typo in book/src/tutorial.md (#1022) --------- Co-authored-by: natalie --- .github/workflows/docs.yml | 3 +- book/book.toml | 8 --- book/src/frost.md | 60 ++++++++-------- book/src/tutorial.md | 11 ++- book/src/tutorial/dkg.md | 54 +++++++------- book/src/tutorial/refreshing-shares.md | 39 +++++----- book/src/tutorial/signing.md | 79 ++++++++++---------- book/src/tutorial/trusted-dealer.md | 99 ++++++++++++-------------- book/src/user/serialization.md | 9 ++- book/src/zcash/devtool-demo.md | 30 ++++---- book/src/zcash/server.md | 52 +++++++------- book/src/zcash/technical-details.md | 20 +++--- book/src/zcash/ywallet-demo.md | 47 ++++++------ 13 files changed, 232 insertions(+), 279 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index da14f62..b31762c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -54,13 +54,12 @@ jobs: - name: Setup mdBook uses: peaceiris/actions-mdbook@v2.0.0 with: - mdbook-version: '0.4.18' + mdbook-version: '0.5.2' # TODO: actions-mdbook does not yet have an option to install mdbook-mermaid https://github.com/peaceiris/actions-mdbook/issues/426 - name: Install plugins run: | cargo install mdbook-mermaid - cargo install mdbook-admonish - name: Build FROST book run: | diff --git a/book/book.toml b/book/book.toml index e95c664..e988329 100644 --- a/book/book.toml +++ b/book/book.toml @@ -1,17 +1,9 @@ [book] authors = ["Zcash Foundation "] language = "en" -multilingual = false src = "src" title = "The ZF FROST Book" -[preprocessor] - -[preprocessor.admonish] -command = "mdbook-admonish" -assets_version = "3.0.2" # do not edit: managed by `mdbook-admonish install` - [output] [output.html] -additional-css = ["./mdbook-admonish.css", "book/mdbook-admonish.css"] diff --git a/book/src/frost.md b/book/src/frost.md index 4b03a3a..8dfc4d3 100644 --- a/book/src/frost.md +++ b/book/src/frost.md @@ -10,10 +10,9 @@ together generate a signature that can be validated by the corresponding verifyi key. One important aspect is that the resulting signature is indistinguishable from a non-threshold signature from the point of view of signature verifiers. -```admonish note -FROST only supports Schnorr signatures. Therefore it can't produce -ECDSA signatures. -``` +> [!NOTE] +> FROST only supports Schnorr signatures. Therefore it can't produce +> ECDSA signatures. ## Key Generation @@ -62,21 +61,19 @@ consolidates them and sends them to each participant. Each one will then produce a signature share, which is sent to the Coordinator who finally aggregates them and produces the final signature. -```admonish note -If having a single coordinator is not desired, then all participants -can act as coordinators. Refer to the -[spec](https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#removing-the-coordinator-role-no-coordinator) -for more information. -``` +> [!NOTE] +> If having a single coordinator is not desired, then all participants +> can act as coordinators. Refer to the +> [spec](https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#removing-the-coordinator-role-no-coordinator) +> for more information. -```admonish warning -ALL participants who are selected for generating the signature need -to produce their share, even if there are more than `t` of them. -For example, in 2-of-3 signing, if 3 participants are selected, -them all 3 must produce signature shares in order for the Coordinator -be able to produce the final signature. Of course, the Coordinator -is still free to start the process with only 2 participants if they wish. -``` +> [!WARNING] +> ALL participants who are selected for generating the signature need +> to produce their share, even if there are more than `t` of them. +> For example, in 2-of-3 signing, if 3 participants are selected, +> them all 3 must produce signature shares in order for the Coordinator +> be able to produce the final signature. Of course, the Coordinator +> is still free to start the process with only 2 participants if they wish. ## Verifying Signatures @@ -112,20 +109,19 @@ shares in a way that maintains the same group public key. Some applications are: in signing sessions with the others. (They can also then use the repair share functionality to issue a new share and move from 2-of-2 back to 2-of-3.) -```admonish danger -It is critically important to keep in mind that the **Refresh Shares -functionality does not "restore full security" to a group**. While the group -evolves and participants are removed and new participants are added, the -security of the group does not depend only on the threshold of the current -participants being honest, but also **on the threshold of all previous set of -participants being honest**! For example, if Alice, Mallory and Eve form a group -and Mallory is eventually excluded from the group and replaced with Bob, it is -not enough to trust 2 out of 3 between Alice, Bob and Eve. **You also need to -trust that Mallory won't collude with, say, Eve which could have kept her -original pre-refresh share and they could both together recompute the original -key and compromise the group.** If that's an unacceptable risk to your use case, -you will need to migrate to a new group if that makes sense to your application. -``` +> [!CAUTION] +> It is critically important to keep in mind that the **Refresh Shares +> functionality does not "restore full security" to a group**. While the group +> evolves and participants are removed and new participants are added, the +> security of the group does not depend only on the threshold of the current +> participants being honest, but also **on the threshold of all previous set of +> participants being honest**! For example, if Alice, Mallory and Eve form a group +> and Mallory is eventually excluded from the group and replaced with Bob, it is +> not enough to trust 2 out of 3 between Alice, Bob and Eve. **You also need to +> trust that Mallory won't collude with, say, Eve which could have kept her +> original pre-refresh share and they could both together recompute the original +> key and compromise the group.** If that's an unacceptable risk to your use case, +> you will need to migrate to a new group if that makes sense to your application. ## Ciphersuites diff --git a/book/src/tutorial.md b/book/src/tutorial.md index 15d0a25..5d86ba5 100644 --- a/book/src/tutorial.md +++ b/book/src/tutorial.md @@ -15,9 +15,8 @@ If you need to support multiple ciphersuites then feel free to use This tutorial will use the `frost-ristretto255` crate, but changing to another ciphersuite should be a matter of simply changing the import. -```admonish note -"The `frost-secp256k1` crate is not compatible with Bitcoin BIP-340 (Taproot) -signatures. Use -[frost-secp256k1-tr](https://crates.io/crates/frost-secp256k1-tr) instead -if you want to support it. -``` +> [!NOTE] +> The `frost-secp256k1` crate is not compatible with Bitcoin BIP-340 (Taproot) +> signatures. Use +> [frost-secp256k1-tr](https://crates.io/crates/frost-secp256k1-tr) instead +> if you want to support it. diff --git a/book/src/tutorial/dkg.md b/book/src/tutorial/dkg.md index 91f0a4d..755dc7d 100644 --- a/book/src/tutorial/dkg.md +++ b/book/src/tutorial/dkg.md @@ -32,33 +32,31 @@ the application.) It returns a `round1::SecretPackage` and a `round1::Package`: {{#include ../../../frost-ristretto255/dkg.md:dkg_part1}} ``` -```admonish info -Check the crate documentation for a [full working example](https://docs.rs/frost-ristretto255/latest/frost_ristretto255/keys/dkg/index.html#example); keep in mind it's an artificial -one since everything runs in the same program. -``` +> [!TIP] +> Check the crate documentation for a [full working example](https://docs.rs/frost-ristretto255/latest/frost_ristretto255/keys/dkg/index.html#example); keep in mind it's an artificial +> one since everything runs in the same program. The `round1::SecretPackage` must be kept in memory to use in the next round. The `round1::Package` must be sent to all other participants using a [**broadcast channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) to ensure that all participants receive the same value. -```admonish danger -A [**broadcast -channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) in this -context is not simply broadcasting the value to all participants. It requires -running a protocol to ensure that all participants have the same value or that -the protocol is aborted. Check the linked [Terminology -section](https://frost.zfnd.org/terminology.html#broadcast-channel) for more -details. - -In the context of the DKG, `n` broadcast channels will need to be set up; one -for each participant. So each participant will broadcast their round 1 package -to the other participants, and each participant needs to handle the broadcast -from the other `n-1` participants. - -**Failure in using a proper broadcast channel will make the key generation -insecure.** -``` +> [!CAUTION] +> A [**broadcast +> channel**](https://frost.zfnd.org/terminology.html#broadcast-channel) in this +> context is not simply broadcasting the value to all participants. It requires +> running a protocol to ensure that all participants have the same value or that +> the protocol is aborted. Check the linked [Terminology +> section](https://frost.zfnd.org/terminology.html#broadcast-channel) for more +> details. +> +> In the context of the DKG, `n` broadcast channels will need to be set up; one +> for each participant. So each participant will broadcast their round 1 package +> to the other participants, and each participant needs to handle the broadcast +> from the other `n-1` participants. +> +> **Failure in using a proper broadcast channel will make the key generation +> insecure.** ## Part 2 @@ -84,11 +82,10 @@ The `round2::Package`s must be sent to their respective participants with the given `Identifier`s, using an [authenticated and confidential communication channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). -```admonish danger -The `round2::Package`s MUST be encrypted, otherwise an attacker who can read -the content of the packages will be able to recreate the secret being -generated. -``` +> [!CAUTION] +> The `round2::Package`s MUST be encrypted, otherwise an attacker who can read +> the content of the packages will be able to recreate the secret being +> generated. ## Part 3 @@ -105,6 +102,5 @@ a `PublicKeyPackage` containing the group verifying key: {{#include ../../../frost-ristretto255/dkg.md:dkg_part3}} ``` -```admonish note -All participants will generate the same `PublicKeyPackage`. -``` \ No newline at end of file +> [!NOTE] +> All participants will generate the same `PublicKeyPackage`. \ No newline at end of file diff --git a/book/src/tutorial/refreshing-shares.md b/book/src/tutorial/refreshing-shares.md index fa45ce9..8b6f7a1 100644 --- a/book/src/tutorial/refreshing-shares.md +++ b/book/src/tutorial/refreshing-shares.md @@ -17,24 +17,21 @@ Each Participant then runs `refresh_share()` to generate a new `KeyPackage` which will replace their old `KeyPackage`; they must also replace their old `PublicKeyPackage` with the one sent by the Trusted Dealer. -```admonish danger -The refreshed `KeyPackage` contents must be stored securely and the original -`KeyPackage` should be deleted. For example: - -- Make sure other users in the system can't read it; -- If possible, use the OS secure storage such that the package - contents can only be opened with the user's password or biometrics. -``` - -```admonish danger -Applications should first ensure that all participants who refreshed their -`KeyPackages` were actually able to do so successfully, before deleting their old -`KeyPackages`. How this is done is up to the application; it might require -successfully generating a signature with all of those participants. -``` - -```admonish danger -Refreshing Shares may be not enough to address security concerns -after a share has been compromised. Refer to the [Understanding -FROST](../frost.md#refreshing-shares) section. -``` \ No newline at end of file +> [!CAUTION] +> The refreshed `KeyPackage` contents must be stored securely and the original +> `KeyPackage` should be deleted. For example: +> +> - Make sure other users in the system can't read it; +> - If possible, use the OS secure storage such that the package +> contents can only be opened with the user's password or biometrics. + +> [!CAUTION] +> Applications should first ensure that all participants who refreshed their +> `KeyPackages` were actually able to do so successfully, before deleting their old +> `KeyPackages`. How this is done is up to the application; it might require +> successfully generating a signature with all of those participants. + +> [!CAUTION] +> Refreshing Shares may be not enough to address security concerns +> after a share has been compromised. Refer to the [Understanding +> FROST](../frost.md#refreshing-shares) section. \ No newline at end of file diff --git a/book/src/tutorial/signing.md b/book/src/tutorial/signing.md index 18b120c..fea4903 100644 --- a/book/src/tutorial/signing.md +++ b/book/src/tutorial/signing.md @@ -27,11 +27,10 @@ their commitments (a `SigningCommitments`) by calling The `SigningNonces` must be kept by the participant to use in Round 2, while the `SigningCommitments` must be sent to the Coordinator. -```admonish info -FROST does not require using an [authenticated nor encrypted -channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel) -during the **signing** process. -``` +> [!NOTE] +> FROST does not require using an [authenticated nor encrypted +> channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel) +> during the **signing** process. ## Coordinator, Round 2 @@ -47,14 +46,13 @@ The `SigningPackage` must then be sent to all the participants. (If the message is confidential, then the channel must also be confidential, since the message is included in the `SigningPackage`.) -```admonish warning -In all of the main FROST ciphersuites, the entire message must -be sent to participants. In some cases, where the message is too big, it may be -necessary to send a hash of the message instead. We strongly suggest creating a -specific ciphersuite for this, and not just sending the hash as if it were the -message. For reference, see [how RFC 8032 handles -"pre-hashing"](https://datatracker.ietf.org/doc/html/rfc8032). -``` +> [!WARNING] +> In all of the main FROST ciphersuites, the entire message must +> be sent to participants. In some cases, where the message is too big, it may be +> necessary to send a hash of the message instead. We strongly suggest creating a +> specific ciphersuite for this, and not just sending the hash as if it were the +> message. For reference, see [how RFC 8032 handles +> "pre-hashing"](https://datatracker.ietf.org/doc/html/rfc8032). ## Participants, Round 2 @@ -69,12 +67,11 @@ their `SigningNonces` from Round 1, by calling The resulting `SignatureShare` must then be sent back to the Coordinator. -```admonish important -In most applications, it is important that the participant must be aware of what -they are signing. Thus the application should show the message to the -participant and obtain their consent to proceed before producing the signature -share. -``` +> [!IMPORTANT] +> In most applications, it is important that the participant must be aware of what +> they are signing. Thus the application should show the message to the +> participant and obtain their consent to proceed before producing the signature +> share. ## Coordinator, Aggregate @@ -92,29 +89,27 @@ with the same `SigningPackage` sent to the participants and the The returned signature, a `Signature`, will be a valid signature for the message in the `SigningPackage` in Round 2 for the group verifying key in the `PublicKeyPackage`. -```admonish note -FROST supports identifiable abort: if a participant misbehaves and produces an -invalid signature share, then aggregation will fail and the returned error -will have the identifier of the misbehaving participant. (If multiple participants -misbehave, only the first one detected will be returned. If you need to detect -all cheaters, use [`aggregate_custom()`](https://docs.rs/frost-ristretto255/latest/frost_ristretto255/fn.aggregate_custom.html)) - -What should be done in that case is up to the application. The misbehaving participant -could be excluded from future signing sessions, for example. -``` - -```admonish danger -In `aggregate()` you need to provide a map from `Identifier` to -`SignatureShare`. If you need cheater detection, then it is important that these -identifiers come from a mapping between authenticated channels and identifiers; -i.e. you should not simply send the `Identifier` along with the -`SignatureShare`; otherwise the cheater could simply lie about their identifier. - -For example, if you authenticate the communication channels with TLS, then you -will need to create a public key -> identifier mapping, and use that mapping -to get the identifier for the connection where the `SignatureShare` was read -from. -``` +> [!NOTE] +> FROST supports identifiable abort: if a participant misbehaves and produces an +> invalid signature share, then aggregation will fail and the returned error +> will have the identifier of the misbehaving participant. (If multiple participants +> misbehave, only the first one detected will be returned. If you need to detect +> all cheaters, use [`aggregate_custom()`](https://docs.rs/frost-ristretto255/latest/frost_ristretto255/fn.aggregate_custom.html)) +> +> What should be done in that case is up to the application. The misbehaving participant +> could be excluded from future signing sessions, for example. + +> [!CAUTION] +> In `aggregate()` you need to provide a map from `Identifier` to +> `SignatureShare`. If you need cheater detection, then it is important that these +> identifiers come from a mapping between authenticated channels and identifiers; +> i.e. you should not simply send the `Identifier` along with the +> `SignatureShare`; otherwise the cheater could simply lie about their identifier. +> +> For example, if you authenticate the communication channels with TLS, then you +> will need to create a public key -> identifier mapping, and use that mapping +> to get the identifier for the connection where the `SignatureShare` was read +> from. ## Verifying signatures diff --git a/book/src/tutorial/trusted-dealer.md b/book/src/tutorial/trusted-dealer.md index ee07008..b17eb5d 100644 --- a/book/src/tutorial/trusted-dealer.md +++ b/book/src/tutorial/trusted-dealer.md @@ -27,55 +27,50 @@ their signing share, verifying share and group verifying key. This is done with {{#include ../../../frost-ristretto255/README.md:tkg_verify}} ``` -```admonish info -Check the crate documentation for a [full working example](https://docs.rs/frost-ristretto255/latest/frost_ristretto255/index.html#example-key-generation-with-trusted-dealer-and-frost-signing); keep in mind it's an artificial -one since everything runs in the same program. -``` - -```admonish info -You can specify which identifiers to use by using [`IdentifierList::Custom`](https://docs.rs/frost-core/latest/frost_core/frost/keys/enum.IdentifierList.html#variant.Custom). Refer to the [DKG](dkg.md#part-1) section for an example on how to create identifiers. -``` - -```admonish danger -Which [**authenticated** and **confidential** channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel) -to use is up to the application. Some examples: - -- Manually require the dealer to send the `SecretShare`s to the - participants using some secure messenger such as Signal; -- Use a TLS connection, authenticating the server with a certificate - and the client with some user/password or another suitable authentication - mechanism; - -Refer to the [Terminology page](https://frost.zfnd.org/terminology.html#peer-to-peer-channel) -for more details. - -Failure of using a **confidential** channel may lead to the shares being -stolen and possibly allowing signature forgeries if a threshold number of -them are stolen. - -Failure of using an **authenticated** channel may lead to shares being -sent to the wrong person, possibly allowing unintended parties -to generate signatures. -``` - -```admonish danger -The `KeyPackage` contents must be stored securely. For example: - -- Make sure other users in the system can't read it; -- If possible, use the OS secure storage such that the package - contents can only be opened with the user's password or biometrics. -``` - -```admonish warning -The participants may wish to not fully trust the dealer. While **the dealer -has access to the original secret and can forge signatures -by simply using the secret to sign** (and this can't be -possibly avoided with this method; use Distributed Key Generation -if that's an issue), the dealer could also tamper with the `SecretShare`s -in a way that the participants will never be able to generate a valid -signature in the future (denial of service). Participants can detect -such tampering by comparing the `VerifiableSecretSharingCommitment` -values from their `SecretShare`s (either by some manual process, or -by using a [broadcast channel](https://frost.zfnd.org/terminology.html#broadcast-channel)) -to make sure they are all equal. -``` +> [!TIP] +> Check the crate documentation for a [full working example](https://docs.rs/frost-ristretto255/latest/frost_ristretto255/index.html#example-key-generation-with-trusted-dealer-and-frost-signing); keep in mind it's an artificial +> one since everything runs in the same program. + +> [!TIP] +> You can specify which identifiers to use by using [`IdentifierList::Custom`](https://docs.rs/frost-core/latest/frost_core/frost/keys/enum.IdentifierList.html#variant.Custom). Refer to the [DKG](dkg.md#part-1) section for an example on how to create identifiers. + +> [!CAUTION] +> Which [**authenticated** and **confidential** channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel) +> to use is up to the application. Some examples: +> +> - Manually require the dealer to send the `SecretShare`s to the +> participants using some secure messenger such as Signal; +> - Use a TLS connection, authenticating the server with a certificate +> and the client with some user/password or another suitable authentication +> mechanism; +> +> Refer to the [Terminology page](https://frost.zfnd.org/terminology.html#peer-to-peer-channel) +> for more details. +> +> Failure of using a **confidential** channel may lead to the shares being +> stolen and possibly allowing signature forgeries if a threshold number of +> them are stolen. +> +> Failure of using an **authenticated** channel may lead to shares being +> sent to the wrong person, possibly allowing unintended parties +> to generate signatures. + +> [!CAUTION] +> The `KeyPackage` contents must be stored securely. For example: +> +> - Make sure other users in the system can't read it; +> - If possible, use the OS secure storage such that the package +> contents can only be opened with the user's password or biometrics. + +> [!WARNING] +> The participants may wish to not fully trust the dealer. While **the dealer +> has access to the original secret and can forge signatures +> by simply using the secret to sign** (and this can't be +> possibly avoided with this method; use Distributed Key Generation +> if that's an issue), the dealer could also tamper with the `SecretShare`s +> in a way that the participants will never be able to generate a valid +> signature in the future (denial of service). Participants can detect +> such tampering by comparing the `VerifiableSecretSharingCommitment` +> values from their `SecretShare`s (either by some manual process, or +> by using a [broadcast channel](https://frost.zfnd.org/terminology.html#broadcast-channel)) +> to make sure they are all equal. diff --git a/book/src/user/serialization.md b/book/src/user/serialization.md index 70c5389..1cfd734 100644 --- a/book/src/user/serialization.md +++ b/book/src/user/serialization.md @@ -56,11 +56,10 @@ Is encoded as - `0b`: the length of the message - `68656c6c6f20776f726c64`: the message -```admonish note -The ciphersuite ID is encoded multiple times in this case because `SigningPackage` includes -`SigningCommitments`, which also need to be communicated in Round 1 and thus also encodes -its ciphersuite ID. This is the only instance where this happens. -``` +> [!NOTE] +> The ciphersuite ID is encoded multiple times in this case because `SigningPackage` includes +> `SigningCommitments`, which also need to be communicated in Round 1 and thus also encodes +> its ciphersuite ID. This is the only instance where this happens. ## Test Vectors diff --git a/book/src/zcash/devtool-demo.md b/book/src/zcash/devtool-demo.md index e8e713a..36aa763 100644 --- a/book/src/zcash/devtool-demo.md +++ b/book/src/zcash/devtool-demo.md @@ -60,11 +60,10 @@ frost-client init -c eve.toml This will create a config file for three users; Alice, Bob and Eve. -```admonish note -If you really want to run the demo in separate machines, then you can omit the -`-c alice.toml` part of the command (i.e. run `frost-client init`); it will -save to a default location in the user's home directory. -``` +> [!NOTE] +> If you really want to run the demo in separate machines, then you can omit the +> `-c alice.toml` part of the command (i.e. run `frost-client init`); it will +> save to a default location in the user's home directory. ## Generating FROST key shares @@ -92,11 +91,10 @@ Generation. If you did the previous section, skip to "Generating the Full Viewing Key for the wallet". -```admonish note -This section assumes each participant is running the commands in their own -machine. If you want to simulate all of them in a single machine, -specify the config file for the user (e.g. `-c alice.toml`) accordingly. -``` +> [!NOTE] +> This section assumes each participant is running the commands in their own +> machine. If you want to simulate all of them in a single machine, +> specify the config file for the user (e.g. `-c alice.toml`) accordingly. ### Initializing config files @@ -161,9 +159,8 @@ and the threshold number with the one given by the first participant. frost-client dkg -d "Alice, Bob and Eve's group" -s localhost:2744 -t 2 -C redpallas ``` -```admonish note -A future version might not require specifying the threshold and group name. -``` +> [!NOTE] +> A future version might not require specifying the threshold and group name. ## Generating the Full Viewing Key for the wallet @@ -271,10 +268,9 @@ the one generated with the `zcash-sign` tool and press enter. The tool will connect to the server and wait for the other participants. -```admonish warning -If you prefer to pass the message (SIGHASH) or randomizer as files by using -the `-m` and `-r` arguments, you will need to convert them to binary format. -``` +> [!WARNING] +> If you prefer to pass the message (SIGHASH) or randomizer as files by using +> the `-m` and `-r` arguments, you will need to convert them to binary format. ### Participant 1 (Alice) diff --git a/book/src/zcash/server.md b/book/src/zcash/server.md index 6350394..50ba3cc 100644 --- a/book/src/zcash/server.md +++ b/book/src/zcash/server.md @@ -159,29 +159,26 @@ For Participants: - Wait for round 2 message by repeatedly polling `/receive` each 2 seconds or longer - Send round 2 message by using `/send` -```admonish info -**Polling** is not optimal. The server will support a better mechanism in the -future. -``` - -```admonish info -Selecting sessions is tricky. Ideally, the user should select what session -to proceed by checking the message being signed; however, that is usually -sent in Round 2. There are multiple ways to handle this: - -- Simply show the users who are participants, hoping that is enough to - disambiguate (we assume that concurrent signing sessions won't be that common) -- Quietly proceed with all sessions, and only prompt the user after the message - is received. (It's harmless to do round 1 of FROST even if the user might - not have agreed to sign the message yet.) -- Change the application so that the message is sent to the participants first - (the server does not really care how the protocol is run). -``` - -```admonish critical -Always gather consent from the user by showing them the message before -signing it. -``` +> [!NOTE] +> **Polling** is not optimal. The server will support a better mechanism in the +> future. + +> [!NOTE] +> Selecting sessions is tricky. Ideally, the user should select what session +> to proceed by checking the message being signed; however, that is usually +> sent in Round 2. There are multiple ways to handle this: +> +> - Simply show the users who are participants, hoping that is enough to +> disambiguate (we assume that concurrent signing sessions won't be that common) +> - Quietly proceed with all sessions, and only prompt the user after the message +> is received. (It's harmless to do round 1 of FROST even if the user might +> not have agreed to sign the message yet.) +> - Change the application so that the message is sent to the participants first +> (the server does not really care how the protocol is run). + +> [!CAUTION] +> Always gather consent from the user by showing them the message before +> signing it. ### `/challenge` @@ -314,11 +311,10 @@ Coordinator, pass an empty list in `recipients` (**do not** use the Coordinator's public key, because that might be ambiguous if they're also a Participant). -```admonish critical -Messages **MUST** be end-to-end encrypted between recipients. The server can't -enforce this and if you fail to encrypt them then the server could read -all the messages. -``` +> [!CAUTION] +> Messages **MUST** be end-to-end encrypted between recipients. The server can't +> enforce this and if you fail to encrypt them then the server could read +> all the messages. ### `/receive` diff --git a/book/src/zcash/technical-details.md b/book/src/zcash/technical-details.md index e75adfe..9f0c39e 100644 --- a/book/src/zcash/technical-details.md +++ b/book/src/zcash/technical-details.md @@ -74,17 +74,15 @@ not catastrophic. Users can recover their key share with the help of other participants, and would only need to remember their identifier (and other participants can probably help with that). -```admonish note -Orchard is simpler to handle, so it may be a good idea to just -support it with FROST. -``` - -```admonish note -The only secret information is the key share. So another possibility -is to just ask the user to backup it (using a seed phrase format, or other -string encoding) and get the remaining information from the other participants -when recovering a wallet. -``` +> [!NOTE] +> Orchard is simpler to handle, so it may be a good idea to just +> support it with FROST. + +> [!NOTE] +> The only secret information is the key share. So another possibility +> is to just ask the user to backup it (using a seed phrase format, or other +> string encoding) and get the remaining information from the other participants +> when recovering a wallet. ## Communications diff --git a/book/src/zcash/ywallet-demo.md b/book/src/zcash/ywallet-demo.md index 1972695..cece551 100644 --- a/book/src/zcash/ywallet-demo.md +++ b/book/src/zcash/ywallet-demo.md @@ -65,11 +65,10 @@ frost-client init -c eve.toml This will create a config file for three users; Alice, Bob and Eve. -```admonish note -If you really want to run the demo in separate machines, then you can omit the -`-c alice.toml` part of the command (i.e. run `frost-client init`); it will -save to a default location in the user's home directory. -``` +> [!NOTE] +> If you really want to run the demo in separate machines, then you can omit the +> `-c alice.toml` part of the command (i.e. run `frost-client init`); it will +> save to a default location in the user's home directory. ## Generating FROST key shares @@ -97,11 +96,10 @@ Generation. If you did the previous section, skip to "Generating the Full Viewing Key for the wallet". -```admonish note -This section assumes each participant is running the commands in their own -machine. If you want to simulate all of them in a single machine, -specify the config file for the user (e.g. `-c alice.toml`) accordingly. -``` +> [!NOTE] +> This section assumes each participant is running the commands in their own +> machine. If you want to simulate all of them in a single machine, +> specify the config file for the user (e.g. `-c alice.toml`) accordingly. ### Initializing config files @@ -166,9 +164,8 @@ and the threshold number with the one given by the first participant. frost-client dkg -d "Alice, Bob and Eve's group" -s localhost:2744 -t 2 -C redpallas ``` -```admonish note -A future version might not require specifying the threshold and group name. -``` +> [!NOTE] +> A future version might not require specifying the threshold and group name. ## Generating the Full Viewing Key for the wallet @@ -210,15 +207,14 @@ Now you will need to fund this wallet with some ZEC. Use the Orchard address printed by the signer (see warning below). Send ZEC to that address using another account (or try [ZecFaucet](https://zecfaucet.com/)). -```admonish danger -The address being show by Ywallet is a unified address that includes both an -Orchard and Sapling address. For the demo to work, you need to receive funds in -your Orchard address. Whether that will happen depends on multiple factors so -it's probably easier to use just the Orchard-only address printed by the signer. -In Ywallet, you can also swipe right on the QR Code until it shows the "Orchard -Address". **IF YOU SEND IT TO THE SAPLING ADDRESS, THE FUNDS WILL BECOME -UNSPENDABLE AND WILL BE LOST!** -``` +> [!CAUTION] +> The address being show by Ywallet is a unified address that includes both an +> Orchard and Sapling address. For the demo to work, you need to receive funds in +> your Orchard address. Whether that will happen depends on multiple factors so +> it's probably easier to use just the Orchard-only address printed by the signer. +> In Ywallet, you can also swipe right on the QR Code until it shows the "Orchard +> Address". **IF YOU SEND IT TO THE SAPLING ADDRESS, THE FUNDS WILL BECOME +> UNSPENDABLE AND WILL BE LOST!** ## Creating the transaction @@ -280,10 +276,9 @@ the one generated with the `zcash-sign` tool and press enter. The tool will connect to the server and wait for the other participants. -```admonish warning -If you prefer to pass the message (SIGHASH) or randomizer as files by using -the `-m` and `-r` arguments, you will need to convert them to binary format. -``` +> [!WARNING] +> If you prefer to pass the message (SIGHASH) or randomizer as files by using +> the `-m` and `-r` arguments, you will need to convert them to binary format. ### Participant 1 (Alice) From 1aef3fbdf7e845da2d389d99b7e2675468ebdf85 Mon Sep 17 00:00:00 2001 From: Conrado Date: Thu, 23 Apr 2026 08:50:37 -0300 Subject: [PATCH 85/86] Release 3.0.0 (#1041) * bump to 3.0.0 * fixes from Claude review --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 6 +++--- book/src/tutorial/importing.md | 4 ++-- frost-core/CHANGELOG.md | 27 +++++++++++++++++++++++++-- frost-rerandomized/CHANGELOG.md | 3 +++ 5 files changed, 41 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f69237..216200a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -526,7 +526,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "frost-core" -version = "3.0.0-rc.0" +version = "3.0.0" dependencies = [ "byteorder", "const-crc32-nostd", @@ -554,7 +554,7 @@ dependencies = [ [[package]] name = "frost-ed25519" -version = "3.0.0-rc.0" +version = "3.0.0" dependencies = [ "criterion", "curve25519-dalek", @@ -576,7 +576,7 @@ dependencies = [ [[package]] name = "frost-ed448" -version = "3.0.0-rc.0" +version = "3.0.0" dependencies = [ "criterion", "document-features", @@ -597,7 +597,7 @@ dependencies = [ [[package]] name = "frost-p256" -version = "3.0.0-rc.0" +version = "3.0.0" dependencies = [ "criterion", "document-features", @@ -618,7 +618,7 @@ dependencies = [ [[package]] name = "frost-rerandomized" -version = "3.0.0-rc.0" +version = "3.0.0" dependencies = [ "derive-getters", "document-features", @@ -629,7 +629,7 @@ dependencies = [ [[package]] name = "frost-ristretto255" -version = "3.0.0-rc.0" +version = "3.0.0" dependencies = [ "criterion", "curve25519-dalek", @@ -651,7 +651,7 @@ dependencies = [ [[package]] name = "frost-secp256k1" -version = "3.0.0-rc.0" +version = "3.0.0" dependencies = [ "criterion", "document-features", @@ -672,7 +672,7 @@ dependencies = [ [[package]] name = "frost-secp256k1-tr" -version = "3.0.0-rc.0" +version = "3.0.0" dependencies = [ "criterion", "document-features", diff --git a/Cargo.toml b/Cargo.toml index 9ed7aac..cf479d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ members = [ # in gencode/src/main.rs. edition = "2021" rust-version = "1.81" -version = "3.0.0-rc.0" +version = "3.0.0" authors = [ "Deirdre Connolly ", "Chelsea Komlo ", @@ -43,8 +43,8 @@ rand_core = "0.6" serde_json = "1.0" tokio = { version = "1.0", features = ["rt", "time", "macros"] } -frost-core = { path = "frost-core", version = "3.0.0-rc.0", default-features = false } -frost-rerandomized = { path = "frost-rerandomized", version = "3.0.0-rc.0", default-features = false } +frost-core = { path = "frost-core", version = "3.0.0", default-features = false } +frost-rerandomized = { path = "frost-rerandomized", version = "3.0.0", default-features = false } [profile.test.package."*"] opt-level = 3 diff --git a/book/src/tutorial/importing.md b/book/src/tutorial/importing.md index ca33d6a..40bc51f 100644 --- a/book/src/tutorial/importing.md +++ b/book/src/tutorial/importing.md @@ -6,7 +6,7 @@ Add to your `Cargo.toml` file: ``` [dependencies] -frost-ristretto255 = "3.0.0-rc.0" +frost-ristretto255 = "3.0.0" ``` ## Handling errors @@ -38,7 +38,7 @@ needs to be transmitted. The importing would look like: ``` [dependencies] -frost-ristretto255 = { version = "3.0.0-rc.0", features = ["serde"] } +frost-ristretto255 = { version = "3.0.0", features = ["serde"] } ``` Note that serde usage is optional. Applications can use different encodings, and diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index 373eb44..1fcb736 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -5,6 +5,16 @@ Entries are listed in reverse chronological order. ## Unreleased +## 3.0.0 + +Refer to the `3.0.0-rc.0` entry below for the main changes if you are upgrading +from `2.x`. + +### Fixed + +* Fixed `verify_signature_share()` so that it calls the + `Ciphersuite::pre_commitment_aggregate()` hook + ## 3.0.0-rc.0 @@ -19,7 +29,9 @@ Entries are listed in reverse chronological order. * Changed `InvalidSignatureShare::culprit` to `culprits`; it is now a `Vec`. * Changed `Error::culprit()` to `culprits()`; is is now a `Vec`. * Added a `min_signers` argument to `PublicKeyPackage::new()`. -* The `std` and `nightly` features were removed from all crates. +* The `std` and `nightly` features were removed from all crates since the crates + are all `no-std`. If you enabled them, just remove the feature from the + import. * Renamed `frost_core::keys::refresh::refresh_dkg_part_1` to `refresh_dkg_part1`. * Fixed the crate-specific versions of the `refresh` module to be non-generic. * Removed the `min_signers` and `max_signers` arguments from @@ -48,7 +60,7 @@ Entries are listed in reverse chronological order. anymore; instead, they must implement `AsMut<[u8]>` and `TryFrom<&[u8]>`. This should be trivial in most cases since they are often implemented by arrays. -### Additional changes +### Added * Added DKG refresh functions to the crate-specific `refresh` modules. * Re-exported the `frost-rerandomized` crate in the ciphersuite functions, e.g. @@ -58,6 +70,17 @@ Entries are listed in reverse chronological order. * Added `aggregate_custom()` function to allow specifying which cheater detection strategy to use. The original `aggregate()` behaviour is to use the `CheaterDetection::FirstCheater` strategy. +* Added `ZeroizeOnDrop` for more types. +* Added `NonceCommitment` getters and `new()` under `internals` feature. + +### Fixed + +* Fixed `dkg::round2::SecretPackage` serialization (it would previously return + an error when deserialized). Technically this is a breaking change; if somehow + your application has serialized round 2 SecretPackages in long-term storage + and you need to deserialize then, you will need to manually parse them + and discard the first element of the `commitment` field. +* Fixed the build for some specific feature combinations. ## 2.2.0 diff --git a/frost-rerandomized/CHANGELOG.md b/frost-rerandomized/CHANGELOG.md index 5a18657..eb10fc5 100644 --- a/frost-rerandomized/CHANGELOG.md +++ b/frost-rerandomized/CHANGELOG.md @@ -5,6 +5,9 @@ Entries are listed in reverse chronological order. ## Unreleased +## 3.0.0 + +* No changes. ## 3.0.0-rc.0 From 2016e44ba4a4757a996300350063b937a2ad33e8 Mon Sep 17 00:00:00 2001 From: natalie Date: Thu, 23 Apr 2026 14:53:04 +0100 Subject: [PATCH 86/86] Add explicit release-drafter permissions (#1061) Add explicit release-drafter permissions (#1060) --- .github/workflows/release-drafter.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 23952d0..2e0a7b6 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -8,6 +8,9 @@ on: jobs: update_release_draft: runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: read steps: # Drafts your next Release notes as Pull Requests are merged into main - uses: release-drafter/release-drafter@v7