diff --git a/packages/wbraid/Cargo.lock b/packages/wbraid/Cargo.lock index cd87ceddc5f..fa2422cbec3 100644 --- a/packages/wbraid/Cargo.lock +++ b/packages/wbraid/Cargo.lock @@ -1762,7 +1762,7 @@ dependencies = [ "base16ct 0.2.0", "crypto-bigint 0.5.5", "digest 0.10.7", - "ff 0.13.1", + "ff", "generic-array", "group", "pem-rfc7468 0.7.0", @@ -1895,16 +1895,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ff" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f" -dependencies = [ - "rand_core 0.10.1", - "subtle", -] - [[package]] name = "fiat-crypto" version = "0.3.0" @@ -2096,7 +2086,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff 0.13.1", + "ff", "rand_core 0.6.4", "subtle", ] @@ -3282,14 +3272,14 @@ dependencies = [ [[package]] name = "primefield" -version = "0.14.0" +version = "0.14.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4" +checksum = "1b52e6ee42db392378a95622b463c9740631171d1efce43fa445a569c1600cb6" dependencies = [ "crypto-bigint 0.7.5", "crypto-common 0.2.2", - "ff 0.14.0", "rand_core 0.10.1", + "rustcrypto-ff", "subtle", "zeroize", ] @@ -4609,7 +4599,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.3", + "getrandom 0.3.4", "once_cell", "rustix", "windows-sys 0.61.2", @@ -5859,3 +5849,8 @@ name = "zmij" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[patch.unused]] +name = "auto_generate_cdp" +version = "0.4.4" +source = "git+https://github.com/mdrokz/auto_generate_cdp?rev=634e636fd8edbe032d7191326527c721a2b51106#634e636fd8edbe032d7191326527c721a2b51106" diff --git a/packages/wbraid/PROVENANCE.md b/packages/wbraid/PROVENANCE.md index 40966489b8f..ffad0200aec 100644 --- a/packages/wbraid/PROVENANCE.md +++ b/packages/wbraid/PROVENANCE.md @@ -61,3 +61,69 @@ repository-root `REUSE.toml`. - **Added AGPL-3.0-only headers to `crates/braid/fuzz/`** (`Cargo.toml` and `.gitignore`), which carried no licence markers. The fuzz crate is its own cargo workspace and keeps no lockfile, matching the source branch. + +## Local modifications for building on stable Rust + +The source branch assumes a nightly toolchain; the following changes make the +workspace build with stable Rust (1.96.0), with upstream behaviour restored on +nightly by enabling the named features: + +- **Gated `crates/vsc`'s nightly feature gates** (`stmt_expr_attributes`, + `proc_macro_hygiene`) behind `cfg_attr(feature = "custom-warnings", ...)`, + and wrapped every `#[crate::warning(...)]` in statement, expression, or + file-module position the same way — those positions reject proc-macro + attributes on stable even though `custom_warning_macro` expands to a no-op + pass-through when its `on` feature is off. Item-position uses are unchanged. +- **Gated the libtest bench** `crates/vsc/benches/shuffle.rs` + (`#![feature(test)]`, a hard error on stable) behind a new empty + `nightly-benches` feature via `required-features`, so `--all-targets` builds + skip it on stable. +- **Pinned `primefield` to `0.14.0-rc.9` in `Cargo.lock`**: cargo's pre-release + semver rules resolve `p256 0.14.0-rc.9`'s `primefield 0.14.0-rc.9` + requirement to the API-incompatible `0.14.0` final release, which does not + compile against p256 rc.9. +- The `[[patch.unused]]` entry for `auto_generate_cdp` in `Cargo.lock` is + written by cargo because `packages/.cargo/config.toml` (an ancestor config) + declares that patch for the main workspace; it is inert here. + +`cargo clippy --workspace` passes on stable. `cargo clippy -p vsc +--all-targets` fails inside `crates/vsc`'s test modules and the +`shuffle_scaling` example (mostly `unwrap_used` and pedantic lints in test +code, identical on nightly); that upstream state is left untouched. + +## Local modifications for clippy + +The tree was imported with warn-level clippy findings in `braid`, `rnk` and +`v2v`, so a `-D warnings` gate failed. The following changes make + +``` +cargo clippy --workspace --exclude vsc --all-targets --no-deps -- -D warnings +cargo clippy -p vsc --no-deps +``` + +pass on stable 1.96.0. `crates/vsc` is linted separately at upstream's own +levels (`--no-deps` keeps `-D warnings` from reaching it through the workspace +run) and is untouched: its lib passes, with upstream's warn-level +`indexing_slicing` findings. + +- **`crates/braid`**: dropped the same-type casts of `PROTOCOL_MANAGER_INDEX` + and a `clone()` of the `Copy` type `MessageType`; `AccumulatorSet::extract` + is `flatten().cloned()`. Two functions were over the argument limit: + `Trustee::sign_mix` lost its unused `_self_index` parameter; + `compute_partial_decryptions_inner` keeps its eight under + `#[expect(clippy::too_many_arguments)]`, since it exists as a monomorphized + call target for the dispatching function and takes exactly that function's + locals. In `tests/model_check*.rs`: two `clone()`s of `Copy` group elements + and a redundant closure. +- **`crates/rnk`**: the seven value types' inherent `to_string()` methods + (`inherent_to_string`) became `fmt::Display` impls producing the same JSON. + `.to_string()` callers are unaffected; the `unwrap()` on serialization is + gone. +- **`crates/v2v`**: `is_multiple_of` for the `% 2` checks, a `clone()` of a + `Copy` scalar, and the test module of `wire/protinfo.rs` moved to the end of + the file (`items_after_test_module`, it had sat between the parse and emit + halves). `wire::crypto::pos_seed` keeps its eight parameters under + `#[expect(clippy::too_many_arguments)]`: they map one-to-one onto the node + the spec hashes. In `tests/`: needless borrows, a `&PathBuf` parameter that is + now `&Path`, a duplicated `allow(dead_code)`, and a boxed-closure table that + is now fn pointers behind a type alias. diff --git a/packages/wbraid/crates/b4/src/s3.rs b/packages/wbraid/crates/b4/src/s3.rs index 308a074d0f5..22cbf4e1d93 100644 --- a/packages/wbraid/crates/b4/src/s3.rs +++ b/packages/wbraid/crates/b4/src/s3.rs @@ -8,12 +8,12 @@ use std::time::Duration; pub async fn init_s3_client() -> Client { let config = aws_config::load_defaults(BehaviorVersion::latest()).await; - + // Force path-style URLs for LocalStack compatibility let s3_config = aws_sdk_s3::config::Builder::from(&config) .force_path_style(true) .build(); - + Client::from_conf(s3_config) } diff --git a/packages/wbraid/crates/b4/src/state.rs b/packages/wbraid/crates/b4/src/state.rs index a9f1dd3e2f1..d222cecdc8b 100644 --- a/packages/wbraid/crates/b4/src/state.rs +++ b/packages/wbraid/crates/b4/src/state.rs @@ -14,9 +14,9 @@ pub struct AppState { impl AppState { pub fn new(db: SqlitePool, s3_client: S3Client) -> Self { - let bucket_name = std::env::var("S3_BUCKET_NAME") - .unwrap_or_else(|_| "wbraid-messages".to_string()); - + let bucket_name = + std::env::var("S3_BUCKET_NAME").unwrap_or_else(|_| "wbraid-messages".to_string()); + Self { db, s3_client, diff --git a/packages/wbraid/crates/braid/src/board/mod.rs b/packages/wbraid/crates/braid/src/board/mod.rs index 64994e523e7..6e6d6fde88a 100644 --- a/packages/wbraid/crates/braid/src/board/mod.rs +++ b/packages/wbraid/crates/braid/src/board/mod.rs @@ -277,8 +277,8 @@ mod tests { use crate::messages::artifact::Configuration; use crate::messages::newtypes::{zero_hash, ConfigurationHash, PublicKeyHash}; - use crate::protocol_manager::ProtocolManager; use crate::messages::wire::ProtocolMessage; + use crate::protocol_manager::ProtocolManager; use cryptography::utils::serialization::Serializable; @@ -363,7 +363,11 @@ mod tests { let first = ProtocolMessage::::shares(&trustee1, DATE, cfg_hash, &vec![1u8, 2, 3]); let first_bytes = first.ser(); client.post(vec![first]).await?; - assert_eq!(board.snapshot().len(), 2, "Configuration + the first Shares"); + assert_eq!( + board.snapshot().len(), + 2, + "Configuration + the first Shares" + ); assert_eq!(client.own_posts().len(), 1, "the slot is now recorded"); // A recomputed sharing for the same slot: fresh randomness, so a different @@ -403,8 +407,7 @@ mod tests { cfg_message, } = setup::(2)?; let sk1 = signing_keys.into_iter().next().unwrap(); - let trustee1 = - Trustee::::new("1".to_string(), sk1, KeyPair::::generate(), &cfg)?; + let trustee1 = Trustee::::new("1".to_string(), sk1, KeyPair::::generate(), &cfg)?; // Parent (DKG) board: Configuration + a Shares from trustee 1. let parent_board = MemoryBoard::::new(); @@ -493,8 +496,7 @@ mod tests { cfg_message, } = setup::(2)?; let sk1 = signing_keys.into_iter().next().unwrap(); - let trustee1 = - Trustee::::new("1".to_string(), sk1, KeyPair::::generate(), &cfg)?; + let trustee1 = Trustee::::new("1".to_string(), sk1, KeyPair::::generate(), &cfg)?; let dkg_board = MemoryBoard::::new(); dkg_board.push(cfg_message); @@ -563,8 +565,7 @@ mod tests { .. } = setup::(2)?; let sk1 = signing_keys.into_iter().next().unwrap(); - let trustee1 = - Trustee::::new("1".to_string(), sk1, KeyPair::::generate(), &cfg)?; + let trustee1 = Trustee::::new("1".to_string(), sk1, KeyPair::::generate(), &cfg)?; let board = MemoryBoard::::new(); board.push(cfg_message); diff --git a/packages/wbraid/crates/braid/src/board/store.rs b/packages/wbraid/crates/braid/src/board/store.rs index 1d099820796..b38e857f439 100644 --- a/packages/wbraid/crates/braid/src/board/store.rs +++ b/packages/wbraid/crates/braid/src/board/store.rs @@ -179,7 +179,10 @@ impl MessageStore { /// message (single ballots slot per board). Feeds the tally-scoped /// Fiat-Shamir labels of the mix and decrypt phases. pub fn tally_id(&self) -> Option { - self.ballots.keys().next().map(|predicate| predicate.tally_id) + self.ballots + .keys() + .next() + .map(|predicate| predicate.tally_id) } /// The body bytes of the `Mix` message whose output out-hash is `output`. diff --git a/packages/wbraid/crates/braid/src/board/verify.rs b/packages/wbraid/crates/braid/src/board/verify.rs index 49ee10b4352..176869f5621 100644 --- a/packages/wbraid/crates/braid/src/board/verify.rs +++ b/packages/wbraid/crates/braid/src/board/verify.rs @@ -61,7 +61,7 @@ pub fn verify( message.sender.pk ) })?; - let is_manager = position == PROTOCOL_MANAGER_INDEX as usize; + let is_manager = position == PROTOCOL_MANAGER_INDEX; let verifier = if is_manager { &configuration.protocol_manager } else { diff --git a/packages/wbraid/crates/braid/src/datalog/accumulator.rs b/packages/wbraid/crates/braid/src/datalog/accumulator.rs index 337c31466d9..8d368bd01fe 100644 --- a/packages/wbraid/crates/braid/src/datalog/accumulator.rs +++ b/packages/wbraid/crates/braid/src/datalog/accumulator.rs @@ -88,10 +88,6 @@ impl AccumulatorSet { /// Extract all present values in trustee-index order. pub(crate) fn extract(&self) -> Vec { - self.values - .iter() - .filter(|t| t.is_some()) - .map(|t| t.clone().expect("t.is_some() == true")) - .collect() + self.values.iter().flatten().cloned().collect() } } diff --git a/packages/wbraid/crates/braid/src/datalog/composed.rs b/packages/wbraid/crates/braid/src/datalog/composed.rs index bf502ae3f29..0813a8631f0 100644 --- a/packages/wbraid/crates/braid/src/datalog/composed.rs +++ b/packages/wbraid/crates/braid/src/datalog/composed.rs @@ -105,8 +105,7 @@ mod tests { /// manager input and must halt the protocol. #[test] fn mixing_set_size_must_match_threshold() { - run(&config_and_ballots(vec![1, 2])) - .expect("a threshold-sized mixing set must not error"); + run(&config_and_ballots(vec![1, 2])).expect("a threshold-sized mixing set must not error"); for trustees in [vec![1], vec![1, 2, 3]] { let err = run(&config_and_ballots(trustees.clone())) diff --git a/packages/wbraid/crates/braid/src/lib.rs b/packages/wbraid/crates/braid/src/lib.rs index 6056a271eeb..d879dcac7df 100644 --- a/packages/wbraid/crates/braid/src/lib.rs +++ b/packages/wbraid/crates/braid/src/lib.rs @@ -95,8 +95,8 @@ extern crate cfg_if; pub mod board; pub mod datalog; -pub mod messages; pub mod dispatch; +pub mod messages; pub mod protocol_manager; pub mod session; pub mod trustee; diff --git a/packages/wbraid/crates/braid/src/messages/artifact.rs b/packages/wbraid/crates/braid/src/messages/artifact.rs index 73088d469b7..53d3797e162 100644 --- a/packages/wbraid/crates/braid/src/messages/artifact.rs +++ b/packages/wbraid/crates/braid/src/messages/artifact.rs @@ -87,7 +87,7 @@ impl Configuration { trustee_pk: &>::Verifier, ) -> Option { if trustee_pk == &self.protocol_manager { - Some(PROTOCOL_MANAGER_INDEX as usize) + Some(PROTOCOL_MANAGER_INDEX) } else { self.trustees.iter().position(|t| t == trustee_pk) } diff --git a/packages/wbraid/crates/braid/src/messages/mod.rs b/packages/wbraid/crates/braid/src/messages/mod.rs index fb8e97e6a44..eb9bbe46871 100644 --- a/packages/wbraid/crates/braid/src/messages/mod.rs +++ b/packages/wbraid/crates/braid/src/messages/mod.rs @@ -20,8 +20,8 @@ //! *participant* (a `Signer`) rather than message vocabulary — lives in //! [`crate::protocol_manager`], alongside [`crate::trustee`]. -pub mod newtypes; pub mod artifact; +pub mod newtypes; pub mod wire; pub mod predicate; diff --git a/packages/wbraid/crates/braid/src/messages/wire.rs b/packages/wbraid/crates/braid/src/messages/wire.rs index 08f4bf1cd88..95d85c92d37 100644 --- a/packages/wbraid/crates/braid/src/messages/wire.rs +++ b/packages/wbraid/crates/braid/src/messages/wire.rs @@ -255,7 +255,7 @@ impl Clone for ProtocolMessage { ProtocolMessage { sender: self.sender.clone(), signature: self.signature.clone(), - message_type: self.message_type.clone(), + message_type: self.message_type, head: self.head.clone(), body: self.body.clone(), } diff --git a/packages/wbraid/crates/braid/src/native/persistence.rs b/packages/wbraid/crates/braid/src/native/persistence.rs index 98dc678f254..4e81dff8f58 100644 --- a/packages/wbraid/crates/braid/src/native/persistence.rs +++ b/packages/wbraid/crates/braid/src/native/persistence.rs @@ -85,10 +85,9 @@ impl Persistence for SqlitePersistence { async fn load_own_posts(&self) -> Result> { let conn = self.conn.lock().expect("predicate store mutex poisoned"); let mut statement = conn.prepare("SELECT bytes, staged_ref FROM own_posts")?; - let rows = statement - .query_map([], |row| { - Ok((row.get::<_, Vec>(0)?, row.get::<_, String>(1)?)) - })?; + let rows = statement.query_map([], |row| { + Ok((row.get::<_, Vec>(0)?, row.get::<_, String>(1)?)) + })?; let mut out = Vec::new(); for row in rows { let (bytes, staged) = row?; diff --git a/packages/wbraid/crates/braid/src/native/test/board_client.rs b/packages/wbraid/crates/braid/src/native/test/board_client.rs index ddc3c7151de..35a36fb6d3a 100644 --- a/packages/wbraid/crates/braid/src/native/test/board_client.rs +++ b/packages/wbraid/crates/braid/src/native/test/board_client.rs @@ -19,9 +19,9 @@ use crate::board::transport::{MemoryBoard, MemoryTransport}; use crate::board::BoardClient; use crate::messages::artifact::Configuration; use crate::messages::newtypes::{ConfigurationHash, Timestamp}; -use crate::protocol_manager::ProtocolManager; use crate::messages::wire::ProtocolMessage; use crate::native::persistence::SqlitePersistence; +use crate::protocol_manager::ProtocolManager; use crate::trustee::Trustee; const DATE: Timestamp = 0; diff --git a/packages/wbraid/crates/braid/src/native/test/protocol_test_http.rs b/packages/wbraid/crates/braid/src/native/test/protocol_test_http.rs index 8d5e1120562..348c84d98b4 100644 --- a/packages/wbraid/crates/braid/src/native/test/protocol_test_http.rs +++ b/packages/wbraid/crates/braid/src/native/test/protocol_test_http.rs @@ -30,15 +30,15 @@ use crate::messages::artifact::{Ballots, Configuration, DkgPublicKey, Plaintexts use crate::messages::newtypes::{ hash_bytes, ConfigurationHash, PublicKeyHash, Timestamp, TrusteeIndex, MAX_TRUSTEES, }; -use crate::protocol_manager::ProtocolManager; use crate::messages::wire::{MessageType, ProtocolMessage}; +use crate::protocol_manager::ProtocolManager; use crate::board::persistence::NoOpPersistence; use crate::board::transport::Transport; use crate::board::BoardClient; use crate::native::http_transport::HttpTransport; -use crate::trustee::Trustee; use crate::session::Session; +use crate::trustee::Trustee; /// b4 server endpoint the test drives against (must be running, with S3). const HTTP_URL: &str = "http://127.0.0.1:3000"; diff --git a/packages/wbraid/crates/braid/src/native/test/protocol_test_http_union.rs b/packages/wbraid/crates/braid/src/native/test/protocol_test_http_union.rs index 6d33ff4f369..17560153d99 100644 --- a/packages/wbraid/crates/braid/src/native/test/protocol_test_http_union.rs +++ b/packages/wbraid/crates/braid/src/native/test/protocol_test_http_union.rs @@ -34,8 +34,8 @@ use crate::messages::artifact::{Ballots, Configuration, DkgPublicKey, Plaintexts use crate::messages::newtypes::{ hash_bytes, ConfigurationHash, PublicKeyHash, Timestamp, TrusteeIndex, MAX_TRUSTEES, }; -use crate::protocol_manager::ProtocolManager; use crate::messages::wire::{MessageType, ProtocolMessage}; +use crate::protocol_manager::ProtocolManager; use crate::board::persistence::Persistence; use crate::board::transport::Transport; @@ -262,10 +262,7 @@ fn temp_db(tag: &str) -> std::path::PathBuf { /// Drive `trustees` over their board `clients` to a fixpoint via the update-first /// cycle (§6), sequentially (HTTP latency dominates). Trustee `i` is paired with /// client `i`. -async fn drive( - trustees: &[Trustee], - clients: &mut [BoardClient], -) -> Result<()> +async fn drive(trustees: &[Trustee], clients: &mut [BoardClient]) -> Result<()> where C: Context, T: Transport, diff --git a/packages/wbraid/crates/braid/src/native/test/protocol_test_memory.rs b/packages/wbraid/crates/braid/src/native/test/protocol_test_memory.rs index 6ed783a5635..91ad26ffdd3 100644 --- a/packages/wbraid/crates/braid/src/native/test/protocol_test_memory.rs +++ b/packages/wbraid/crates/braid/src/native/test/protocol_test_memory.rs @@ -34,14 +34,14 @@ use crate::messages::artifact::{Ballots, Configuration, DkgPublicKey, Plaintexts use crate::messages::newtypes::{ hash_bytes, ConfigurationHash, PublicKeyHash, Timestamp, TrusteeIndex, MAX_TRUSTEES, }; -use crate::protocol_manager::ProtocolManager; use crate::messages::wire::{MessageType, ProtocolMessage}; +use crate::protocol_manager::ProtocolManager; use crate::board::persistence::NoOpPersistence; use crate::board::transport::{MemoryBoard, MemoryTransport}; use crate::board::BoardClient; -use crate::trustee::Trustee; use crate::session::Session; +use crate::trustee::Trustee; /// Wire `date` for every message the harness posts (§3.1); a fixed value is fine /// (M1 does not verify timestamps). @@ -306,15 +306,8 @@ async fn run_invalid_ballot() -> Result<()> { encrypted[1].proof = proof0; let ballots = Ballots::::new(encrypted); - let ballots_message = ProtocolMessage::::ballots( - &pm, - DATE, - cfg_hash, - pk_hash, - mixing_trustees, - 1, - &ballots, - ); + let ballots_message = + ProtocolMessage::::ballots(&pm, DATE, cfg_hash, pk_hash, mixing_trustees, 1, &ballots); board.push(ballots_message); let err = drive(&mut sessions) diff --git a/packages/wbraid/crates/braid/src/native/test/protocol_test_memory_union.rs b/packages/wbraid/crates/braid/src/native/test/protocol_test_memory_union.rs index fc8d4566ed2..5ba9a2efe63 100644 --- a/packages/wbraid/crates/braid/src/native/test/protocol_test_memory_union.rs +++ b/packages/wbraid/crates/braid/src/native/test/protocol_test_memory_union.rs @@ -36,8 +36,8 @@ use crate::messages::artifact::{Ballots, Configuration, DkgPublicKey, Plaintexts use crate::messages::newtypes::{ hash_bytes, ConfigurationHash, PublicKeyHash, Timestamp, TrusteeIndex, MAX_TRUSTEES, }; -use crate::protocol_manager::ProtocolManager; use crate::messages::wire::{MessageType, ProtocolMessage}; +use crate::protocol_manager::ProtocolManager; use crate::board::persistence::{NoOpPersistence, Persistence}; use crate::board::transport::{MemoryBoard, MemoryTransport, Transport}; @@ -243,10 +243,7 @@ async fn run_with_width( /// clients are advanced. Each round: update every client, `step` every trustee in /// parallel over its client view (CPU-bound crypto), then post. A round that /// produces nothing is the fixpoint. Trustee `i` is paired with client `i`. -async fn drive( - trustees: &[Trustee], - clients: &mut [BoardClient], -) -> Result<()> +async fn drive(trustees: &[Trustee], clients: &mut [BoardClient]) -> Result<()> where C: Context, // `Transport`/`Persistence` are `?Send` (Option B); the parallel step below diff --git a/packages/wbraid/crates/braid/src/trustee/decrypt.rs b/packages/wbraid/crates/braid/src/trustee/decrypt.rs index 966a1de1f89..708826bc222 100644 --- a/packages/wbraid/crates/braid/src/trustee/decrypt.rs +++ b/packages/wbraid/crates/braid/src/trustee/decrypt.rs @@ -98,6 +98,9 @@ impl Trustee { /// separate `#[inline(never)]` function so each dispatch arm is a call rather /// than an inlined copy, bounding the caller's stack frame (see the note in /// `compute_partial_decryptions`). + // The parameters are exactly the caller's locals, handed down one by one; + // bundling them into a struct would only move the count. + #[expect(clippy::too_many_arguments, reason = "mirrors the caller's locals")] #[inline(never)] fn compute_partial_decryptions_inner( &self, @@ -260,10 +263,9 @@ impl Trustee { )); } - let contributions: [AttributedDecryption; T] = - contributions.try_into().map_err(|v: Vec<_>| { - anyhow!("expected {} partial decryptions, got {}", T, v.len()) - })?; + let contributions: [AttributedDecryption; T] = contributions + .try_into() + .map_err(|v: Vec<_>| anyhow!("expected {} partial decryptions, got {}", T, v.len()))?; let plaintexts = combine::(&mix.ciphertexts, &contributions, &label) .map_err(|e| anyhow!("failed to combine decryption factors: {:?}", e))?; diff --git a/packages/wbraid/crates/braid/src/trustee/mix.rs b/packages/wbraid/crates/braid/src/trustee/mix.rs index 283af47c9df..5fabb0fcd8f 100644 --- a/packages/wbraid/crates/braid/src/trustee/mix.rs +++ b/packages/wbraid/crates/braid/src/trustee/mix.rs @@ -117,8 +117,14 @@ impl Trustee { // An empty input yields a null mix: no shuffle, no proof (§8). if input_ciphertexts.is_empty() { let mix = Mix::::null(); - let message = - ProtocolMessage::::mix(self, WIRE_DATE, *cfg_hash, *pk_hash, *input_hash, &mix); + let message = ProtocolMessage::::mix( + self, + WIRE_DATE, + *cfg_hash, + *pk_hash, + *input_hash, + &mix, + ); return Ok(vec![message]); } @@ -156,7 +162,6 @@ impl Trustee { source: &MixSource, input_hash: &CiphertextsHash, output_hash: &CiphertextsHash, - _self_index: TrusteeIndex, ) -> Result>> { use cryptography::cryptosystem::elgamal::PublicKey; use cryptography::zkp::shuffle::Shuffler; diff --git a/packages/wbraid/crates/braid/src/trustee/mod.rs b/packages/wbraid/crates/braid/src/trustee/mod.rs index 738f68470f7..eb80e084dd5 100644 --- a/packages/wbraid/crates/braid/src/trustee/mod.rs +++ b/packages/wbraid/crates/braid/src/trustee/mod.rs @@ -100,7 +100,7 @@ impl Trustee { let position = configuration .get_trustee_position(&self_pk) .ok_or_else(|| anyhow!("this trustee's key is not part of the configuration"))?; - if position == PROTOCOL_MANAGER_INDEX as usize { + if position == PROTOCOL_MANAGER_INDEX { return Err(anyhow!("the protocol manager does not run a trustee")); } // 0-based configuration position -> 1-based trustee index (§4.3). @@ -151,8 +151,8 @@ impl Trustee { Action::ComputeMix(cfg, public_key, source, input, self_index) => { self.compute_mix(view, cfg, public_key, source, input, *self_index) } - Action::SignMix(cfg, public_key, source, input, output, self_index) => { - self.sign_mix(view, cfg, public_key, source, input, output, *self_index) + Action::SignMix(cfg, public_key, source, input, output, _self_index) => { + self.sign_mix(view, cfg, public_key, source, input, output) } Action::ComputePartialDecryptions( cfg, diff --git a/packages/wbraid/crates/braid/src/wasm/emulator.rs b/packages/wbraid/crates/braid/src/wasm/emulator.rs index 776b8dfe920..2a3e5ee680b 100644 --- a/packages/wbraid/crates/braid/src/wasm/emulator.rs +++ b/packages/wbraid/crates/braid/src/wasm/emulator.rs @@ -67,8 +67,8 @@ use crate::messages::artifact::{Ballots, Configuration, DkgPublicKey, Plaintexts use crate::messages::newtypes::{ hash_bytes, ConfigurationHash, PublicKeyHash, Timestamp, TrusteeIndex, }; -use crate::protocol_manager::ProtocolManager; use crate::messages::wire::{MessageType, ProtocolMessage}; +use crate::protocol_manager::ProtocolManager; use crate::board::transport::Transport; use crate::board::BoardClient; @@ -413,7 +413,9 @@ fn keys_from_blob(blob: &SetupBlob) -> Result { ); let mut signing = Vec::with_capacity(blob.trustee_sks.len()); for s in &blob.trustee_sks { - signing.push(Sig::signer_from_base64_string(s).map_err(|e| anyhow!("decode trustee key: {e}"))?); + signing.push( + Sig::signer_from_base64_string(s).map_err(|e| anyhow!("decode trustee key: {e}"))?, + ); } let mut share = Vec::with_capacity(blob.share_sks.len()); for s in &blob.share_sks { @@ -630,7 +632,10 @@ impl Emulator { /// refresh always finds the setup as it stood. fn save(&self) -> Result { let setup = { - let inner = self.inner.try_borrow().map_err(|_| anyhow!("emulator is busy"))?; + let inner = self + .inner + .try_borrow() + .map_err(|_| anyhow!("emulator is busy"))?; Setup { id: self.setup_id.clone(), trustees: self.trustees_n, @@ -835,8 +840,7 @@ impl Emulator { setup: Setup, fresh: bool, ) -> Result { - let (cfg, cfg_hash) = - configuration_for(&keys, setup.threshold, setup.width).map_err(js)?; + let (cfg, cfg_hash) = configuration_for(&keys, setup.threshold, setup.width).map_err(js)?; let parent_board = parent_board_name(&setup.id); if fresh { WasmHttpTransport::create_board(&b4_url, &parent_board) diff --git a/packages/wbraid/crates/braid/src/wasm/persistence.rs b/packages/wbraid/crates/braid/src/wasm/persistence.rs index b836d3fc7bc..f762fedaeda 100644 --- a/packages/wbraid/crates/braid/src/wasm/persistence.rs +++ b/packages/wbraid/crates/braid/src/wasm/persistence.rs @@ -50,12 +50,7 @@ impl IndexedDbPersistence { request.set_on_upgrade_needed(Some( |evt: &IdbVersionChangeEvent| -> std::result::Result<(), JsValue> { for store in [PREDICATE_STORE, OWN_POST_STORE] { - if evt - .db() - .object_store_names() - .find(|n| n == store) - .is_none() - { + if evt.db().object_store_names().find(|n| n == store).is_none() { evt.db().create_object_store(store)?; } } diff --git a/packages/wbraid/crates/braid/tests/model_check.rs b/packages/wbraid/crates/braid/tests/model_check.rs index a61347aa789..8fbaba7b238 100644 --- a/packages/wbraid/crates/braid/tests/model_check.rs +++ b/packages/wbraid/crates/braid/tests/model_check.rs @@ -205,7 +205,7 @@ impl BraidModel { trustee_vks.push(Sig::verifying_key(&sk)); signing_keys.push(sk); let keypair = KeyPair::::generate(); - share_enc_keys.push(keypair.pkey.y.clone()); + share_enc_keys.push(keypair.pkey.y); share_keypairs.push(keypair); } @@ -218,8 +218,8 @@ impl BraidModel { share_enc_keys, PhantomData, ); - let configuration_hash = ConfigurationHash::from_configuration(&configuration) - .expect("configuration hash"); + let configuration_hash = + ConfigurationHash::from_configuration(&configuration).expect("configuration hash"); let mut enc_rng = C::get_rng(); let plaintexts_in: Vec<[Element; W]> = (0..BALLOTS) @@ -364,7 +364,7 @@ impl BraidModel { Turn::PostBallots => { use cryptography::cryptosystem::naoryung; let (dkg_pk, pk_hash) = self.public_key_on(state)?; - let pk = PublicKey::::new(dkg_pk.pk.clone()); + let pk = PublicKey::::new(dkg_pk.pk); let ctx_enc = braid::trustee::ballot_encryption_context::( self.configuration.id, &dkg_pk.pk, @@ -409,7 +409,12 @@ impl BraidModel { // Compute outside the lock: the cycle does real work, and another // worker asking for a different edge shouldn't wait on it. let computed = self.successor(state, turn); - self.memo.lock().unwrap().entry(key).or_insert(computed).clone() + self.memo + .lock() + .unwrap() + .entry(key) + .or_insert(computed) + .clone() } } @@ -481,18 +486,18 @@ impl Model for BraidModel { // reachability rather than liveness because the search is depth-capped // (see the module note), so "on every path" is not a claim this // exploration can support. - Property::::sometimes("plaintexts published correctly", |model, state| { - match model.plaintexts_on(state) { + Property::::sometimes( + "plaintexts published correctly", + |model, state| match model.plaintexts_on(state) { Some(published) => { let expected: HashSet<[Element; W]> = model.plaintexts_in.iter().cloned().collect(); - let actual: HashSet<[Element; W]> = - published.0.into_iter().collect(); + let actual: HashSet<[Element; W]> = published.0.into_iter().collect(); expected == actual } None => false, - } - }), + }, + ), ] } } diff --git a/packages/wbraid/crates/braid/tests/model_check_symbolic.rs b/packages/wbraid/crates/braid/tests/model_check_symbolic.rs index f3b2fd8a25a..067115f210c 100644 --- a/packages/wbraid/crates/braid/tests/model_check_symbolic.rs +++ b/packages/wbraid/crates/braid/tests/model_check_symbolic.rs @@ -791,8 +791,8 @@ impl SymbolicModel { share_enc_keys, PhantomData, ); - let configuration_hash = ConfigurationHash::from_configuration(&configuration) - .expect("configuration hash"); + let configuration_hash = + ConfigurationHash::from_configuration(&configuration).expect("configuration hash"); let trustees = signing_keys .into_iter() @@ -1122,8 +1122,7 @@ impl SymbolicModel { let mut predicates = view.get_predicates(); predicates.push(self.trustees[i].configuration_valid.clone().into()); - let actions = - braid::datalog::composed::run(&predicates).map_err(|e| anyhow::anyhow!(e))?; + let actions = braid::datalog::composed::run(&predicates).map_err(|e| anyhow::anyhow!(e))?; let mut outgoing = Vec::new(); for action in &actions { @@ -1214,10 +1213,13 @@ impl SymbolicModel { Some(DishonestKind::SkipsAnchor) | None => (input_voters.clone(), true), }; let token = encode_mix(*self_index, input, &output_voters, opaque); - vec![ProtocolMessage::::mix(t, DATE, *cfg, *pk, *input, &token)] + vec![ProtocolMessage::::mix( + t, DATE, *cfg, *pk, *input, &token, + )] } Action::SignMix(cfg, pk, source, input, output, self_index) => { - let signer = ProtocolMessage::::mix_signature(t, DATE, *cfg, *pk, *input, *output); + let signer = + ProtocolMessage::::mix_signature(t, DATE, *cfg, *pk, *input, *output); // A mixing-dishonest trustee signs without verifying anything. let blind = matches!( self.dishonest_mixers.get(self_index), @@ -1427,7 +1429,12 @@ impl SymbolicModel { return cached.clone(); } let computed = self.successor(state, turn); - self.memo.lock().unwrap().entry(key).or_insert(computed).clone() + self.memo + .lock() + .unwrap() + .entry(key) + .or_insert(computed) + .clone() } } @@ -1479,8 +1486,7 @@ impl Model for SymbolicModel { } // The adversarial manager can post a divergent second ballots while // its budget lasts (meaningful only once a first ballots exists). - if has_ballots && state.faults.ballots_equivocations < self.budgets.ballots_equivocations - { + if has_ballots && state.faults.ballots_equivocations < self.budgets.ballots_equivocations { candidates.push(Turn::EquivocateBallots); } // Adversarial b4 can begin withholding a whole strand (ballots + its @@ -1550,9 +1556,10 @@ impl Model for SymbolicModel { |model, state| { let board = BoardContents::read(model, state); let bound = state.board.len() + 1; - board.decrypted_sets(model.mixing_trustees.len()).iter().all(|set| { - matches!(board.content_at(set, bound), Some((_, opaque)) if opaque) - }) + board + .decrypted_sets(model.mixing_trustees.len()) + .iter() + .all(|set| matches!(board.content_at(set, bound), Some((_, opaque)) if opaque)) }, )); @@ -1621,9 +1628,10 @@ impl Model for SymbolicModel { )); // Non-vacuity: the adversary actually acts on some path (else every // conditioned property passes without testing the attack). - props.push(Property::::sometimes("an adversary acts", |model, state| { - adversarial_acted(model, state) - })); + props.push(Property::::sometimes( + "an adversary acts", + adversarial_acted, + )); } // The halt-on-equivocation guard applies only on a consistent board, // where a trustee can see both ballots and halt on the collision. Under @@ -1773,8 +1781,7 @@ fn view_content_maps(view: &MessageStore) -> (HashMap { - if let Some((out, opaque)) = - view.mix_body_by_output(&m.output).and_then(decode_mix) + if let Some((out, opaque)) = view.mix_body_by_output(&m.output).and_then(decode_mix) { mixes.insert(m.output, (m.input, out, opaque)); } @@ -1874,7 +1881,10 @@ fn check(model: SymbolicModel, label: &str) -> usize { fn expect_violation(model: SymbolicModel, property: &str) { let checker = model.checker().threads(1).spawn_bfs().join(); assert!( - checker.discoveries().iter().any(|(name, _)| *name == property), + checker + .discoveries() + .iter() + .any(|(name, _)| *name == property), "expected a counterexample for `{property}`, but it held" ); } diff --git a/packages/wbraid/crates/rnk/src/basic/combination.rs b/packages/wbraid/crates/rnk/src/basic/combination.rs index c1f4b263dd7..2dd1b9a23dc 100644 --- a/packages/wbraid/crates/rnk/src/basic/combination.rs +++ b/packages/wbraid/crates/rnk/src/basic/combination.rs @@ -11,6 +11,7 @@ use anyhow::Result; use number_encoding::combinadics; use serde::{Deserialize, Serialize}; use std::collections::HashSet; +use std::fmt; use crate::{Rank, Ranked, RankedValue}; @@ -46,11 +47,6 @@ impl Combination { Ok(serde_json::from_str(json)?) } - /// Serialize this Combination to JSON string. - pub fn to_string(&self) -> String { - serde_json::to_string(self).unwrap() - } - /// Get the values (chosen elements) of this combination. pub fn get_values(&self) -> &Vec { &self.values @@ -62,6 +58,14 @@ impl Combination { } } +/// JSON serialization, the inverse of [`Combination::from_string`]. +impl fmt::Display for Combination { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let json = serde_json::to_string(self).map_err(|_| fmt::Error)?; + f.write_str(&json) + } +} + /// A set type representing all possible combinations of k elements from a ground set. /// /// Combinations choose k unique elements from the ground set without regard to order. diff --git a/packages/wbraid/crates/rnk/src/basic/mcombination.rs b/packages/wbraid/crates/rnk/src/basic/mcombination.rs index 9cf93ffe042..e4e8e90ee07 100644 --- a/packages/wbraid/crates/rnk/src/basic/mcombination.rs +++ b/packages/wbraid/crates/rnk/src/basic/mcombination.rs @@ -10,6 +10,7 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use std::collections::HashSet; +use std::fmt; use crate::{Rank, Ranked, RankedValue, decode_multi, encode_multi}; @@ -45,11 +46,6 @@ impl MCombination { Ok(serde_json::from_str(json)?) } - /// Serialize this MCombination to JSON string. - pub fn to_string(&self) -> String { - serde_json::to_string(self).unwrap() - } - /// Get the values (chosen elements, with possible repetition) of this multicombination. pub fn get_values(&self) -> &Vec { &self.values @@ -61,6 +57,14 @@ impl MCombination { } } +/// JSON serialization, the inverse of [`MCombination::from_string`]. +impl fmt::Display for MCombination { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let json = serde_json::to_string(self).map_err(|_| fmt::Error)?; + f.write_str(&json) + } +} + /// A set type representing all possible multicombinations of k elements from a ground set. /// /// Multicombinations choose k elements from the ground set with repetition allowed. diff --git a/packages/wbraid/crates/rnk/src/basic/ordered_combination.rs b/packages/wbraid/crates/rnk/src/basic/ordered_combination.rs index ed0ada7be1b..cc131b084b5 100644 --- a/packages/wbraid/crates/rnk/src/basic/ordered_combination.rs +++ b/packages/wbraid/crates/rnk/src/basic/ordered_combination.rs @@ -10,6 +10,7 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use std::collections::HashSet; +use std::fmt; use crate::{Rank, Ranked, RankedValue}; @@ -45,11 +46,6 @@ impl OrderedCombination { Ok(serde_json::from_str(json)?) } - /// Serialize this OrderedCombination to JSON string. - pub fn to_string(&self) -> String { - serde_json::to_string(self).unwrap() - } - /// Get the values (ordered chosen elements) of this k-permutation. pub fn get_values(&self) -> &Vec { &self.values @@ -61,6 +57,14 @@ impl OrderedCombination { } } +/// JSON serialization, the inverse of [`OrderedCombination::from_string`]. +impl fmt::Display for OrderedCombination { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let json = serde_json::to_string(self).map_err(|_| fmt::Error)?; + f.write_str(&json) + } +} + /// A set type representing all possible k-permutations from a ground set. /// /// Ordered combinations choose k unique elements from the ground set and arrange them in order. diff --git a/packages/wbraid/crates/rnk/src/basic/permutation.rs b/packages/wbraid/crates/rnk/src/basic/permutation.rs index 9f961dde12d..ad8a35cedbb 100644 --- a/packages/wbraid/crates/rnk/src/basic/permutation.rs +++ b/packages/wbraid/crates/rnk/src/basic/permutation.rs @@ -11,6 +11,7 @@ use anyhow::Result; use number_encoding::factoradics; use serde::{Deserialize, Serialize}; use std::collections::HashSet; +use std::fmt; use crate::{Rank, Ranked, RankedValue}; @@ -46,11 +47,6 @@ impl Permutation { Ok(serde_json::from_str(json)?) } - /// Serialize this Permutation to JSON string. - pub fn to_string(&self) -> String { - serde_json::to_string(self).unwrap() - } - /// Get the values (ordered elements) of this permutation. pub fn get_values(&self) -> &Vec { &self.values @@ -62,6 +58,14 @@ impl Permutation { } } +/// JSON serialization, the inverse of [`Permutation::from_string`]. +impl fmt::Display for Permutation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let json = serde_json::to_string(self).map_err(|_| fmt::Error)?; + f.write_str(&json) + } +} + /// A set type representing all possible permutations of a ground set. /// /// Permutations use all elements from the ground set exactly once, in different orders. diff --git a/packages/wbraid/crates/rnk/src/basic/subset.rs b/packages/wbraid/crates/rnk/src/basic/subset.rs index 23f6edf6f0a..38ae9826e9e 100644 --- a/packages/wbraid/crates/rnk/src/basic/subset.rs +++ b/packages/wbraid/crates/rnk/src/basic/subset.rs @@ -10,6 +10,7 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use std::collections::HashSet; +use std::fmt; use crate::{Rank, Ranked, RankedValue}; @@ -45,11 +46,6 @@ impl Subset { Ok(serde_json::from_str(json)?) } - /// Serialize this Subset to JSON string. - pub fn to_string(&self) -> String { - serde_json::to_string(self).unwrap() - } - /// Get the values (elements in this subset) of this subset. pub fn get_values(&self) -> &Vec { &self.values @@ -61,6 +57,14 @@ impl Subset { } } +/// JSON serialization, the inverse of [`Subset::from_string`]. +impl fmt::Display for Subset { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let json = serde_json::to_string(self).map_err(|_| fmt::Error)?; + f.write_str(&json) + } +} + /// A set type representing all possible subsets of a ground set (power set). /// /// Subsets can contain any combination of elements from the ground set, including diff --git a/packages/wbraid/crates/rnk/src/composite.rs b/packages/wbraid/crates/rnk/src/composite.rs index a81945bda41..52653625bc6 100644 --- a/packages/wbraid/crates/rnk/src/composite.rs +++ b/packages/wbraid/crates/rnk/src/composite.rs @@ -15,6 +15,7 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use std::collections::HashSet; +use std::fmt; use crate::{Rank, Ranked, RankedSet, RankedValue}; @@ -45,10 +46,6 @@ impl Product { Ok(serde_json::from_str(json)?) } - pub fn to_string(&self) -> String { - serde_json::to_string(self).unwrap() - } - pub fn get_values(&self) -> &Vec { &self.values } @@ -58,6 +55,14 @@ impl Product { } } +/// JSON serialization, the inverse of [`Product::from_string`]. +impl fmt::Display for Product { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let json = serde_json::to_string(self).map_err(|_| fmt::Error)?; + f.write_str(&json) + } +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct UnionElement { member_of: String, @@ -88,10 +93,6 @@ impl UnionElement { Ok(serde_json::from_str(json)?) } - pub fn to_string(&self) -> String { - serde_json::to_string(self).unwrap() - } - pub fn get_value(&self) -> &RankedValue { &self.value } @@ -101,6 +102,14 @@ impl UnionElement { } } +/// JSON serialization, the inverse of [`UnionElement::from_string`]. +impl fmt::Display for UnionElement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let json = serde_json::to_string(self).map_err(|_| fmt::Error)?; + f.write_str(&json) + } +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct UnionSet { pub name: String, diff --git a/packages/wbraid/crates/v2v/src/emit.rs b/packages/wbraid/crates/v2v/src/emit.rs index ab52fbc981e..0f1bd622ad3 100644 --- a/packages/wbraid/crates/v2v/src/emit.rs +++ b/packages/wbraid/crates/v2v/src/emit.rs @@ -115,7 +115,11 @@ impl SessionSpec { self.active.len() )); } - if let Some(bad) = self.active.iter().find(|&&p| p < 1 || p > self.info.parties) { + if let Some(bad) = self + .active + .iter() + .find(|&&p| p < 1 || p > self.info.parties) + { return Err(anyhow!( "active party {bad} is outside 1..={}", self.info.parties @@ -306,10 +310,7 @@ pub fn mixing( let mut joint_key = None; for party in 1..=K { let shares: [VerifiableShare; K] = std::array::from_fn(|d| { - VerifiableShare::new( - dealt[d].shares[party - 1].clone(), - dealt[d].checking_values.clone(), - ) + VerifiableShare::new(dealt[d].shares[party - 1], dealt[d].checking_values.clone()) }); let (recipient, joint_pk, _vks) = Recipient::::from_shares( ParticipantPosition::from_usize(party), @@ -359,7 +360,11 @@ pub fn mixing( let u: Vec<[P256Element; W]> = mixed.iter().map(|c| c.0[0]).collect(); let scaled: Vec> = (1..=K) - .map(|party| delta.contains(&party).then(|| secrets[party - 1].mul(&inv_alpha))) + .map(|party| { + delta + .contains(&party) + .then(|| secrets[party - 1].mul(&inv_alpha)) + }) .collect(); let factors: Vec> = scaled .iter() @@ -437,17 +442,18 @@ pub fn mixing( .collect(); // --- the plaintexts ------------------------------------------------------ - let alpha_c: Vec = crate::wire::lagrange::p256_modified_lagrange_coefficients(delta, K) - .into_iter() - .map(|(negative, magnitude)| { - let s = P256Scalar::from_bytes_reduced(&magnitude); - if negative { - s.neg() - } else { - s - } - }) - .collect(); + let alpha_c: Vec = + crate::wire::lagrange::p256_modified_lagrange_coefficients(delta, K) + .into_iter() + .map(|(negative, magnitude)| { + let s = P256Scalar::from_bytes_reduced(&magnitude); + if negative { + s.neg() + } else { + s + } + }) + .collect(); let plaintexts: Vec<[P256Element; W]> = (0..n) .map(|i| { let mut combined: [P256Element; W] = std::array::from_fn(|_| P256Element::one()); @@ -513,7 +519,10 @@ fn challenges(spec: &SessionSpec, rho: &[u8]) -> VmnChallenges { ) } -fn encrypt_random(pk: &PublicKey, n: usize) -> Vec> { +fn encrypt_random( + pk: &PublicKey, + n: usize, +) -> Vec> { (0..n) .map(|_| { let m: [::Element; W] = diff --git a/packages/wbraid/crates/v2v/src/encode.rs b/packages/wbraid/crates/v2v/src/encode.rs index f9a713e22de..499a5e30f7d 100644 --- a/packages/wbraid/crates/v2v/src/encode.rs +++ b/packages/wbraid/crates/v2v/src/encode.rs @@ -131,9 +131,7 @@ fn ciphertext_parts( /// Decode an array of width-`W` ciphertexts (the inverse of /// [`ciphertexts_to_tree`], undoing the transposition). -pub fn tree_to_ciphertexts( - tree: &ByteTree, -) -> Result>> { +pub fn tree_to_ciphertexts(tree: &ByteTree) -> Result>> { let sides = tree .as_node_of(2) .map_err(|e| anyhow!("ciphertext array is not (u, v): {e}"))?; @@ -215,9 +213,7 @@ pub fn public_key_to_tree(y: &P256Element) -> Result { /// The shape of `Plaintexts.bt` and of each `DecryptionFactors.bt`: `W` /// arrays of `N` components, transposed as in [`ciphertexts_to_tree`], but with /// only one side rather than a `(u, v)` pair. -pub fn component_array_to_tree( - elements: &[[P256Element; W]], -) -> Result { +pub fn component_array_to_tree(elements: &[[P256Element; W]]) -> Result { let rows = elements .iter() .map(|e| e.iter().map(element_to_tree).collect::>>()) diff --git a/packages/wbraid/crates/v2v/src/session.rs b/packages/wbraid/crates/v2v/src/session.rs index ac22616c343..ad455f805f6 100644 --- a/packages/wbraid/crates/v2v/src/session.rs +++ b/packages/wbraid/crates/v2v/src/session.rs @@ -48,13 +48,13 @@ use cryptography::zkp::shuffle::{Responses, ShuffleCommitments, ShuffleProof, Sh use crate::challenges::VmnChallenges; use crate::decrypt::BatchedDecryptionProof; +use crate::encode; use crate::generators::vmn_generators; use crate::verify::{verify_decryption, PartyContribution, SessionParams}; use crate::wire::arithm::bool_array_values; use crate::wire::bytetree::ByteTree; use crate::wire::crypto::{global_prefix, Hashfunction}; use crate::wire::protinfo::ProtocolInfo; -use crate::encode; /// The kind of session a proof directory describes (VMNV §9.1, `type`). #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -177,18 +177,16 @@ pub fn verify_session( continue; } - let output = - encode::tree_to_ciphertexts::(&tree(dir, &format!("proofs/Ciphertexts{slot:02}.bt"))?)?; + let output = encode::tree_to_ciphertexts::(&tree( + dir, + &format!("proofs/Ciphertexts{slot:02}.bt"), + )?)?; let proof = read_shuffle_proof::(dir, slot)?; - let shuffler = Shuffler::::new(generators.clone(), PublicKey::::new(y)); - let challenges = VmnChallenges::new( - hash, - rho.clone(), - info.n_e as usize, - info.n_v as usize, - W, - ); + let shuffler = + Shuffler::::new(generators.clone(), PublicKey::::new(y)); + let challenges = + VmnChallenges::new(hash, rho.clone(), info.n_e as usize, info.n_v as usize, W); let ok = shuffler .verify_with(¤t, &output, &proof, &[], &challenges) .map_err(|e| anyhow!("shuffle verification failed for mixer {slot}: {e:?}"))?; @@ -290,10 +288,7 @@ fn read_contribution( )) } -fn read_shuffle_proof( - dir: &Path, - slot: usize, -) -> Result> { +fn read_shuffle_proof(dir: &Path, slot: usize) -> Result> { let u_n = encode::tree_to_elements(&tree( dir, &format!("proofs/PermutationCommitment{slot:02}.bt"), @@ -318,11 +313,9 @@ fn read_shuffle_proof( .collect::>(), )) }; - let f_prime = encode::tree_to_ciphertexts::(&ByteTree::node(vec![ - wrap(&pair[0])?, - wrap(&pair[1])?, - ]))? - .remove(0); + let f_prime = + encode::tree_to_ciphertexts::(&ByteTree::node(vec![wrap(&pair[0])?, wrap(&pair[1])?]))? + .remove(0); let commitments = ShuffleCommitments::::new( encode::tree_to_elements(&tau[0])?, diff --git a/packages/wbraid/crates/v2v/src/verify.rs b/packages/wbraid/crates/v2v/src/verify.rs index 6dd9e6579d9..d13d2d4dfe1 100644 --- a/packages/wbraid/crates/v2v/src/verify.rs +++ b/packages/wbraid/crates/v2v/src/verify.rs @@ -32,10 +32,10 @@ use cryptography::groups::p256::scalar::P256Scalar; use cryptography::traits::groups::{DistGroupOps, GroupElement, GroupScalar}; use crate::decrypt::{batch, BatchedDecryptionProof}; +use crate::encode; use crate::wire::bytetree::ByteTree; use crate::wire::crypto::{dec_challenge, dec_seed, Hashfunction, Prg}; use crate::wire::lagrange::{correct_set, p256_modified_lagrange_coefficients}; -use crate::encode; /// Session parameters a verifier needs, all of which come from the protocol /// info file and the proof directory rather than being assumed. diff --git a/packages/wbraid/crates/v2v/src/wire/bytetree.rs b/packages/wbraid/crates/v2v/src/wire/bytetree.rs index 09d768aec6a..3675ec33e43 100644 --- a/packages/wbraid/crates/v2v/src/wire/bytetree.rs +++ b/packages/wbraid/crates/v2v/src/wire/bytetree.rs @@ -100,8 +100,8 @@ impl ByteTree { return Err(Error::UnexpectedEnd); } let (len_bytes, mut rest) = rest.split_at(4); - let len = u32::from_be_bytes([len_bytes[0], len_bytes[1], len_bytes[2], len_bytes[3]]) - as usize; + let len = + u32::from_be_bytes([len_bytes[0], len_bytes[1], len_bytes[2], len_bytes[3]]) as usize; match tag { LEAF_TAG => { diff --git a/packages/wbraid/crates/v2v/src/wire/crypto.rs b/packages/wbraid/crates/v2v/src/wire/crypto.rs index fee132f0172..b9fc86cb393 100644 --- a/packages/wbraid/crates/v2v/src/wire/crypto.rs +++ b/packages/wbraid/crates/v2v/src/wire/crypto.rs @@ -238,12 +238,7 @@ pub fn ro_challenge(hash: Hashfunction, n_v: usize) -> RandomOracle { /// digest and then the byte tree: the prefix is **raw bytes**, not a byte tree /// node, so it is concatenated ahead of the serialized tree rather than /// wrapped with it. -pub fn oracle_query( - hash: Hashfunction, - out_bits: usize, - rho: &[u8], - data: &ByteTree, -) -> Vec { +pub fn oracle_query(hash: Hashfunction, out_bits: usize, rho: &[u8], data: &ByteTree) -> Vec { let tree_bytes = data.to_bytes(); let mut input = Vec::with_capacity(rho.len() + tree_bytes.len()); input.extend_from_slice(rho); @@ -281,6 +276,9 @@ pub fn wide_public_key(pk: &ByteTree, width: usize) -> crate::wire::error::Resul /// ``` /// /// Output length is `8 * seedlen(PRG)` bits, i.e. one PRG seed. +// One parameter per component of the node VMNV §8.3 hashes; bundling them +// would hide the correspondence with the spec. +#[expect(clippy::too_many_arguments, reason = "mirrors the spec's node")] pub fn pos_seed( hash: Hashfunction, rho: &[u8], diff --git a/packages/wbraid/crates/v2v/src/wire/lagrange.rs b/packages/wbraid/crates/v2v/src/wire/lagrange.rs index 492854618c1..78f2efb98ad 100644 --- a/packages/wbraid/crates/v2v/src/wire/lagrange.rs +++ b/packages/wbraid/crates/v2v/src/wire/lagrange.rs @@ -123,11 +123,7 @@ pub fn modified_lagrange_coefficient( } /// All modified Lagrange coefficients for Δ, in Δ's order. -pub fn modified_lagrange_coefficients( - delta: &[usize], - k: usize, - q: &BigUint, -) -> Vec { +pub fn modified_lagrange_coefficients(delta: &[usize], k: usize, q: &BigUint) -> Vec { delta .iter() .map(|&index| modified_lagrange_coefficient(delta, index, k, q)) @@ -155,10 +151,7 @@ pub fn p256_order() -> BigUint { /// sign matters — these values are deliberately signed (see the module docs) — /// so it is returned alongside rather than folded into a modular /// representative. -pub fn p256_modified_lagrange_coefficients( - delta: &[usize], - k: usize, -) -> Vec<(bool, [u8; 32])> { +pub fn p256_modified_lagrange_coefficients(delta: &[usize], k: usize) -> Vec<(bool, [u8; 32])> { let q = p256_order(); modified_lagrange_coefficients(delta, k, &q) .into_iter() diff --git a/packages/wbraid/crates/v2v/src/wire/marshal.rs b/packages/wbraid/crates/v2v/src/wire/marshal.rs index f7eb8f7b393..bb67d130549 100644 --- a/packages/wbraid/crates/v2v/src/wire/marshal.rs +++ b/packages/wbraid/crates/v2v/src/wire/marshal.rs @@ -69,12 +69,14 @@ fn to_hex(bytes: &[u8]) -> String { } fn from_hex(s: &str) -> Result> { - if s.len() % 2 != 0 { + if !s.len().is_multiple_of(2) { return Err(Error::BadMarshal("odd-length hex")); } (0..s.len()) .step_by(2) - .map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|_| Error::BadMarshal("bad hex digit"))) + .map(|i| { + u8::from_str_radix(&s[i..i + 2], 16).map_err(|_| Error::BadMarshal("bad hex digit")) + }) .collect() } diff --git a/packages/wbraid/crates/v2v/src/wire/protinfo.rs b/packages/wbraid/crates/v2v/src/wire/protinfo.rs index 1518e4afa78..288f90474a8 100644 --- a/packages/wbraid/crates/v2v/src/wire/protinfo.rs +++ b/packages/wbraid/crates/v2v/src/wire/protinfo.rs @@ -214,6 +214,105 @@ fn number(body: &str, tag: &str) -> Result { .map_err(|_| Error::BadProtocolInfo("element is not a non-negative integer")) } +// ------------------------------------------------------------------------- +// Writing protocol info files +// ------------------------------------------------------------------------- + +/// A signature key lifted verbatim from a generated protocol info file, reused +/// for every party in a synthesized one. +/// +/// Producing genuine per-party keys would mean implementing VMN's marshalling of +/// RSA public keys, and buys nothing: Fiat–Shamir verification checks no +/// signatures, which is why `vmnv` accepts a file whose parties share a key. +/// That was confirmed by running it against a hand-built four-party file. +/// +/// It is also why [`ProtocolInfo::to_xml`] stamps its output as +/// verification-only — a file with duplicate signing keys resembles a real +/// protocol configuration and must never be used as one. +pub(crate) const PLACEHOLDER_PKEY: &str = concat!( + "com.verificatum.crypto.SignaturePKeyHeuristic(RSA, bitlength=2048)::", + "0000000002010000002d636f6d2e766572696669636174756d2e63727970746f2e53", + "69676e6174757265504b657948657572697374696300000000020100000126308201", + "22300d06092a864886f70d01010105000382010f003082010a0282010100a9e0b6b8", + "450981b9baf72550e4ac92ed78a886bff8c0f2a2f123e0c9e75449c63772c2215131", + "1aa0800b2acc9d4dff21c95e9860be2a52258172b2339f8d265a5da4e176658a4477", + "19527b6cbaa2d5c9609726361c5f24764ffc4f2976bc7d2e652c742f74e9be3a41d4", + "7c965b2760631a8baad172df34291c0b911fb68dee88ff4f68ffb4d369a54cffe8e3", + "aa8a4664139d961e14df715a5334d2ea0ea88d9ddc15fff041c30af33142f8e2e0d1", + "5cf96364774f274757e80c3b26f1054d244554ab240acd5005e568239ca6d4b8b114", + "3c6b071dc06dfb7287e420bae4f84e44ec42301a363fc053d224c37df40b0301c467", + "aa506e7a6238aa9c9cb695b8207f0203010001010000000400000800", +); + +impl ProtocolInfo { + /// Render a protocol info file that `vmnv` will accept. + /// + /// The point is parameterization: `vmnv` takes its session shape — `k`, `λ`, + /// the group, the widths — from this file, so testing a shape means having a + /// file for it. Checking one in per combination does not scale, and + /// generating them with `vmni` needs a Unix host. + /// + /// # This is not a protocol configuration + /// + /// Every party gets the same signature key, so the result is usable only for + /// verification. It carries a comment saying so, because the format is + /// otherwise indistinguishable from a real one. + /// + /// # Round-trip + /// + /// `parse(x.to_xml())` returns `x`, which is what makes ρ derived from a + /// synthesized file agree with ρ derived from a generated one. + #[must_use] + pub fn to_xml(&self) -> String { + let mut out = String::new(); + out.push_str( + "\n\n\n", + ); + + let mut element = |tag: &str, value: &str| { + out.push_str(&format!(" <{tag}>{value}\n")); + }; + element("version", &self.version); + element("sid", &self.sid); + element("name", "Synthesized"); + element("descr", ""); + element("nopart", &self.parties.to_string()); + element("statdist", &self.n_r.to_string()); + element( + "bullboard", + "com.verificatum.protocol.com.BullBoardBasicHTTPW", + ); + element("thres", &self.threshold.to_string()); + element("pgroup", &self.pgroup); + element("keywidth", &self.key_width.to_string()); + element("vbitlen", "128"); + element("vbitlenro", &self.n_v.to_string()); + element("ebitlen", "128"); + element("ebitlenro", &self.n_e.to_string()); + element("prg", &self.prg); + element("rohash", &self.rohash); + element("corr", "noninteractive"); + element("width", &self.width.to_string()); + element("maxciph", "0"); + + for party in 1..=self.parties { + out.push_str(&format!( + "\n \n Party{party}\n \ + anyrole\n \n \ + {PLACEHOLDER_PKEY}\n \ + http://localhost:8040\n \ + localhost:4040\n \n" + )); + } + + out.push_str("\n\n"); + out + } +} + #[cfg(test)] mod tests { use super::*; @@ -324,7 +423,9 @@ mod tests { #[test] fn auxsid_comes_from_the_caller_not_the_file() { - let params = ProtocolInfo::parse(SAMPLE).unwrap().prefix_params("session7"); + let params = ProtocolInfo::parse(SAMPLE) + .unwrap() + .prefix_params("session7"); assert_eq!(params.auxsid, "session7"); assert_eq!(params.sid, "braidpoc"); } @@ -393,99 +494,3 @@ mod tests { assert_eq!(ProtocolInfo::parse(&xml).unwrap().parties, 5); } } - -// ------------------------------------------------------------------------- -// Writing protocol info files -// ------------------------------------------------------------------------- - -/// A signature key lifted verbatim from a generated protocol info file, reused -/// for every party in a synthesized one. -/// -/// Producing genuine per-party keys would mean implementing VMN's marshalling of -/// RSA public keys, and buys nothing: Fiat–Shamir verification checks no -/// signatures, which is why `vmnv` accepts a file whose parties share a key. -/// That was confirmed by running it against a hand-built four-party file. -/// -/// It is also why [`ProtocolInfo::to_xml`] stamps its output as -/// verification-only — a file with duplicate signing keys resembles a real -/// protocol configuration and must never be used as one. -pub(crate) const PLACEHOLDER_PKEY: &str = concat!( - "com.verificatum.crypto.SignaturePKeyHeuristic(RSA, bitlength=2048)::", - "0000000002010000002d636f6d2e766572696669636174756d2e63727970746f2e53", - "69676e6174757265504b657948657572697374696300000000020100000126308201", - "22300d06092a864886f70d01010105000382010f003082010a0282010100a9e0b6b8", - "450981b9baf72550e4ac92ed78a886bff8c0f2a2f123e0c9e75449c63772c2215131", - "1aa0800b2acc9d4dff21c95e9860be2a52258172b2339f8d265a5da4e176658a4477", - "19527b6cbaa2d5c9609726361c5f24764ffc4f2976bc7d2e652c742f74e9be3a41d4", - "7c965b2760631a8baad172df34291c0b911fb68dee88ff4f68ffb4d369a54cffe8e3", - "aa8a4664139d961e14df715a5334d2ea0ea88d9ddc15fff041c30af33142f8e2e0d1", - "5cf96364774f274757e80c3b26f1054d244554ab240acd5005e568239ca6d4b8b114", - "3c6b071dc06dfb7287e420bae4f84e44ec42301a363fc053d224c37df40b0301c467", - "aa506e7a6238aa9c9cb695b8207f0203010001010000000400000800", -); - -impl ProtocolInfo { - /// Render a protocol info file that `vmnv` will accept. - /// - /// The point is parameterization: `vmnv` takes its session shape — `k`, `λ`, - /// the group, the widths — from this file, so testing a shape means having a - /// file for it. Checking one in per combination does not scale, and - /// generating them with `vmni` needs a Unix host. - /// - /// # This is not a protocol configuration - /// - /// Every party gets the same signature key, so the result is usable only for - /// verification. It carries a comment saying so, because the format is - /// otherwise indistinguishable from a real one. - /// - /// # Round-trip - /// - /// `parse(x.to_xml())` returns `x`, which is what makes ρ derived from a - /// synthesized file agree with ρ derived from a generated one. - #[must_use] - pub fn to_xml(&self) -> String { - let mut out = String::new(); - out.push_str( - "\n\n\n", - ); - - let mut element = |tag: &str, value: &str| { - out.push_str(&format!(" <{tag}>{value}\n")); - }; - element("version", &self.version); - element("sid", &self.sid); - element("name", "Synthesized"); - element("descr", ""); - element("nopart", &self.parties.to_string()); - element("statdist", &self.n_r.to_string()); - element("bullboard", "com.verificatum.protocol.com.BullBoardBasicHTTPW"); - element("thres", &self.threshold.to_string()); - element("pgroup", &self.pgroup); - element("keywidth", &self.key_width.to_string()); - element("vbitlen", "128"); - element("vbitlenro", &self.n_v.to_string()); - element("ebitlen", "128"); - element("ebitlenro", &self.n_e.to_string()); - element("prg", &self.prg); - element("rohash", &self.rohash); - element("corr", "noninteractive"); - element("width", &self.width.to_string()); - element("maxciph", "0"); - - for party in 1..=self.parties { - out.push_str(&format!( - "\n \n Party{party}\n \ - anyrole\n \n \ - {PLACEHOLDER_PKEY}\n \ - http://localhost:8040\n \ - localhost:4040\n \n" - )); - } - - out.push_str("\n\n"); - out - } -} diff --git a/packages/wbraid/crates/v2v/tests/common/mod.rs b/packages/wbraid/crates/v2v/tests/common/mod.rs index 9cc4b63fce1..166f5d7267c 100644 --- a/packages/wbraid/crates/v2v/tests/common/mod.rs +++ b/packages/wbraid/crates/v2v/tests/common/mod.rs @@ -234,7 +234,11 @@ fn script(shape: &Shape, source: &str, out: &str) -> String { let restrict = match active { None => String::new(), Some(set) => { - let list = set.iter().map(usize::to_string).collect::>().join(","); + let list = set + .iter() + .map(usize::to_string) + .collect::>() + .join(","); format!( "for step in \"sact '{{{list}}}'\" delete mix; do\n\ \x20 eval \"./$step\" >> \"$WORK/demo.log\" 2>&1 \\\n\ @@ -358,7 +362,7 @@ impl Corpus { .arg(classpath()?) .arg("com.verificatum.protocol.mixnet.MixNetElGamalVerifyFiatShamirTool") .args(["vmnv"]) - .arg(&source) + .arg(source) .arg(&seed) .args(["-mix", "-t", "par,der,bas,PoS,Dec,u", "-wd"]) // Unique per call: concurrent vmnv runs sharing a working directory @@ -427,7 +431,11 @@ fn provision_random_source() -> Option<(PathBuf, PathBuf)> { // The hash function descriptor the PRG is built from, then the PRG itself; // `vog` writes the source and seed as a side effect of the second call. - let descriptor = vog(&source, &seed, &["-gen", "HashfunctionHeuristic", "SHA-256"])?; + let descriptor = vog( + &source, + &seed, + &["-gen", "HashfunctionHeuristic", "SHA-256"], + )?; vog( &source, &seed, diff --git a/packages/wbraid/crates/v2v/tests/corpus_roundtrip.rs b/packages/wbraid/crates/v2v/tests/corpus_roundtrip.rs index ebbd89e86d5..fc2c7aee7e0 100644 --- a/packages/wbraid/crates/v2v/tests/corpus_roundtrip.rs +++ b/packages/wbraid/crates/v2v/tests/corpus_roundtrip.rs @@ -91,7 +91,11 @@ fn every_corpus_byte_tree_roundtrips_exactly() { ); } - eprintln!("round-tripped {} byte trees from {}", files.len(), dir.display()); + eprintln!( + "round-tripped {} byte trees from {}", + files.len(), + dir.display() + ); } /// Structural checks against the documented layout (VMNV §9.1), not just byte @@ -108,14 +112,17 @@ fn corpus_structures_match_the_specification() { // point -- an end-to-end check of the coordinate encoding. let pk = ByteTree::from_bytes(&std::fs::read(dir.join("FullPublicKey.bt")).unwrap()).unwrap(); let parts = pk.as_node_of(2).expect("pk = (g, y)"); - assert_eq!(parts[0], marshal::p256::generator(), "pk's g is the P-256 generator"); + assert_eq!( + parts[0], + marshal::p256::generator(), + "pk's g is the P-256 generator" + ); let y = parts[1].as_node_of(2).expect("y is an affine point"); assert_eq!(y[0].as_leaf().unwrap().len(), width); // Ciphertexts.bt is an array of width-2 ciphertexts: node(u_arrays, v_arrays), // each side transposed into `width` component arrays of N elements. - let ciphs = - ByteTree::from_bytes(&std::fs::read(dir.join("Ciphertexts.bt")).unwrap()).unwrap(); + let ciphs = ByteTree::from_bytes(&std::fs::read(dir.join("Ciphertexts.bt")).unwrap()).unwrap(); let sides = ciphs.as_node_of(2).expect("ciphertext = (u, v)"); let u_rows = arithm::product_array_rows(&sides[0]).expect("u side transposes"); let v_rows = arithm::product_array_rows(&sides[1]).expect("v side transposes"); @@ -125,10 +132,8 @@ fn corpus_structures_match_the_specification() { assert_eq!(u_rows[0].len(), 2, "width 2"); // tau^pos: node(B, A', B', C', D', F') with |B| = |B'| = N (VMNV §8.3). - let tau = ByteTree::from_bytes( - &std::fs::read(dir.join("proofs/PoSCommitment01.bt")).unwrap(), - ) - .unwrap(); + let tau = ByteTree::from_bytes(&std::fs::read(dir.join("proofs/PoSCommitment01.bt")).unwrap()) + .unwrap(); let tau = tau.as_node_of(6).expect("tau^pos has 6 components"); assert_eq!(tau[0].as_node().unwrap().len(), n, "B has N entries"); assert_eq!(tau[2].as_node().unwrap().len(), n, "B' has N entries"); @@ -138,24 +143,35 @@ fn corpus_structures_match_the_specification() { let sigma = ByteTree::from_bytes(&std::fs::read(dir.join("proofs/PoSReply01.bt")).unwrap()).unwrap(); let sigma = sigma.as_node_of(6).expect("sigma^pos has 6 components"); - assert_eq!(sigma[0].as_leaf().unwrap().len(), width, "k_A is a fixed-width scalar"); + assert_eq!( + sigma[0].as_leaf().unwrap().len(), + width, + "k_A is a fixed-width scalar" + ); assert_eq!(sigma[1].as_node().unwrap().len(), n, "k_B has N entries"); assert_eq!(sigma[4].as_node().unwrap().len(), n, "k_E has N entries"); - assert_eq!(sigma[5].as_node().unwrap().len(), 2, "k_F has omega entries"); + assert_eq!( + sigma[5].as_node().unwrap().len(), + 2, + "k_F has omega entries" + ); // The permutation commitment is a flat array of N group elements. let mu = ByteTree::from_bytes( &std::fs::read(dir.join("proofs/PermutationCommitment01.bt")).unwrap(), ) .unwrap(); - assert_eq!(mu.as_node().unwrap().len(), n, "mu has N Pedersen commitments"); + assert_eq!( + mu.as_node().unwrap().len(), + n, + "mu has N Pedersen commitments" + ); // CorrectIndices.bt is a boolean array of length k+1 whose true entries are // the set Delta (VMNV §9.1 point 20). - let indices = ByteTree::from_bytes( - &std::fs::read(dir.join("proofs/CorrectIndices.bt")).unwrap(), - ) - .unwrap(); + let indices = + ByteTree::from_bytes(&std::fs::read(dir.join("proofs/CorrectIndices.bt")).unwrap()) + .unwrap(); let flags = arithm::bool_array_values(&indices).expect("boolean array"); assert!(flags.iter().any(|&b| b), "at least one party decrypted"); @@ -190,10 +206,9 @@ fn shuffle_challenge_matches_vmn() { let rho = reference_rho(); let seed = hex_bytes(&golden_pos_s); - let tau_pos = ByteTree::from_bytes( - &std::fs::read(dir.join("proofs/PoSCommitment01.bt")).unwrap(), - ) - .unwrap(); + let tau_pos = + ByteTree::from_bytes(&std::fs::read(dir.join("proofs/PoSCommitment01.bt")).unwrap()) + .unwrap(); let v = v2v::wire::crypto::pos_challenge( v2v::wire::crypto::Hashfunction::Sha256, @@ -228,10 +243,7 @@ fn shuffle_seed_matches_vmn() { let Some(dir) = corpus_dir() else { return common::skip("Verificatum is unavailable"); }; - let (Some(text), Some(vectors)) = ( - common::shared_raw_vectors(), - corpus_vectors(), - ) else { + let (Some(text), Some(vectors)) = (common::shared_raw_vectors(), corpus_vectors()) else { return common::skip("vmnv -t produced no test vectors"); }; let golden_pos_s = vectors["PoS.s"].clone(); @@ -239,7 +251,7 @@ fn shuffle_seed_matches_vmn() { // Diagnostic: `bas.pk` is printed in the same point-list format AND stored // on disk as FullPublicKey.bt, so parsing it and comparing against the file // isolates the parser from everything else. - let parsed_pk = parse_point_list(&text, "bas.pk").expect("bas.pk in test vectors"); + let parsed_pk = parse_point_list(text, "bas.pk").expect("bas.pk in test vectors"); let file_pk = ByteTree::from_bytes(&std::fs::read(dir.join("FullPublicKey.bt")).unwrap()).unwrap(); assert_eq!( @@ -247,27 +259,28 @@ fn shuffle_seed_matches_vmn() { "point-list parser must reproduce FullPublicKey.bt exactly" ); - let h = parse_point_list(&text, "bas.h").expect("bas.h in test vectors"); - eprintln!("parsed {} independent generators", h.as_node().unwrap().len()); + let h = parse_point_list(text, "bas.h").expect("bas.h in test vectors"); + eprintln!( + "parsed {} independent generators", + h.as_node().unwrap().len() + ); - let read_tree = |name: &str| { - ByteTree::from_bytes(&std::fs::read(dir.join(name)).unwrap()).unwrap() - }; + let read_tree = + |name: &str| ByteTree::from_bytes(&std::fs::read(dir.join(name)).unwrap()).unwrap(); // The key is WIDENED to omega before entering the query -- not the stored // FullPublicKey.bt as VMNV §8.3's "pk in C_kappa" would suggest. - let wide_pk = - v2v::wire::crypto::wide_public_key(&read_tree("FullPublicKey.bt"), 2).unwrap(); + let wide_pk = v2v::wire::crypto::wide_public_key(&read_tree("FullPublicKey.bt"), 2).unwrap(); let seed = v2v::wire::crypto::pos_seed( v2v::wire::crypto::Hashfunction::Sha256, &reference_rho(), - &marshal::p256::generator(), // g - &h, // h, the independent generators + &marshal::p256::generator(), // g + &h, // h, the independent generators &read_tree("proofs/PermutationCommitment01.bt"), // u - &wide_pk, // pk, widened to omega - &read_tree("Ciphertexts.bt"), // w = L_0 - &read_tree("proofs/Ciphertexts01.bt"), // w' = L_1 + &wide_pk, // pk, widened to omega + &read_tree("Ciphertexts.bt"), // w = L_0 + &read_tree("proofs/Ciphertexts01.bt"), // w' = L_1 ); assert_eq!( @@ -330,7 +343,10 @@ fn decryption_transcript_matches_vmn() { v2v::wire::crypto::Hashfunction::Sha256, &rho, &marshal::p256::generator(), - &read_tree(&format!("proofs/Ciphertexts{:02}.bt", meta.active_threshold)), + &read_tree(&format!( + "proofs/Ciphertexts{:02}.bt", + meta.active_threshold + )), &read_tree("proofs/PolynomialInExponent.bt"), &factors, ); @@ -372,7 +388,7 @@ fn independent_generators_match_vmn() { let Some(text) = common::shared_raw_vectors() else { return common::skip("vmnv -t produced no test vectors"); }; - let expected = parse_point_list(&text, "bas.h").expect("bas.h in test vectors"); + let expected = parse_point_list(text, "bas.h").expect("bas.h in test vectors"); let count = expected.as_node().unwrap().len(); let derived = v2v::wire::generators::independent_generators( @@ -384,7 +400,10 @@ fn independent_generators_match_vmn() { ) .expect("derive generators"); - assert_eq!(derived, expected, "derived generators must match vmnv -t bas.h"); + assert_eq!( + derived, expected, + "derived generators must match vmnv -t bas.h" + ); eprintln!("derived {count} independent generators matching VMN exactly"); } @@ -401,13 +420,14 @@ fn shuffle_seed_from_fully_derived_inputs() { return common::skip("vmnv -t produced no test vectors"); }; - let read_tree = |name: &str| { - ByteTree::from_bytes(&std::fs::read(dir.join(name)).unwrap()).unwrap() - }; + let read_tree = + |name: &str| ByteTree::from_bytes(&std::fs::read(dir.join(name)).unwrap()).unwrap(); let w = read_tree("Ciphertexts.bt"); // N is the number of ciphertexts, which also fixes how many generators the // shuffle proof needs. - let n = arithm::product_array_rows(&w.as_node_of(2).unwrap()[0]).unwrap().len(); + let n = arithm::product_array_rows(&w.as_node_of(2).unwrap()[0]) + .unwrap() + .len(); let rho = reference_rho(); let h = v2v::wire::generators::independent_generators( @@ -456,7 +476,7 @@ fn parse_point_list(text: &str, name: &str) -> Option { .map(str::trim) .filter(|t| !t.is_empty() && t.chars().all(|c| c.is_ascii_hexdigit())) .collect(); - if coords.is_empty() || coords.len() % 2 != 0 { + if coords.is_empty() || !coords.len().is_multiple_of(2) { return None; } @@ -472,10 +492,8 @@ fn parse_point_list(text: &str, name: &str) -> Option { /// parameters rather than hardcoded, so this test also re-exercises ρ. fn reference_rho() -> Vec { let info = corpus_info().expect("the generated corpus ships its protocol info"); - let auxsid = std::fs::read_to_string( - common::shared().expect("a corpus").nizkp.join("auxsid"), - ) - .expect("read auxsid"); + let auxsid = std::fs::read_to_string(common::shared().expect("a corpus").nizkp.join("auxsid")) + .expect("read auxsid"); v2v::wire::crypto::global_prefix( v2v::wire::crypto::Hashfunction::Sha256, &info.prefix_params(auxsid.trim()), @@ -486,7 +504,7 @@ fn reference_rho() -> Vec { /// (the printed test vectors trim leading zeros). fn hex_bytes(s: &str) -> Vec { let padded; - let s = if s.len() % 2 == 0 { + let s = if s.len().is_multiple_of(2) { s } else { padded = format!("0{s}"); diff --git a/packages/wbraid/crates/v2v/tests/lagrange.rs b/packages/wbraid/crates/v2v/tests/lagrange.rs index 8c4409576e0..0281518d909 100644 --- a/packages/wbraid/crates/v2v/tests/lagrange.rs +++ b/packages/wbraid/crates/v2v/tests/lagrange.rs @@ -14,8 +14,7 @@ use num_bigint::{BigInt, BigUint}; use num_traits::{One, Signed, Zero}; use v2v::wire::lagrange::{ - alpha, correct_set, lcm_up_to, modified_lagrange_coefficient, - modified_lagrange_coefficients, + alpha, correct_set, lcm_up_to, modified_lagrange_coefficient, modified_lagrange_coefficients, }; /// P-256's group order, the modulus these coefficients live in. @@ -81,7 +80,10 @@ fn coefficients_are_small_signed_integers() { // field elements, and negative values are expected. assert!(coefficients.iter().any(|c| c.is_negative())); for c in &coefficients { - assert!(c.magnitude() < &BigUint::from(u64::MAX), "coefficient stayed small"); + assert!( + c.magnitude() < &BigUint::from(u64::MAX), + "coefficient stayed small" + ); } } @@ -135,8 +137,5 @@ fn a_single_party_is_the_degenerate_case() { // above: alpha is 1 and the only coefficient is 1. let q = q(); assert_eq!(alpha(1), BigUint::one()); - assert_eq!( - modified_lagrange_coefficient(&[1], 1, 1, &q), - BigInt::one() - ); + assert_eq!(modified_lagrange_coefficient(&[1], 1, 1, &q), BigInt::one()); } diff --git a/packages/wbraid/crates/v2v/tests/random_oracle.rs b/packages/wbraid/crates/v2v/tests/random_oracle.rs index b208ec322e2..2aebed30216 100644 --- a/packages/wbraid/crates/v2v/tests/random_oracle.rs +++ b/packages/wbraid/crates/v2v/tests/random_oracle.rs @@ -38,22 +38,25 @@ fn reference_params() -> PrefixParams { } } +/// A field of [`PrefixParams`], named for the assertion, and a change to it. +type Mutation = (&'static str, fn(&mut PrefixParams)); + /// ρ must be sensitive to every field it commits to — a prefix that ignored one /// would still pass the equality test above while failing to separate sessions. #[test] fn global_prefix_binds_every_parameter() { let base = global_prefix(Hashfunction::Sha256, &reference_params()); - let mutations: Vec<(&str, Box)> = vec![ - ("version", Box::new(|p: &mut PrefixParams| p.version = "3.1.1".into())), - ("sid", Box::new(|p: &mut PrefixParams| p.sid = "other".into())), - ("auxsid", Box::new(|p: &mut PrefixParams| p.auxsid = "other".into())), - ("n_r", Box::new(|p: &mut PrefixParams| p.n_r = 101)), - ("n_v", Box::new(|p: &mut PrefixParams| p.n_v = 255)), - ("n_e", Box::new(|p: &mut PrefixParams| p.n_e = 255)), - ("prg", Box::new(|p: &mut PrefixParams| p.prg = "SHA-512".into())), - ("pgroup", Box::new(|p: &mut PrefixParams| p.pgroup = "ECqPGroup(P-384)::00".into())), - ("rohash", Box::new(|p: &mut PrefixParams| p.rohash = "SHA-512".into())), + let mutations: Vec = vec![ + ("version", |p| p.version = "3.1.1".into()), + ("sid", |p| p.sid = "other".into()), + ("auxsid", |p| p.auxsid = "other".into()), + ("n_r", |p| p.n_r = 101), + ("n_v", |p| p.n_v = 255), + ("n_e", |p| p.n_e = 255), + ("prg", |p| p.prg = "SHA-512".into()), + ("pgroup", |p| p.pgroup = "ECqPGroup(P-384)::00".into()), + ("rohash", |p| p.rohash = "SHA-512".into()), ]; for (field, mutate) in mutations { @@ -95,8 +98,11 @@ fn prg_is_hash_of_seed_and_counter() { let mut input = seed.to_vec(); input.extend_from_slice(&i.to_be_bytes()); let expected = sha2::Sha256::digest(&input); - assert_eq!(&out[i as usize * 32..(i as usize + 1) * 32], &expected[..], - "PRG block {i}"); + assert_eq!( + &out[i as usize * 32..(i as usize + 1) * 32], + &expected[..], + "PRG block {i}" + ); } } @@ -121,7 +127,11 @@ fn random_oracle_prefixes_the_output_length() { let b = RandomOracle::new(Hashfunction::Sha256, 512).eval(data); assert_eq!(a.len(), 32); assert_eq!(b.len(), 64); - assert_ne!(a[..], b[..32], "differing n_out must reseed the PRG differently"); + assert_ne!( + a[..], + b[..32], + "differing n_out must reseed the PRG differently" + ); } #[test] @@ -147,7 +157,11 @@ fn random_oracle_masks_leading_bits_for_non_multiples_of_eight() { #[test] fn hashfunction_names_round_trip() { - for h in [Hashfunction::Sha256, Hashfunction::Sha384, Hashfunction::Sha512] { + for h in [ + Hashfunction::Sha256, + Hashfunction::Sha384, + Hashfunction::Sha512, + ] { assert_eq!(Hashfunction::from_name(h.name()), Some(h)); assert_eq!(h.outlen() * 8, h.outlen_bits()); } @@ -213,10 +227,7 @@ fn the_proof_transcripts_match_the_installed_verificatum() { vectors.contains_key(name), "vmnv -t should have reported {name} for a mixing session" ); - assert!( - !vectors[name].is_empty(), - "{name} must not be empty" - ); + assert!(!vectors[name].is_empty(), "{name} must not be empty"); } // The values themselves are checked where they are computed -- diff --git a/packages/wbraid/crates/v2v/tests/spec_examples.rs b/packages/wbraid/crates/v2v/tests/spec_examples.rs index e6cbb22a873..fa811b4887f 100644 --- a/packages/wbraid/crates/v2v/tests/spec_examples.rs +++ b/packages/wbraid/crates/v2v/tests/spec_examples.rs @@ -22,9 +22,15 @@ fn hex(bytes: &[u8]) -> String { #[test] fn leaf_and_node_wire_format() { // VMNV §4.2: leaf = 01 || len_4 || data, node = 00 || count_4 || children. - assert_eq!(hex(&ByteTree::leaf(vec![0x01, 0x07]).to_bytes()), "010000000201 07".replace(' ', "")); assert_eq!( - hex(&ByteTree::node(vec![ByteTree::leaf(vec![0xaa]), ByteTree::leaf(vec![0xbb])]).to_bytes()), + hex(&ByteTree::leaf(vec![0x01, 0x07]).to_bytes()), + "010000000201 07".replace(' ', "") + ); + assert_eq!( + hex( + &ByteTree::node(vec![ByteTree::leaf(vec![0xaa]), ByteTree::leaf(vec![0xbb])]) + .to_bytes() + ), "00000000020100000001aa0100000001bb" ); } @@ -57,7 +63,9 @@ fn example_9_and_10_field_elements_are_fixed_width() { let width = arithm::fixed_width_for_modulus_bits(9); assert_eq!(width, 2); assert_eq!( - hex(&arithm::field_element(&[0x01, 0x02], width).unwrap().to_bytes()), + hex(&arithm::field_element(&[0x01, 0x02], width) + .unwrap() + .to_bytes()), "01000000020102" ); assert_eq!( @@ -169,9 +177,17 @@ fn predicted_sizes_match_the_real_corpus() { let m_kw = ByteTree::node(vec![arr.clone(); W]); let ciphertexts = ByteTree::node(vec![m_kw.clone(), m_kw.clone()]); - assert_eq!(ByteTree::node(vec![point(), point()]).serialized_len(), 167, "FullPublicKey.bt"); + assert_eq!( + ByteTree::node(vec![point(), point()]).serialized_len(), + 167, + "FullPublicKey.bt" + ); assert_eq!(ciphertexts.serialized_len(), 3275, "Ciphertexts.bt"); - assert_eq!(m_kw.serialized_len(), 1635, "Plaintexts.bt / DecryptionFactors01.bt"); + assert_eq!( + m_kw.serialized_len(), + 1635, + "Plaintexts.bt / DecryptionFactors01.bt" + ); assert_eq!(arr.serialized_len(), 815, "PermutationCommitment01.bt"); // tau^pos = node(B, A', B', C', D', F') -- VMNV §8.3, and field for field @@ -181,24 +197,24 @@ fn predicted_sizes_match_the_real_corpus() { ByteTree::node(vec![point(); W]), ]); let tau_pos = ByteTree::node(vec![ - arr.clone(), // B - point(), // A' - arr.clone(), // B' - point(), // C' - point(), // D' - f_prime, // F' + arr.clone(), // B + point(), // A' + arr.clone(), // B' + point(), // C' + point(), // D' + f_prime, // F' ]); assert_eq!(tau_pos.serialized_len(), 2217, "PoSCommitment01.bt"); // sigma^pos = node(k_A, k_B, k_C, k_D, k_E, k_F) -- braid's Responses. let scalar_arr = ByteTree::node(vec![scalar(); N]); let sigma_pos = ByteTree::node(vec![ - scalar(), // k_A - scalar_arr.clone(), // k_B - scalar(), // k_C - scalar(), // k_D - scalar_arr, // k_E - ByteTree::node(vec![scalar(); W]), // k_F + scalar(), // k_A + scalar_arr.clone(), // k_B + scalar(), // k_C + scalar(), // k_D + scalar_arr, // k_E + ByteTree::node(vec![scalar(); W]), // k_F ]); assert_eq!(sigma_pos.serialized_len(), 970, "PoSReply01.bt"); } diff --git a/packages/wbraid/crates/v2v/tests/they_verify_ours.rs b/packages/wbraid/crates/v2v/tests/they_verify_ours.rs index f2b654cfd60..e7a3be1cb7e 100644 --- a/packages/wbraid/crates/v2v/tests/they_verify_ours.rs +++ b/packages/wbraid/crates/v2v/tests/they_verify_ours.rs @@ -41,13 +41,11 @@ //! exactly like `-shuffle`, so it is not a safe way to check the mixing phase //! alone. - -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; // Shared with the other interop tests: locating Verificatum, and the random // source it refuses to start without. -#[allow(dead_code)] mod common; // The shipped emitter, so these tests exercise what the tool ships rather @@ -60,7 +58,6 @@ const N: usize = 8; const SID: &str = "braidpoc"; const AUXSID: &str = "default"; - const VERIFY_TOOL: &str = "com.verificatum.protocol.mixnet.MixNetElGamalVerifyFiatShamirTool"; struct Env { @@ -201,7 +198,7 @@ fn vmnv_accepts(env: &Env, dir: &PathBuf) -> bool { /// Rewrite a proof directory to claim the output is the input, i.e. that nothing /// was shuffled. The proof is then invalid for that statement. -fn claim_no_shuffling_happened(dir: &PathBuf) { +fn claim_no_shuffling_happened(dir: &Path) { let input = std::fs::read(dir.join("Ciphertexts.bt")).unwrap(); std::fs::write(dir.join("ShuffledCiphertexts.bt"), &input).unwrap(); std::fs::write(dir.join("proofs/Ciphertexts01.bt"), &input).unwrap(); @@ -445,8 +442,6 @@ fn vmnv_is_silent_about_a_failed_shuffle() { ); } - - /// Assert that `vmnv -mix` verified every phase, and say which one failed if not. fn assert_mix_verified(output: &str, mixers: usize) { assert!( @@ -468,7 +463,6 @@ fn assert_mix_verified(output: &str, mixers: usize) { // Synthesized protocol info files // ------------------------------------------------------------------------- - /// Write a synthesized protocol info file and return its path. fn write_protinfo(info: &ProtocolInfo, name: &str) -> PathBuf { let path = std::env::temp_dir().join(format!("{}_{name}.xml", private_name("protinfo"))); @@ -500,7 +494,10 @@ fn vmnv_accepts_a_synthesized_protocol_info_file() { emit_chain(&dir, 3); let (code, output) = run_vmnv(&env, &dir, true); - assert_eq!(code, 0, "vmnv must accept a synthesized info file:\n{output}"); + assert_eq!( + code, 0, + "vmnv must accept a synthesized info file:\n{output}" + ); assert_eq!( output.matches("Verify proof of shuffle... done.").count(), 3, @@ -532,8 +529,7 @@ fn vmnv_accepts_a_sweep_of_session_shapes() { // lambda_a >= lambda, and the emitter derives the active threshold // from the number of mixers. - let dir = std::env::temp_dir() - .join(format!("braid_vmnv_sweep_{parties}_{threshold}")); + let dir = std::env::temp_dir().join(format!("braid_vmnv_sweep_{parties}_{threshold}")); emit_chain(&dir, threshold); let (code, output) = run_vmnv(&env, &dir, true); diff --git a/packages/wbraid/crates/v2v/tests/vmn_decrypt.rs b/packages/wbraid/crates/v2v/tests/vmn_decrypt.rs index d3bc6ad794d..11264056406 100644 --- a/packages/wbraid/crates/v2v/tests/vmn_decrypt.rs +++ b/packages/wbraid/crates/v2v/tests/vmn_decrypt.rs @@ -11,13 +11,12 @@ //! completely different code path — the recipient's share verification — than //! the commitments the product is taken over. - -use v2v::decrypt; use cryptography::context::{Context, P256Ctx}; use cryptography::dkgd::dealer::Dealer; use cryptography::dkgd::recipient::{ParticipantPosition, Recipient}; use cryptography::groups::p256::element::P256Element; use cryptography::traits::groups::{CryptographicGroup, GroupElement, GroupScalar}; +use v2v::decrypt; const T: usize = 2; const P: usize = 3; @@ -29,7 +28,10 @@ fn run_dkg() -> (Vec>, P256Element) { let dealers: Vec> = (0..P).map(|_| Dealer::generate()).collect(); let all_shares: Vec<_> = dealers .iter() - .map(|d| d.get_verifiable_shares(TEST_DKG_CTX).expect("dealing must succeed")) + .map(|d| { + d.get_verifiable_shares(TEST_DKG_CTX) + .expect("dealing must succeed") + }) .collect(); let commitments: Vec> = all_shares @@ -41,7 +43,7 @@ fn run_dkg() -> (Vec>, P256Element) { use cryptography::dkgd::dealer::VerifiableShare; let shares_for_first: [VerifiableShare; P] = std::array::from_fn(|d| { VerifiableShare::new( - all_shares[d].shares[0].clone(), + all_shares[d].shares[0], all_shares[d].checking_values.clone(), ) }); @@ -90,10 +92,8 @@ fn factor_conversion_is_the_documented_exponent() { let braid_factor: [P256Element; W] = std::array::from_fn(|_| ::G::random_element(&mut rng)); - let converted = decrypt::to_vmn_factor( - &braid_factor, - &decrypt::negated_inverse_alpha(k).unwrap(), - ); + let converted = + decrypt::to_vmn_factor(&braid_factor, &decrypt::negated_inverse_alpha(k).unwrap()); // Undo it: alpha, negated. (x^{-1/a})^{-a} = x. let alpha_scalar = { @@ -120,8 +120,10 @@ fn inactive_factors_are_all_identity() { assert_eq!(factors.len(), 7, "one per ciphertext"); for factor in &factors { for component in factor { - assert!(component.is_identity(), "every component is the group identity"); + assert!( + component.is_identity(), + "every component is the group identity" + ); } } } - diff --git a/packages/wbraid/crates/v2v/tests/vmn_encode.rs b/packages/wbraid/crates/v2v/tests/vmn_encode.rs index d4c1dff4166..9dccc2454bd 100644 --- a/packages/wbraid/crates/v2v/tests/vmn_encode.rs +++ b/packages/wbraid/crates/v2v/tests/vmn_encode.rs @@ -12,16 +12,15 @@ //! Corpus-backed checks run against a generated corpus; the rest are //! self-contained. - mod common; use std::path::PathBuf; -use v2v::encode; use cryptography::context::{Context, P256Ctx}; use cryptography::cryptosystem::elgamal::KeyPair; use cryptography::groups::p256::element::P256Element; use cryptography::traits::groups::{CryptographicGroup, GroupElement}; +use v2v::encode; use v2v::wire::bytetree::ByteTree; /// The reference proof directory: the in-repo corpus by default, overridable @@ -84,7 +83,11 @@ fn ciphertext_arrays_are_transposed_not_listed() { for side in sides { let components = side.as_node_of(W).expect("W component arrays"); for component in components { - assert_eq!(component.as_node().unwrap().len(), N, "N entries per component"); + assert_eq!( + component.as_node().unwrap().len(), + N, + "N entries per component" + ); } } } diff --git a/packages/wbraid/crates/v2v/tests/we_verify_theirs.rs b/packages/wbraid/crates/v2v/tests/we_verify_theirs.rs index 7b07ce43780..0b2591a555e 100644 --- a/packages/wbraid/crates/v2v/tests/we_verify_theirs.rs +++ b/packages/wbraid/crates/v2v/tests/we_verify_theirs.rs @@ -65,7 +65,11 @@ fn generate_and_verify(shape: Shape) -> bool { true } -fn check(nizkp: &std::path::Path, info: &ProtocolInfo, meta: &session::ProofMetadata) { +fn check( + nizkp: &std::path::Path, + info: &ProtocolInfo, + meta: &session::ProofMetadata, +) { let outcome = session::verify_session::(nizkp, info, meta) .expect("the directory must be well formed") .expect("every proof in a generated session must verify"); @@ -158,8 +162,7 @@ fn a_tampered_verificatum_proof_is_rejected() { .expect("parse byte tree") }; let gamma = v2v::encode::tree_to_elements(&read("proofs/PolynomialInExponent.bt")).unwrap(); - let correct = - v2v::wire::arithm::bool_array_values(&read("proofs/CorrectIndices.bt")).unwrap(); + let correct = v2v::wire::arithm::bool_array_values(&read("proofs/CorrectIndices.bt")).unwrap(); let mixed = v2v::encode::tree_to_ciphertexts::<2>(&read(&format!( "proofs/Ciphertexts{:02}.bt", meta.active_threshold @@ -225,7 +228,10 @@ fn an_inactive_party_gets_identity_factors_and_a_zero_reply() { .unwrap(), ) .unwrap(); - assert!(!correct[2], "party 2 sat out, so CorrectIndices must say so"); + assert!( + !correct[2], + "party 2 sat out, so CorrectIndices must say so" + ); let (factors, proof) = read_party::<2>(dir, 2); assert!( @@ -237,7 +243,9 @@ fn an_inactive_party_gets_identity_factors_and_a_zero_reply() { "and its commitment is node(1, 1^omega)" ); assert!( - proof.k_x.equals(&cryptography::groups::p256::scalar::P256Scalar::zero()), + proof + .k_x + .equals(&cryptography::groups::p256::scalar::P256Scalar::zero()), "and its reply is the zero scalar" ); @@ -281,10 +289,8 @@ fn read_party( .unwrap() .try_into() .expect("omega components"), - k_x: v2v::encode::tree_to_scalar(&read(format!( - "proofs/DecrFactReply{party:02}.bt" - ))) - .unwrap(), + k_x: v2v::encode::tree_to_scalar(&read(format!("proofs/DecrFactReply{party:02}.bt"))) + .unwrap(), }, ) } diff --git a/packages/wbraid/crates/vsc/Cargo.toml b/packages/wbraid/crates/vsc/Cargo.toml index 2f0afbe5a38..33088c1af44 100644 --- a/packages/wbraid/crates/vsc/Cargo.toml +++ b/packages/wbraid/crates/vsc/Cargo.toml @@ -68,10 +68,14 @@ custom-warnings = ["custom_warning_macro/on"] serde = ["dep:serde"] long_running_tests = [] wasm = [] +# The benches use the nightly-only libtest harness (#![feature(test)]); this +# feature keeps them out of stable builds of --all-targets. +nightly-benches = [] [[bench]] name = "shuffle" harness = true +required-features = ["nightly-benches"] [dev-dependencies] serde = { version = "1.0.219", features=["derive"] } diff --git a/packages/wbraid/crates/vsc/benches/shuffle.rs b/packages/wbraid/crates/vsc/benches/shuffle.rs index 775654ccd22..ea2c7f052e5 100644 --- a/packages/wbraid/crates/vsc/benches/shuffle.rs +++ b/packages/wbraid/crates/vsc/benches/shuffle.rs @@ -7,13 +7,13 @@ //! This benchmark measures the performance of the Terelius-Wikstrom [`shuffler`][`cryptography::zkp::shuffle::Shuffler`] //! for proof computation and proof verification. The benchmark will print timings for these functions. //! -//! This benchmark can be run with +//! This benchmark requires a nightly toolchain and can be run with //! -//! `cargo bench shuffle` +//! `cargo bench --features nightly-benches shuffle` //! //! You can include the P-256 benchmark with //! -//! `cargo bench shuffle -- --include-ignored` +//! `cargo bench --features nightly-benches shuffle -- --include-ignored` #![feature(test)] diff --git a/packages/wbraid/crates/vsc/macros/canonical_derive/src/lib.rs b/packages/wbraid/crates/vsc/macros/canonical_derive/src/lib.rs index 166d3b11e9b..6908c36c6e4 100644 --- a/packages/wbraid/crates/vsc/macros/canonical_derive/src/lib.rs +++ b/packages/wbraid/crates/vsc/macros/canonical_derive/src/lib.rs @@ -56,7 +56,8 @@ fn impl_canonical(ast: &syn::DeriveInput) -> TokenStream { .collect(); let tys: Vec<_> = fields.named.iter().map(|f| &f.ty).collect(); write_stmts = quote! { #( Serializable::write(&self.#names, out); )* }; - read_ctor = quote! { Self { #( #names: <#tys as Deserializable>::read(input)?, )* } }; + read_ctor = + quote! { Self { #( #names: <#tys as Deserializable>::read(input)?, )* } }; } syn::Fields::Unnamed(fields) => { let indices = (0..fields.unnamed.len()).map(syn::Index::from); diff --git a/packages/wbraid/crates/vsc/src/context.rs b/packages/wbraid/crates/vsc/src/context.rs index 4c8d24622bc..bf3d4be86c3 100644 --- a/packages/wbraid/crates/vsc/src/context.rs +++ b/packages/wbraid/crates/vsc/src/context.rs @@ -17,8 +17,8 @@ use crate::utils::signatures::Ed25519; use crate::utils::signatures::SignatureScheme; /// Global hashing function -/// -/// Used by +/// +/// Used by /// - cryptographic groups for hashing to curve and hashing to scalar pub type CryptographicHasher = crate::utils::hash::Hasher512; @@ -62,14 +62,20 @@ pub type CryptographicHasher = crate::utils::hash::Hasher512; * } * ``` */ -pub trait Context: private::Sealed + std::fmt::Debug + PartialEq + Clone + Send + Sync + 'static +pub trait Context: + private::Sealed + std::fmt::Debug + PartialEq + Clone + Send + Sync + 'static where >::Signer: Send + Sync, >::Verifier: Send + Sync, >::Signature: Send + Sync, { /// The group element type. - type Element: GroupElement + Serializable + Deserializable + Clone + Send + Sync; + type Element: GroupElement + + Serializable + + Deserializable + + Clone + + Send + + Sync; /// The group scalar type. type Scalar: GroupScalar + Serializable + Deserializable + Clone + Send + Sync + From; @@ -141,7 +147,7 @@ where * - The library's default hasher as the hashing function * - `StdRng` as the random number generator. * - `Ed25519` as the digital signature scheme. - * + * * Note: hashing to curve and scalar uses the p256 crate's internal hasher (SHA-256), * not the the hasher specified with `Hasher` (which is the library's default hasher). */ diff --git a/packages/wbraid/crates/vsc/src/dkgd/dealer.rs b/packages/wbraid/crates/vsc/src/dkgd/dealer.rs index b2aa147e575..2112ec5f08e 100644 --- a/packages/wbraid/crates/vsc/src/dkgd/dealer.rs +++ b/packages/wbraid/crates/vsc/src/dkgd/dealer.rs @@ -10,8 +10,8 @@ use crate::context::Context; use crate::dkgd::recipient::ParticipantPosition; use crate::traits::groups::GroupElement; use crate::traits::groups::GroupScalar; -use crate::zkp::schnorr::SchnorrProof; use crate::utils::error::Error; +use crate::zkp::schnorr::SchnorrProof; use canonical_derive::Canonical; /** @@ -168,8 +168,11 @@ impl Dealer { pub(crate) fn get_shares(&self) -> [C::Scalar; P] { array::from_fn(|p| { // p + 1 cannot overflow, P < 100 is compile-time checked - let recipient: u32 = p.checked_add(1).expect("P < 100") - .try_into().expect("P < 100 < u32::MAX"); + let recipient: u32 = p + .checked_add(1) + .expect("P < 100") + .try_into() + .expect("P < 100 < u32::MAX"); let recipient: C::Scalar = recipient.into(); self.polynomial.eval(&recipient) }) @@ -178,18 +181,21 @@ impl Dealer { /// Compute the `T` checking values for this dealer's polynomial, with Schnorr proofs. /// /// See section 2.4: - /// + /// /// "Common mitigations include an initial round during which every trustee /// commits to its Ki,j values before opening them and resuming the protocol, or /// requiring every trustee to provide a Schnorr proof that it knows the discrete - /// logarithms of its Ki,j values w.r.t. g" - /// + /// logarithms of its Ki,j values w.r.t. g" + /// /// Each checking value is computed as `g^polynomial_coefficient`. /// Use [`Self::get_verifiable_shares`] to obtain the shares [along /// with][`DealerShares`] their checking values. - pub(crate) fn get_checking_values_proofs(&self, proof_context: &[u8]) -> Result<[CheckingValue; T], Error> { + pub(crate) fn get_checking_values_proofs( + &self, + proof_context: &[u8], + ) -> Result<[CheckingValue; T], Error> { let g = C::generator(); - let values: [Result, Error>; T] = self.polynomial.0.clone().map(|v| { + let values: [Result, Error>; T] = self.polynomial.0.clone().map(|v| { let value = g.exp(&v); let proof = SchnorrProof::::prove(&g, &value, &v, proof_context); let cv = CheckingValue::new(value, proof?); @@ -198,8 +204,10 @@ impl Dealer { let values: Vec> = values .into_iter() .collect::>, Error>>()?; - let values: [CheckingValue; T] = values.try_into().expect("Vec length matches array length T"); - + let values: [CheckingValue; T] = values + .try_into() + .expect("Vec length matches array length T"); + Ok(values) } } @@ -332,11 +340,11 @@ impl CheckingValue { Self { value, proof } } /// Verify the Schnorr proof of knowledge for this checking value. - /// + /// /// # Errors /// /// - `HashToElementError` if challenge generation returns error - /// + /// /// Returns `true` if the proof is valid, `false` otherwise. pub fn verify(&self, g: &C::Element, proof_context: &[u8]) -> Result { self.proof.verify(g, &self.value, proof_context) diff --git a/packages/wbraid/crates/vsc/src/dkgd/mod.rs b/packages/wbraid/crates/vsc/src/dkgd/mod.rs index c621fcbbf02..44fff59896a 100644 --- a/packages/wbraid/crates/vsc/src/dkgd/mod.rs +++ b/packages/wbraid/crates/vsc/src/dkgd/mod.rs @@ -58,5 +58,8 @@ pub mod recipient; #[cfg(test)] #[cfg_attr(coverage_nightly, coverage(off))] -#[crate::warning("Need more threshold parameter combinations")] +#[cfg_attr( + feature = "custom-warnings", + crate::warning("Need more threshold parameter combinations") +)] mod tests; diff --git a/packages/wbraid/crates/vsc/src/dkgd/recipient.rs b/packages/wbraid/crates/vsc/src/dkgd/recipient.rs index 46077d90c15..ce7b00afdf4 100644 --- a/packages/wbraid/crates/vsc/src/dkgd/recipient.rs +++ b/packages/wbraid/crates/vsc/src/dkgd/recipient.rs @@ -13,8 +13,8 @@ use crate::traits::groups::GroupElement; use crate::traits::groups::GroupScalar; use crate::utils::error::Error; use crate::zkp::dlogeq::DlogEqProof; -use std::array; use canonical_derive::Canonical; +use std::array; /** * A recipient in the Joint-Feldman distributed key generation (DKG) protocol. @@ -135,11 +135,7 @@ impl Recipient { /// /// A `Recipient` is created through the [`from_shares`][`Self::from_shares`] /// function, which verifies the dealings this constructor trusts. - fn new( - position: ParticipantPosition

, - verification_key: C::Element, - sk: C::Scalar, - ) -> Self { + fn new(position: ParticipantPosition

, verification_key: C::Element, sk: C::Scalar) -> Self { #[allow(path_statements)] Self::CHECK; @@ -266,9 +262,8 @@ impl Recipient { // The raw checking values, for the algebraic checks and the // verification-key derivations. - let raw: [[C::Element; T]; P] = array::from_fn(|d| { - array::from_fn(|j| shares[d].checking_values[j].value.clone()) - }); + let raw: [[C::Element; T]; P] = + array::from_fn(|d| array::from_fn(|j| shares[d].checking_values[j].value.clone())); // Round-2 step 2: each share against its dealer's checking values; // accumulate the joint public key and this recipient's secret. @@ -626,7 +621,7 @@ fn batching_exponents( proof_context: &[u8], ) -> Result, Error> { use crate::traits::groups::CryptographicGroup; - use crate::utils::hash::{update_hasher, Hasher}; + use crate::utils::hash::{Hasher, update_hasher}; use crate::utils::serialization::Serializable as _; use sha3::Digest as _; @@ -685,7 +680,10 @@ impl ParticipantPosition

{ /// Panics if the position is not in the range [1, P]. #[must_use] pub fn new(position: u32) -> Self { - #[crate::warning("Possibly avoidable panics")] + #[cfg_attr( + feature = "custom-warnings", + crate::warning("Possibly avoidable panics") + )] assert!(position > 0); assert!(position as usize <= P); @@ -704,7 +702,10 @@ impl ParticipantPosition

{ /// Panics if the position is not in the range [1, P]. #[must_use] pub fn from_usize(position: usize) -> Self { - #[crate::warning("Possibly avoidable panics")] + #[cfg_attr( + feature = "custom-warnings", + crate::warning("Possibly avoidable panics") + )] assert!(position > 0); assert!(position <= P); @@ -745,7 +746,10 @@ pub fn combine( let bases: Vec<[C::Element; W]> = ciphertexts.iter().map(|c| c.u().clone()).collect(); let mut divisors_acc: Vec<[C::Element; W]> = vec![<[C::Element; W]>::one(); ciphertexts.len()]; - #[crate::warning("Ensure that the contributions are from distinct participants.")] + #[cfg_attr( + feature = "custom-warnings", + crate::warning("Ensure that the contributions are from distinct participants.") + )] for contribution in contributions { let factors = &contribution.partial.factors; if factors.len() != ciphertexts.len() { diff --git a/packages/wbraid/crates/vsc/src/dkgd/tests.rs b/packages/wbraid/crates/vsc/src/dkgd/tests.rs index 9a5bd39118d..1c2d8ad10f9 100644 --- a/packages/wbraid/crates/vsc/src/dkgd/tests.rs +++ b/packages/wbraid/crates/vsc/src/dkgd/tests.rs @@ -8,7 +8,7 @@ use crate::cryptosystem::elgamal::{Ciphertext, PublicKey}; use crate::dkgd::dealer::{CheckingValue, Dealer, VerifiableShare}; use crate::dkgd::recipient::combine; use crate::dkgd::recipient::{ - AttributedDecryption, ParticipantPosition, PartialDecryption, Recipient, + AttributedDecryption, PartialDecryption, ParticipantPosition, Recipient, }; use crate::traits::groups::DistGroupOps; use crate::traits::groups::GroupElement; @@ -81,9 +81,11 @@ fn test_dkgd() { let mut recipients: [(Recipient, PublicKey); P] = array::from_fn(|i| { let position = ParticipantPosition::from_usize(i + 1); - let verifiable_shares: [VerifiableShare; P] = dealers - .clone() - .map(|d| d.get_verifiable_shares(DKG_PROOF_CTX).unwrap().for_recipient(&position)); + let verifiable_shares: [VerifiableShare; P] = dealers.clone().map(|d| { + d.get_verifiable_shares(DKG_PROOF_CTX) + .unwrap() + .for_recipient(&position) + }); let (recipient, joint_pk, _vks) = Recipient::from_shares(position, &verifiable_shares, DKG_PROOF_CTX).unwrap(); @@ -140,9 +142,11 @@ fn test_dkgd_all_participants, PublicKey); P] = array::from_fn(|i| { let position = ParticipantPosition::from_usize(i + 1); - let verifiable_shares: [VerifiableShare; P] = dealers - .clone() - .map(|d| d.get_verifiable_shares(DKG_PROOF_CTX).unwrap().for_recipient(&position)); + let verifiable_shares: [VerifiableShare; P] = dealers.clone().map(|d| { + d.get_verifiable_shares(DKG_PROOF_CTX) + .unwrap() + .for_recipient(&position) + }); let (recipient, joint_pk, _vks) = Recipient::from_shares(position, &verifiable_shares, DKG_PROOF_CTX).unwrap(); @@ -177,9 +181,11 @@ fn test_joint_pkey() let recipients: [(Recipient, PublicKey); P] = array::from_fn(|i| { let position = ParticipantPosition::from_usize(i + 1); - let verifiable_shares: [VerifiableShare; P] = dealers - .clone() - .map(|d| d.get_verifiable_shares(DKG_PROOF_CTX).unwrap().for_recipient(&position)); + let verifiable_shares: [VerifiableShare; P] = dealers.clone().map(|d| { + d.get_verifiable_shares(DKG_PROOF_CTX) + .unwrap() + .for_recipient(&position) + }); let (recipient, joint_pk, _vks) = Recipient::from_shares(position, &verifiable_shares, DKG_PROOF_CTX).unwrap(); @@ -287,9 +293,11 @@ fn test_batched_proof_rejects; P] = array::from_fn(|_| Dealer::generate()); let recipients: [(Recipient, PublicKey); P] = array::from_fn(|i| { let position = ParticipantPosition::from_usize(i + 1); - let verifiable_shares: [VerifiableShare; P] = dealers - .clone() - .map(|d| d.get_verifiable_shares(DKG_PROOF_CTX).unwrap().for_recipient(&position)); + let verifiable_shares: [VerifiableShare; P] = dealers.clone().map(|d| { + d.get_verifiable_shares(DKG_PROOF_CTX) + .unwrap() + .for_recipient(&position) + }); let (recipient, joint_pk, _vks) = Recipient::from_shares(position, &verifiable_shares, DKG_PROOF_CTX).unwrap(); (recipient, joint_pk) @@ -376,20 +384,22 @@ fn test_from_shares_rejects() { let dealers: [Dealer; P] = array::from_fn(|_| Dealer::generate()); let position = ParticipantPosition::from_usize(1); - let shares: [VerifiableShare; P] = dealers - .clone() - .map(|d| d.get_verifiable_shares(DKG_PROOF_CTX).unwrap().for_recipient(&position)); + let shares: [VerifiableShare; P] = dealers.clone().map(|d| { + d.get_verifiable_shares(DKG_PROOF_CTX) + .unwrap() + .for_recipient(&position) + }); // Untampered: verifies. - assert!( - Recipient::::from_shares(position.clone(), &shares, DKG_PROOF_CTX).is_ok() - ); + assert!(Recipient::::from_shares(position.clone(), &shares, DKG_PROOF_CTX).is_ok()); // A swapped checking-value proof (valid, but for another dealer's value) // must reject, naming dealer 2. - let mut tampered = dealers - .clone() - .map(|d| d.get_verifiable_shares(DKG_PROOF_CTX).unwrap().for_recipient(&position)); + let mut tampered = dealers.clone().map(|d| { + d.get_verifiable_shares(DKG_PROOF_CTX) + .unwrap() + .for_recipient(&position) + }); let foreign_proof = tampered[0].checking_values[0].proof.clone(); tampered[1].checking_values[0].proof = foreign_proof; match Recipient::::from_shares(position.clone(), &tampered, DKG_PROOF_CTX) { @@ -405,8 +415,11 @@ fn test_from_shares_rejects() { // A tampered share (proofs intact) must reject the algebraic check, // naming dealer 3. - let mut tampered = dealers - .map(|d| d.get_verifiable_shares(DKG_PROOF_CTX).unwrap().for_recipient(&position)); + let mut tampered = dealers.map(|d| { + d.get_verifiable_shares(DKG_PROOF_CTX) + .unwrap() + .for_recipient(&position) + }); tampered[2].value = tampered[2].value.add(&::Scalar::one()); match Recipient::::from_shares(position, &tampered, DKG_PROOF_CTX) { Err(Error::ShareVerificationFailed(msg)) => { @@ -471,9 +484,11 @@ fn test_batched_proof_is_bound_to_its_author() { let dealers: [Dealer; P] = array::from_fn(|_| Dealer::generate()); let recipients: [(Recipient, PublicKey); P] = array::from_fn(|i| { let position = ParticipantPosition::from_usize(i + 1); - let verifiable_shares: [VerifiableShare; P] = dealers - .clone() - .map(|d| d.get_verifiable_shares(DKG_PROOF_CTX).unwrap().for_recipient(&position)); + let verifiable_shares: [VerifiableShare; P] = dealers.clone().map(|d| { + d.get_verifiable_shares(DKG_PROOF_CTX) + .unwrap() + .for_recipient(&position) + }); let (recipient, joint_pk, _vks) = Recipient::from_shares(position, &verifiable_shares, DKG_PROOF_CTX).unwrap(); (recipient, joint_pk) @@ -483,8 +498,14 @@ fn test_batched_proof_is_bound_to_its_author() { let message: [::Element; W] = array::from_fn(|_| RCtx::random_element()); let encrypted = vec![pk.encrypt(&message)]; - let first = recipients[0].0.partial_decrypt(&encrypted, &vec![]).unwrap(); - let second = recipients[1].0.partial_decrypt(&encrypted, &vec![]).unwrap(); + let first = recipients[0] + .0 + .partial_decrypt(&encrypted, &vec![]) + .unwrap(); + let second = recipients[1] + .0 + .partial_decrypt(&encrypted, &vec![]) + .unwrap(); // Party 1's factors, party 2's proof, checked against party 1's key. let forged = PartialDecryption::new(first.factors.clone(), second.proof.clone()); diff --git a/packages/wbraid/crates/vsc/src/groups/p256/element.rs b/packages/wbraid/crates/vsc/src/groups/p256/element.rs index 48ab845a27a..3d7c6a86405 100644 --- a/packages/wbraid/crates/vsc/src/groups/p256/element.rs +++ b/packages/wbraid/crates/vsc/src/groups/p256/element.rs @@ -12,7 +12,7 @@ use core::fmt::Debug; use p256::elliptic_curve::Group; use p256::elliptic_curve::sec1::{FromSec1Point, ToSec1Point}; use p256::elliptic_curve::subtle::CtOption; -use p256::{Sec1Point, ProjectivePoint}; +use p256::{ProjectivePoint, Sec1Point}; /** * A [`GroupElement`] implementation for the P-256 curve. @@ -150,8 +150,9 @@ impl Deserializable for P256Element { "Failed to parse P256 encoded point".to_string(), ) })?; - let point: CtOption = - ProjectivePoint::from_sec1_point(&point).map(P256Element).into(); + let point: CtOption = ProjectivePoint::from_sec1_point(&point) + .map(P256Element) + .into(); if point.is_some().into() { Ok(point.expect("point.is_some() == true")) diff --git a/packages/wbraid/crates/vsc/src/groups/p256/group.rs b/packages/wbraid/crates/vsc/src/groups/p256/group.rs index cc1d9f0d19b..504be28f70f 100644 --- a/packages/wbraid/crates/vsc/src/groups/p256/group.rs +++ b/packages/wbraid/crates/vsc/src/groups/p256/group.rs @@ -12,8 +12,8 @@ use crate::traits::groups::GroupScalar; use p256::NistP256; use p256::ProjectivePoint; -use p256::hash2curve::{ExpandMsgXmd, GroupDigest, hash_to_scalar}; use p256::elliptic_curve::array::sizes::U32; +use p256::hash2curve::{ExpandMsgXmd, GroupDigest, hash_to_scalar}; use crate::utils::error::Error; use crate::utils::rng; @@ -41,8 +41,9 @@ impl CryptographicGroup for P256Group { /// - `HashToScalarError` if `NistP256::hash_to_scalar` returns error #[crate::warning("Panics on empty input")] fn hash_to_scalar(input_slices: &[&[u8]], ds_tags: &[&[u8]]) -> Result { - let ret = hash_to_scalar::, U32>(input_slices, ds_tags) - .map_err(Error::HashToScalarError); + let ret = + hash_to_scalar::, U32>(input_slices, ds_tags) + .map_err(Error::HashToScalarError); Ok(P256Scalar(ret?)) } @@ -96,8 +97,7 @@ impl CryptographicGroup for P256Group { ) -> Result<[u8; O], Error> { let chunks: Result, Error> = element.iter().map(Self::decode_30_bytes).collect(); - let chunks: [[u8; CHUNK_SIZE]; I] = - chunks?.try_into().expect("chunks.len() == I"); + let chunks: [[u8; CHUNK_SIZE]; I] = chunks?.try_into().expect("chunks.len() == I"); Ok(Codec::::join(&chunks)) } @@ -108,14 +108,17 @@ impl CryptographicGroup for P256Group { let ds_tags: &[&[u8]] = &[b"context", b"independent_generators_p256_counter"]; let mut ret = vec![]; - #[crate::warning("The following code is not optimized. Parallelize with rayon")] + #[cfg_attr( + feature = "custom-warnings", + crate::warning("The following code is not optimized. Parallelize with rayon") + )] for i in 0..count { // Cannot use platform dependent type in random oracle let i_u64 = i as u64; let inputs = &[label, &i_u64.to_be_bytes()]; let point = NistP256::hash_from_bytes(inputs, ds_tags) .map_err(|e| Error::HashToElementError(e.to_string())); - + ret.push(P256Element(point?)); } @@ -206,8 +209,7 @@ impl P256Group { for counter in 0..=u8::MAX { x[31] = counter; - let candidate = - p256::AffinePoint::decompress(&x.into(), Choice::from(0)); + let candidate = p256::AffinePoint::decompress(&x.into(), Choice::from(0)); if bool::from(candidate.is_some()) { let affine = candidate.expect("candidate.is_some() == true"); return Ok(P256Element(ProjectivePoint::from(affine))); diff --git a/packages/wbraid/crates/vsc/src/groups/p256/tests.rs b/packages/wbraid/crates/vsc/src/groups/p256/tests.rs index ef63ceb0a90..6f35106d90e 100644 --- a/packages/wbraid/crates/vsc/src/groups/p256/tests.rs +++ b/packages/wbraid/crates/vsc/src/groups/p256/tests.rs @@ -367,10 +367,8 @@ mod encoding { let keypair = KeyPair::::generate(); let scalar = crate::context::P256Ctx::random_scalar(); - let ciphertext = - P256Group::encrypt_scalar(&scalar, &keypair.pkey.y).expect("encrypt"); - let recovered = - P256Group::decrypt_scalar(&ciphertext, &keypair.skey).expect("decrypt"); + let ciphertext = P256Group::encrypt_scalar(&scalar, &keypair.pkey.y).expect("encrypt"); + let recovered = P256Group::decrypt_scalar(&ciphertext, &keypair.skey).expect("decrypt"); assert_eq!(scalar, recovered, "DKG share must survive encryption"); } diff --git a/packages/wbraid/crates/vsc/src/groups/productgroup/tests.rs b/packages/wbraid/crates/vsc/src/groups/productgroup/tests.rs index f224c4d853e..61d297a7ab0 100644 --- a/packages/wbraid/crates/vsc/src/groups/productgroup/tests.rs +++ b/packages/wbraid/crates/vsc/src/groups/productgroup/tests.rs @@ -713,20 +713,20 @@ fn test_multi_exp_override_matches_the_naive_default() { const N: usize = 5; let mut rng = RCtx::get_rng(); - let bases: Vec<::Element> = - (0..N).map(|_| ::Element::random(&mut rng)).collect(); + let bases: Vec<::Element> = (0..N) + .map(|_| ::Element::random(&mut rng)) + .collect(); let refs: Vec<&::Element> = bases.iter().collect(); - let exponents: Vec<::Scalar> = - (0..N).map(|_| ::Scalar::random(&mut rng)).collect(); + let exponents: Vec<::Scalar> = (0..N) + .map(|_| ::Scalar::random(&mut rng)) + .collect(); let specialized = ::Element::multi_exp(&refs, &exponents).unwrap(); - let naive = bases - .iter() - .zip(&exponents) - .fold(::Element::one(), |acc, (base, exponent)| { - acc.mul(&base.exp(exponent)) - }); + let naive = bases.iter().zip(&exponents).fold( + ::Element::one(), + |acc, (base, exponent)| acc.mul(&base.exp(exponent)), + ); assert_eq!(specialized, naive); } diff --git a/packages/wbraid/crates/vsc/src/groups/ristretto255/element.rs b/packages/wbraid/crates/vsc/src/groups/ristretto255/element.rs index 972b6d57b53..917f0b4e140 100644 --- a/packages/wbraid/crates/vsc/src/groups/ristretto255/element.rs +++ b/packages/wbraid/crates/vsc/src/groups/ristretto255/element.rs @@ -78,10 +78,7 @@ impl GroupElement for RistrettoElement { /// implementation which is faster still, but it is only sound for public /// scalars, so adopting it would need a separate method with that /// precondition in its name. - fn multi_exp( - bases: &[&Self], - exponents: &[Self::Scalar], - ) -> Result { + fn multi_exp(bases: &[&Self], exponents: &[Self::Scalar]) -> Result { if bases.len() != exponents.len() { return Err(CryptographyError::MismatchedMultiExpLength( bases.len(), diff --git a/packages/wbraid/crates/vsc/src/groups/ristretto255/group.rs b/packages/wbraid/crates/vsc/src/groups/ristretto255/group.rs index 85cc6e2ee63..a06974cbebd 100644 --- a/packages/wbraid/crates/vsc/src/groups/ristretto255/group.rs +++ b/packages/wbraid/crates/vsc/src/groups/ristretto255/group.rs @@ -111,7 +111,10 @@ impl CryptographicGroup for Ristretto255Group { hasher.update(label); hasher.update(b"independent_generators_ristretto"); - #[crate::warning("The following code is not optimized. Parallelize with rayon")] + #[cfg_attr( + feature = "custom-warnings", + crate::warning("The following code is not optimized. Parallelize with rayon") + )] let ret: Vec = (0..count) .into_par_iter() .map(|i| { @@ -135,16 +138,16 @@ impl CryptographicGroup for Ristretto255Group { /// - `SerializationError` if the ciphertext cannot be serialized fn encrypt_scalar(scalar: &Self::Scalar, public_key: &Self::Element) -> Result, Error> { use crate::context::RistrettoCtx; - use crate::cryptosystem::elgamal::{PublicKey, Ciphertext}; + use crate::cryptosystem::elgamal::{Ciphertext, PublicKey}; use crate::utils::serialization::Serializable; - + // Encode scalar into 2 elements let elements = Self::encode_scalar(scalar)?; - + // Create public key and encrypt let pk = PublicKey::new(*public_key); let ciphertext: Ciphertext = pk.encrypt(&elements); - + // Serialize to bytes Ok(ciphertext.ser()) } @@ -158,21 +161,25 @@ impl CryptographicGroup for Ristretto255Group { fn decrypt_scalar(ciphertext: &[u8], secret_key: &Self::Scalar) -> Result { use crate::context::RistrettoCtx; use crate::cryptosystem::elgamal::{Ciphertext, KeyPair, PublicKey}; - use crate::utils::serialization::Deserializable; use crate::traits::groups::GroupElement; - + use crate::utils::serialization::Deserializable; + // Deserialize ciphertext - let ct: Ciphertext = Ciphertext::deser(ciphertext) - .map_err(|e| Error::DeserializationError(format!("Failed to deserialize ciphertext: {e:?}")))?; - + let ct: Ciphertext = Ciphertext::deser(ciphertext).map_err(|e| { + Error::DeserializationError(format!("Failed to deserialize ciphertext: {e:?}")) + })?; + // Create keypair (we need public key for KeyPair structure) let public_element = Self::generator().exp(secret_key); let pk = PublicKey::new(public_element); - let keypair = KeyPair { skey: *secret_key, pkey: pk }; - + let keypair = KeyPair { + skey: *secret_key, + pkey: pk, + }; + // Decrypt to get elements let elements = keypair.decrypt(&ct); - + // Decode elements back to scalar Self::decode_scalar(&elements) } @@ -286,4 +293,3 @@ const CHUNK_SIZE: usize = 30; /// Byte-array chunking into per-element units, shared with the other backends. type Codec = crate::groups::codec::Codec; - diff --git a/packages/wbraid/crates/vsc/src/lib.rs b/packages/wbraid/crates/vsc/src/lib.rs index 1f6493f21d8..c04ed1fca66 100644 --- a/packages/wbraid/crates/vsc/src/lib.rs +++ b/packages/wbraid/crates/vsc/src/lib.rs @@ -26,16 +26,19 @@ #![allow(dead_code)] // Only necessary for custom_warning_macro -#![feature(stmt_expr_attributes)] +#![cfg_attr(feature = "custom-warnings", feature(stmt_expr_attributes))] // Only necessary for custom_warning_macro -#![feature(proc_macro_hygiene)] +#![cfg_attr(feature = "custom-warnings", feature(proc_macro_hygiene))] #![doc = include_str!("../README.md")] #![cfg_attr(coverage_nightly, feature(coverage_attribute))] /// Defines implementation choices for key cryptographic functionalities. pub mod context; pub mod cryptosystem; -#[crate::warning("This module is not optimized.")] +#[cfg_attr( + feature = "custom-warnings", + crate::warning("This module is not optimized.") +)] pub mod dkgd; pub mod groups; /// Abstractions for curve arithmetic, groups, elements and scalars. @@ -44,8 +47,8 @@ pub mod traits; pub mod utils; pub mod zkp; -pub use custom_warning_macro::warning; pub use canonical_derive::Canonical; +pub use custom_warning_macro::warning; /// Create the `cryptography` alias that points to `crate` /// @@ -59,10 +62,10 @@ pub use canonical_derive::Canonical; extern crate self as cryptography; /// Debug macro that works in both native and WASM contexts. -/// +/// /// In WASM builds (when `wasm` feature + `wasm32` target), uses browser `console.log`. /// In all other cases, uses `info!`. -/// +/// /// # Examples /// ```ignore /// use strand::debug_log; @@ -82,4 +85,4 @@ macro_rules! debug_log { info!($($arg)*); } }; -} \ No newline at end of file +} diff --git a/packages/wbraid/crates/vsc/src/utils/error.rs b/packages/wbraid/crates/vsc/src/utils/error.rs index 83d673c8e6c..07fe596bab6 100644 --- a/packages/wbraid/crates/vsc/src/utils/error.rs +++ b/packages/wbraid/crates/vsc/src/utils/error.rs @@ -95,9 +95,9 @@ pub enum Error { /// Attaches a contextual string to an Error. pub trait ErrorContext { /// Attaches a contextual string to an Error. - /// + /// /// # Errors - /// + /// /// Returns the wrapped error with context if the result is an error. fn with_context(self, context: &str) -> Result; } @@ -105,12 +105,9 @@ impl ErrorContext for Result { /// Attaches a contextual string to an Error. fn with_context(self, context: &str) -> Result { if let Err(e) = self { - Err(Error::WrappedError( - context.to_string(), - Box::new(e), - )) + Err(Error::WrappedError(context.to_string(), Box::new(e))) } else { self } } -} \ No newline at end of file +} diff --git a/packages/wbraid/crates/vsc/src/utils/rng.rs b/packages/wbraid/crates/vsc/src/utils/rng.rs index 9ed4c3faa59..041c6149ed6 100644 --- a/packages/wbraid/crates/vsc/src/utils/rng.rs +++ b/packages/wbraid/crates/vsc/src/utils/rng.rs @@ -14,7 +14,7 @@ pub trait CRng: rand::Rng + rand::CryptoRng {} /** * `ThreadRng` is a cryptographically secure random number generator. - * + * * When compiling to WebAssembly, the underlying [`getrandom`] crate sources entropy from the * browser's Web Crypto API (`crypto.getRandomValues`) via JS interop. */ @@ -43,7 +43,7 @@ use rand::rand_core::UnwrapErr; /** * `SysRng` is a cryptographically secure random number generator. - * + * * When compiling to WebAssembly, the underlying [`getrandom`] crate sources entropy from the * browser's Web Crypto API (`crypto.getRandomValues`) via JS interop. */ @@ -51,17 +51,17 @@ impl CRng for UnwrapErr {} /** * Implements the random number generation [context][`crate::context::Context`] dependency with [`UnwrapErr`] and [`SysRng`]. - * + * * Note that this will panic if the underlying `SysRng` fails to generate random bytes. In practice, `SysRng` * delegates directly to the OS or platform RNG (`getrandom` on Linux, `BCryptGenRandom` on Windows, * `crypto.getRandomValues` in browsers), and failure of these primitives is considered a non-recoverable * system fault. Propagating the error rather than panicking would add complexity to all call sites without * any meaningful recovery path. - * - * If we want to use a fallible RNG in the future, we can change our Rng trait to + * + * If we want to use a fallible RNG in the future, we can change our Rng trait to * be fallible and implement it for `SysRng` directly, without using `UnwrapErr`, for example starting * with the following code: - * + * * `pub trait CTryRng: rand::TryRng + rand::TryCryptoRng {}` */ impl Rng for UnwrapErr { @@ -72,31 +72,31 @@ impl Rng for UnwrapErr { /** * `StdRng` is a cryptographically secure random number generator. - * + * * When compiling to WebAssembly, the underlying [`getrandom`] crate sources entropy from the * browser's Web Crypto API (`crypto.getRandomValues`) via JS interop. */ impl CRng for StdRng {} /* -We cannot implement Rng for StdRng because StdRng::try_from_rng is fallible, and our Rng trait is infallible. +We cannot implement Rng for StdRng because StdRng::try_from_rng is fallible, and our Rng trait is infallible. Additionally, even if constructing StdRng was infallible, it would not an efficient choice, as calls to -Context::get_rng() are very frequent in small functions, and these calls would pay the cost of constructing -a new StdRng instance every time. +Context::get_rng() are very frequent in small functions, and these calls would pay the cost of constructing +a new StdRng instance every time. -For now, we will use ThreadRng as the default rng implementation, which is infallible and cryptographically secure. +For now, we will use ThreadRng as the default rng implementation, which is infallible and cryptographically secure. If we want to use StdRng in the future, we can change our Rng trait to be fallible and use some kind of thread local storage to store the StdRng instance, so that we only pay the cost of constructing it once per thread. Unlike -using `ThreadRng`, this approach would allow us to control the seed of the StdRng instance, which can yield deterministic +using `ThreadRng`, this approach would allow us to control the seed of the StdRng instance, which can yield deterministic behaviour for testing and debugging purposes. - + Implements the random number generation [context][`crate::context::Context`] dependency with [`StdRng`]. impl Rng for StdRng { fn rng() -> StdRng { // rand::rngs::StdRng // FIXME we would have to change our Rng trait to be fallible - // this fallibility is present only on construction, since once StdRng is constructed, it is deterministic and will not fail + // this fallibility is present only on construction, since once StdRng is constructed, it is deterministic and will not fail StdRng::try_from_rng(&mut SysRng).unwrap() } -}*/ \ No newline at end of file +}*/ diff --git a/packages/wbraid/crates/vsc/src/utils/serialization/properties.rs b/packages/wbraid/crates/vsc/src/utils/serialization/properties.rs index 546e46d8a06..4430cd8a033 100644 --- a/packages/wbraid/crates/vsc/src/utils/serialization/properties.rs +++ b/packages/wbraid/crates/vsc/src/utils/serialization/properties.rs @@ -149,14 +149,12 @@ fn vars() -> impl Strategy { } fn sink() -> impl Strategy> { - (prims(), vars(), any::<[u32; 3]>(), inner::()).prop_map(|(prims, vars, arr, inner)| { - Sink { - prims, - vars, - arr, - inner, - phantom: PhantomData, - } + (prims(), vars(), any::<[u32; 3]>(), inner::()).prop_map(|(prims, vars, arr, inner)| Sink { + prims, + vars, + arr, + inner, + phantom: PhantomData, }) } diff --git a/packages/wbraid/crates/vsc/src/utils/signatures.rs b/packages/wbraid/crates/vsc/src/utils/signatures.rs index 4dcee828679..c1b05f48aca 100644 --- a/packages/wbraid/crates/vsc/src/utils/signatures.rs +++ b/packages/wbraid/crates/vsc/src/utils/signatures.rs @@ -42,7 +42,14 @@ pub trait SignatureScheme { /// The signer type, a private key used for signing. type Signer: Signer + Serializable + Deserializable + Clone; /// The verifier type, a public key used to verify signatures. - type Verifier: Verifier + Serializable + Deserializable + Clone + PartialEq + Eq + std::hash::Hash + std::fmt::Debug; + type Verifier: Verifier + + Serializable + + Deserializable + + Clone + + PartialEq + + Eq + + std::hash::Hash + + std::fmt::Debug; /// The signature type, a digital signature on some data. type Signature: Serializable + Deserializable + Clone; @@ -50,45 +57,45 @@ pub trait SignatureScheme { /// /// The corresponding public verification key can be obtained with `signing_key.verifying_key()`. fn gen_signing_key(rng: &mut R) -> Self::Signer; - + /// Gets the verifying key from a signing key. fn verifying_key(signer: &Self::Signer) -> Self::Verifier; - + /// Serializes a verifying key to a base64-encoded string. - /// + /// /// This is useful when reading/writing verification keys to/from configuration files /// in a generic context. - /// + /// /// # Errors - /// + /// /// Returns an error if serialization fails. fn verifier_to_base64_string(verifier: &Self::Verifier) -> Result; - + /// Deserializes a verifying key from a base64-encoded string. /// /// This is the inverse of `verifier_to_base64_string`. - /// + /// /// # Errors - /// + /// /// Returns an error if parsing fails. fn verifier_from_base64_string(s: &str) -> Result; - + /// Serializes a signing key to a base64-encoded string. /// /// This is useful when reading/writing signing keys to/from configuration files /// in a generic context. - /// + /// /// # Errors - /// + /// /// Returns an error if serialization fails. fn signer_to_base64_string(signer: &Self::Signer) -> Result; - + /// Deserializes a signing key from a base64-encoded string. /// /// This is the inverse of `signer_to_base64_string`. - /// + /// /// # Errors - /// + /// /// Returns an error if parsing fails. fn signer_from_base64_string(s: &str) -> Result; } @@ -124,53 +131,54 @@ impl SignatureScheme for Ed25519 { fn gen_signing_key(rng: &mut R) -> ed25519_dalek::SigningKey { Self::Signer::generate(rng) } - + fn verifying_key(signer: &Self::Signer) -> Self::Verifier { signer.verifying_key() } - + fn verifier_to_base64_string(verifier: &Self::Verifier) -> Result { - use base64::{engine::general_purpose, Engine as _}; - + use base64::{Engine as _, engine::general_purpose}; + // Ed25519 public keys are 32 bytes let bytes = verifier.to_bytes(); Ok(general_purpose::STANDARD.encode(bytes)) } - + fn verifier_from_base64_string(s: &str) -> Result { - use base64::{engine::general_purpose, Engine as _}; - - let bytes = general_purpose::STANDARD - .decode(s) - .map_err(|e| CryptoError::DeserializationError(format!("Failed to decode base64: {e:?}")))?; - - let bytes: [u8; 32] = bytes - .try_into() - .map_err(|_| CryptoError::DeserializationError("Invalid key length: expected 32 bytes".to_string()))?; - - VerifyingKey::from_bytes(&bytes) - .map_err(|e| CryptoError::DeserializationError(format!("Failed to parse verifying key: {e:?}"))) + use base64::{Engine as _, engine::general_purpose}; + + let bytes = general_purpose::STANDARD.decode(s).map_err(|e| { + CryptoError::DeserializationError(format!("Failed to decode base64: {e:?}")) + })?; + + let bytes: [u8; 32] = bytes.try_into().map_err(|_| { + CryptoError::DeserializationError("Invalid key length: expected 32 bytes".to_string()) + })?; + + VerifyingKey::from_bytes(&bytes).map_err(|e| { + CryptoError::DeserializationError(format!("Failed to parse verifying key: {e:?}")) + }) } - + fn signer_to_base64_string(signer: &Self::Signer) -> Result { - use base64::{engine::general_purpose, Engine as _}; - + use base64::{Engine as _, engine::general_purpose}; + // Ed25519 secret keys are 32 bytes let bytes = signer.to_bytes(); Ok(general_purpose::STANDARD.encode(bytes)) } - + fn signer_from_base64_string(s: &str) -> Result { - use base64::{engine::general_purpose, Engine as _}; - - let bytes = general_purpose::STANDARD - .decode(s) - .map_err(|e| CryptoError::DeserializationError(format!("Failed to decode base64: {e:?}")))?; - - let bytes: [u8; 32] = bytes - .try_into() - .map_err(|_| CryptoError::DeserializationError("Invalid key length: expected 32 bytes".to_string()))?; - + use base64::{Engine as _, engine::general_purpose}; + + let bytes = general_purpose::STANDARD.decode(s).map_err(|e| { + CryptoError::DeserializationError(format!("Failed to decode base64: {e:?}")) + })?; + + let bytes: [u8; 32] = bytes.try_into().map_err(|_| { + CryptoError::DeserializationError("Invalid key length: expected 32 bytes".to_string()) + })?; + Ok(SigningKey::from_bytes(&bytes)) } } @@ -183,8 +191,9 @@ impl Serializable for SigningKey { impl Deserializable for SigningKey { fn read(input: &mut &[u8]) -> Result { let bytes = take(input, ed25519_dalek::SECRET_KEY_LENGTH)?; - let array: [u8; ed25519_dalek::SECRET_KEY_LENGTH] = - bytes.try_into().expect("take returns exactly the requested bytes"); + let array: [u8; ed25519_dalek::SECRET_KEY_LENGTH] = bytes + .try_into() + .expect("take returns exactly the requested bytes"); Ok(SigningKey::from_bytes(&array)) } } @@ -197,8 +206,9 @@ impl Serializable for VerifyingKey { impl Deserializable for VerifyingKey { fn read(input: &mut &[u8]) -> Result { let bytes = take(input, ed25519_dalek::PUBLIC_KEY_LENGTH)?; - let array: [u8; ed25519_dalek::PUBLIC_KEY_LENGTH] = - bytes.try_into().expect("take returns exactly the requested bytes"); + let array: [u8; ed25519_dalek::PUBLIC_KEY_LENGTH] = bytes + .try_into() + .expect("take returns exactly the requested bytes"); Ok(VerifyingKey::from_bytes(&array)?) } } @@ -211,8 +221,9 @@ impl Serializable for Signature { impl Deserializable for Signature { fn read(input: &mut &[u8]) -> Result { let bytes = take(input, ed25519_dalek::SIGNATURE_LENGTH)?; - let array: [u8; ed25519_dalek::SIGNATURE_LENGTH] = - bytes.try_into().expect("take returns exactly the requested bytes"); + let array: [u8; ed25519_dalek::SIGNATURE_LENGTH] = bytes + .try_into() + .expect("take returns exactly the requested bytes"); Ok(Signature::from_bytes(&array)) } } diff --git a/packages/wbraid/crates/vsc/src/utils/symm.rs b/packages/wbraid/crates/vsc/src/utils/symm.rs index e30d3430cc0..428f5cf4e07 100644 --- a/packages/wbraid/crates/vsc/src/utils/symm.rs +++ b/packages/wbraid/crates/vsc/src/utils/symm.rs @@ -23,13 +23,13 @@ //! ``` use canonical_derive::Canonical; -use chacha20poly1305::{aead::Aead, aead::Generate, aead::KeyInit, ChaCha20Poly1305, Nonce}; use chacha20poly1305::aead::Key; +use chacha20poly1305::{ChaCha20Poly1305, Nonce, aead::Aead, aead::Generate, aead::KeyInit}; use crate::utils::error::Error; /// Symmetric encryption key for ChaCha20-Poly1305 -/// +/// /// Re-export the Array type from chacha20poly1305's dependency pub type SymmetricKey = Key; @@ -54,14 +54,19 @@ impl EncryptionData { } /// Generate a random symmetric encryption key -/// +/// /// From crate doc:"Generate random key using the operating system’s secure RNG." -/// +/// /// # Errors /// /// Returns `Error::EncryptionError` if key generation fails pub fn gen_key() -> Result { - #[crate::warning("We should pass in our single rng entry point, instead of delegating to Key internal generator")] + #[cfg_attr( + feature = "custom-warnings", + crate::warning( + "We should pass in our single rng entry point, instead of delegating to Key internal generator" + ) + )] Ok(Key::::generate()) } @@ -73,7 +78,12 @@ pub fn gen_key() -> Result { pub fn encrypt(key: SymmetricKey, data: &[u8]) -> Result { // https://docs.rs/chacha20poly1305/latest/chacha20poly1305/trait.AeadCore.html#method.generate_nonce // 4,294,967,296 messages with random nonces can be encrypted under a given key - #[crate::warning("We should pass in our single rng entry point, instead of delegating to Nonce internal generator")] + #[cfg_attr( + feature = "custom-warnings", + crate::warning( + "We should pass in our single rng entry point, instead of delegating to Nonce internal generator" + ) + )] let nonce = Nonce::generate(); let cipher = ChaCha20Poly1305::new(&key); let encrypted = cipher @@ -104,8 +114,9 @@ pub fn decrypt(key: &SymmetricKey, ed: &EncryptionData) -> Result, Error /// /// Returns `Error::DeserializationError` if the byte slice is not exactly 32 bytes pub fn sk_from_bytes(bytes: &[u8]) -> Result { - let array: [u8; 32] = bytes.try_into() - .map_err(|_| Error::DeserializationError("Invalid symmetric key length: expected 32 bytes".to_string()))?; + let array: [u8; 32] = bytes.try_into().map_err(|_| { + Error::DeserializationError("Invalid symmetric key length: expected 32 bytes".to_string()) + })?; Ok(array.into()) } diff --git a/packages/wbraid/crates/vsc/src/zkp/shuffle.rs b/packages/wbraid/crates/vsc/src/zkp/shuffle.rs index 7469225cdcd..7e11821f74b 100644 --- a/packages/wbraid/crates/vsc/src/zkp/shuffle.rs +++ b/packages/wbraid/crates/vsc/src/zkp/shuffle.rs @@ -18,9 +18,9 @@ use crate::utils::error::ErrorContext; use crate::utils::hash; use crate::utils::serialization::Serializable; +use canonical_derive::Canonical; use rand::RngExt; use sha3::Digest; -use canonical_derive::Canonical; use rayon::prelude::*; @@ -226,7 +226,10 @@ impl Shuffler { /// /// Returns a tuple of form (commitment exponents, re-encryption exponents) pub(crate) fn gen_private_exponents(size: usize) -> (Vec, Vec<[C::Scalar; W]>) { - #[crate::warning("The following code is not optimized. Parallelize with rayon")] + #[cfg_attr( + feature = "custom-warnings", + crate::warning("The following code is not optimized. Parallelize with rayon") + )] (0..size) .into_par_iter() .map(|_| { @@ -302,7 +305,9 @@ impl Shuffler { return Err(Error::EmptyShuffle); } if ciphertexts.len() != self.h_generators.len() { - return Err(Error::MismatchedShuffleLength).with_context("Mismatched length between ciphertexts and h_generators when mixing"); + return Err(Error::MismatchedShuffleLength).with_context( + "Mismatched length between ciphertexts and h_generators when mixing", + ); } let big_n = ciphertexts.len(); @@ -455,7 +460,10 @@ impl Shuffler { // This means we start the computation at i = 1 (which is i = 2 in EVS) // and our vector d_n has d_n[0] = b_n[0] (d1 = b1 in EVS) let mut d_n = vec![b_n[0].clone()]; - #[crate::warning("Figure out how this skip(1) behaves")] + #[cfg_attr( + feature = "custom-warnings", + crate::warning("Figure out how this skip(1) behaves") + )] for (i, b) in b_n.iter().enumerate().skip(1) { // cannot underflow, skip(1) starts at 1 #[allow(clippy::arithmetic_side_effects)] @@ -531,7 +539,13 @@ impl Shuffler { proof: &ShuffleProof, context: &[u8], ) -> Result { - self.verify_with(ciphertexts, permuted_ciphertexts, proof, context, &NativeChallenges) + self.verify_with( + ciphertexts, + permuted_ciphertexts, + proof, + context, + &NativeChallenges, + ) } /// As [`verify`](Self::verify), but deriving the challenges through @@ -559,19 +573,26 @@ impl Shuffler { return Err(Error::EmptyShuffle); } if ciphertexts.len() != permuted_ciphertexts.len() { - return Err(Error::MismatchedShuffleLength).with_context("Mismatched length between ciphertexts and permuted ciphertexts"); + return Err(Error::MismatchedShuffleLength) + .with_context("Mismatched length between ciphertexts and permuted ciphertexts"); } if ciphertexts.len() != self.h_generators.len() { - return Err(Error::MismatchedShuffleLength).with_context("Mismatched length between ciphertexts and h_generators"); + return Err(Error::MismatchedShuffleLength) + .with_context("Mismatched length between ciphertexts and h_generators"); } if proof.commitments.big_b_n.len() != ciphertexts.len() { - return Err(Error::MismatchedShuffleLength).with_context("Mismatched length between proof commitments big_b_n and ciphertexts"); + return Err(Error::MismatchedShuffleLength).with_context( + "Mismatched length between proof commitments big_b_n and ciphertexts", + ); } if proof.commitments.big_b_prime_n.len() != ciphertexts.len() { - return Err(Error::MismatchedShuffleLength).with_context("Mismatched length between proof commitments big_b_prime_n and ciphertexts"); + return Err(Error::MismatchedShuffleLength).with_context( + "Mismatched length between proof commitments big_b_prime_n and ciphertexts", + ); } if proof.commitments.u_n.len() != ciphertexts.len() { - return Err(Error::MismatchedShuffleLength).with_context("Mismatched length between proof commitments u_n and ciphertexts"); + return Err(Error::MismatchedShuffleLength) + .with_context("Mismatched length between proof commitments u_n and ciphertexts"); } let commitments = &proof.commitments; @@ -691,11 +712,10 @@ impl Shuffler { let w_prime_n = permuted_ciphertexts; let w_prime_n_k_e_n = w_prime_n.par_iter().zip(responses.k_e_n.par_iter()); let w_prime_n_k_e_n = w_prime_n_k_e_n.map(|(w, k)| w.map_ref(|uv| uv.dist_exp(k))); - let w_prime_n_k_e_n_fold = fold_values( - w_prime_n_k_e_n, - <[[C::Element; W]; 2]>::one, - |acc, next| acc.mul(next), - ); + let w_prime_n_k_e_n_fold = + fold_values(w_prime_n_k_e_n, <[[C::Element; W]; 2]>::one, |acc, next| { + acc.mul(next) + }); let one = [g, self.pk.y.clone()].map(|gy| gy.repl_exp(&responses.k_f.neg())); let rhs_5 = one.mul(&w_prime_n_k_e_n_fold); @@ -729,7 +749,10 @@ impl Shuffler { let s_permuted = permutation.apply_inverse(&s_n)?; let r_h_permuted = r_permuted.into_par_iter().zip(h_permuted.into_par_iter()); - #[crate::warning("The following code is not optimized. Parallelize with rayon")] + #[cfg_attr( + feature = "custom-warnings", + crate::warning("The following code is not optimized. Parallelize with rayon") + )] let u_n: Vec = r_h_permuted .into_par_iter() .map(|(r, h)| { @@ -741,7 +764,10 @@ impl Shuffler { let s_w_permuted = w_permuted.into_par_iter().zip(s_permuted.into_par_iter()); - #[crate::warning("The following code is not optimized. Parallelize with rayon")] + #[cfg_attr( + feature = "custom-warnings", + crate::warning("The following code is not optimized. Parallelize with rayon") + )] let w_prime_n: Vec> = s_w_permuted .into_par_iter() .map(|(c, s)| c.re_encrypt(s, &self.pk.y)) @@ -776,7 +802,6 @@ impl Shuffler { b"big_f_prime_n", b"shuffle_challenge_input_v_context", ]; - } /// Fold the values of a parallel iterator into one, combining with `combine` @@ -1153,7 +1178,7 @@ impl Permutation { } /// Shuffle the given integers in place, using `SliceRandom` from the rand crate. - /// + /// /// This function uses the [`SliceRandom`](https://rust-random.github.io/rand/rand/seq/trait.SliceRandom.html#tymethod.shuffle) trait generate the permutation, according /// to which /// @@ -1269,8 +1294,7 @@ mod tests { let expected = values .iter() .fold(Scalar::zero(), |acc, next| acc.add(next)); - let actual = - super::bounded_combine(&values, Scalar::zero, |acc, next| acc.add(next)); + let actual = super::bounded_combine(&values, Scalar::zero, |acc, next| acc.add(next)); assert_eq!(expected, actual, "mismatch at len {len}"); }