diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a333ae54..8e2b23db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: rust: - - 1.83.0 + - 1.85.0 target: - wasm32-unknown-unknown steps: @@ -37,7 +37,7 @@ jobs: strategy: matrix: rust: - - 1.83.0 + - 1.85.0 target: - x86_64-unknown-linux-gnu steps: @@ -80,7 +80,7 @@ jobs: matrix: include: - target: x86_64-unknown-linux-gnu - rust: 1.83.0 # MSRV + rust: 1.85.0 # MSRV steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@v1 @@ -98,7 +98,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.83.0 # MSRV + toolchain: 1.85.0 # MSRV components: clippy override: true profile: minimal diff --git a/Cargo.lock b/Cargo.lock index 7848c368..2b38c4ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -767,7 +767,7 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "manul" version = "0.3.0-dev" -source = "git+https://github.com/entropyxyz/manul.git?rev=d23fcb8cc9e88729a55006874c464d918197a965#d23fcb8cc9e88729a55006874c464d918197a965" +source = "git+https://github.com/entropyxyz/manul.git?rev=4bed1944dc225c51c880330baba74f1e514c9339#4bed1944dc225c51c880330baba74f1e514c9339" dependencies = [ "derive-where", "digest", diff --git a/Cargo.toml b/Cargo.toml index 0392a0df..ce807585 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,8 @@ name = "synedrion" authors = ['Entropy Cryptography '] version = "0.4.0-dev" -edition = "2021" -rust-version = "1.83" +edition = "2024" +rust-version = "1.85" license = "AGPL-3.0-or-later" description = "Threshold signing library based on Canetti-Gennaro-Goldfeder-Makriyannis-Peled '24 scheme" repository = "https://github.com/entropyxyz/synedrion" @@ -11,7 +11,7 @@ readme = "README.md" categories = ["cryptography", "no-std"] [dependencies] -manul = { git = "https://github.com/entropyxyz/manul.git", rev = "d23fcb8cc9e88729a55006874c464d918197a965" } +manul = { git = "https://github.com/entropyxyz/manul.git", rev = "4bed1944dc225c51c880330baba74f1e514c9339" } signature = { version = "2", default-features = false } rand_core = { version = "0.6.4", default-features = false } digest = { version = "0.10", default-features = false, features = ["alloc"] } @@ -44,7 +44,7 @@ sha3 = { version = "0.10", optional = true, default-features = false } dudect-bencher = { version = "0.6", optional = true } [dev-dependencies] -manul = { git = "https://github.com/entropyxyz/manul.git", rev = "d23fcb8cc9e88729a55006874c464d918197a965", features = ["dev"] } +manul = { git = "https://github.com/entropyxyz/manul.git", rev = "4bed1944dc225c51c880330baba74f1e514c9339", features = ["dev"] } serde_assert = "0.8" tokio = { version = "1", features = ["rt", "sync", "time", "macros"] } rand = { version = "0.8", features = ["getrandom"] } diff --git a/benches/bench.rs b/benches/bench.rs index 8c989197..910b31fb 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -1,13 +1,13 @@ use std::collections::BTreeSet; -use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; +use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; use elliptic_curve::FieldBytes; use manul::{ - dev::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, + dev::{BinaryFormat, TestSessionParams, TestSigner, TestVerifier, run_sync}, signature::Keypair, }; use rand_core::{OsRng, RngCore}; -use synedrion::{dev::TestParams, AuxGen, AuxInfo, InteractiveSigning, KeyInit, KeyShare, SchemeParams}; +use synedrion::{AuxGen, AuxInfo, InteractiveSigning, KeyInit, KeyShare, SchemeParams, dev::TestParams}; fn bench_happy_paths(c: &mut Criterion) { let mut group = c.benchmark_group("happy path"); diff --git a/benches/zk_proofs.rs b/benches/zk_proofs.rs index b45ebc69..aeed1330 100644 --- a/benches/zk_proofs.rs +++ b/benches/zk_proofs.rs @@ -1,6 +1,6 @@ use std::sync::LazyLock; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; use rand_core::OsRng; use synedrion::{k256::ProductionParams112, private_benches::zk_proofs::*}; diff --git a/src/curve.rs b/src/curve.rs index e4fef2da..ba471f54 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -10,7 +10,7 @@ mod bip32; pub use ecdsa::RecoverableSignature; -pub(crate) use arithmetic::{chain_curve, secret_split, Point, Scalar}; +pub(crate) use arithmetic::{Point, Scalar, chain_curve, secret_split}; #[cfg(feature = "bip32")] pub use bip32::{DeriveChildKey, PublicTweakable, SecretTweakable}; diff --git a/src/curve/arithmetic.rs b/src/curve/arithmetic.rs index ac8d77cd..4b2477fd 100644 --- a/src/curve/arithmetic.rs +++ b/src/curve/arithmetic.rs @@ -4,15 +4,15 @@ use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign}; use digest::XofReader; use ecdsa::VerifyingKey; use elliptic_curve::{ + Curve, CurveArithmetic, Field, FieldBytes, FieldBytesSize, Group, NonZeroScalar, PrimeField, ScalarPrimitive, bigint::{ArrayEncoding, Concat, NonZero, Split, Zero}, - generic_array::{typenum::marker_traits::Unsigned, GenericArray}, + generic_array::{GenericArray, typenum::marker_traits::Unsigned}, group::Curve as _, ops::Reduce, point::AffineCoordinates, scalar::FromUintUnchecked, sec1::{EncodedPoint, FromEncodedPoint, ModulusSize, ToEncodedPoint}, subtle::{Choice, ConditionallySelectable, CtOption}, - Curve, CurveArithmetic, Field, FieldBytes, FieldBytesSize, Group, NonZeroScalar, PrimeField, ScalarPrimitive, }; use rand_core::CryptoRngCore; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -24,7 +24,7 @@ use ::{ecdsa::SigningKey, elliptic_curve::SecretKey}; use crate::{ params::SchemeParams, - tools::{hashing::Chain, BoxedRng, Secret}, + tools::{Secret, hashing::Chain}, uint::BoxedEncoding, }; @@ -67,12 +67,12 @@ impl Scalar

{ Self(backend_scalar) } - pub fn random(rng: &mut dyn CryptoRngCore) -> Self { - Self(ScalarPrimitive::::random(&mut BoxedRng(rng)).into()) + pub fn random(rng: &mut impl CryptoRngCore) -> Self { + Self(ScalarPrimitive::::random(rng).into()) } - pub fn random_nonzero(rng: &mut dyn CryptoRngCore) -> Self { - Self(*NonZeroScalar::::random(&mut BoxedRng(rng)).as_ref()) + pub fn random_nonzero(rng: &mut impl CryptoRngCore) -> Self { + Self(*NonZeroScalar::::random(rng).as_ref()) } pub fn mul_by_generator(&self) -> Point

{ @@ -156,7 +156,7 @@ impl Secret> { } pub(crate) fn secret_split( - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, scalar: Secret>, num: usize, ) -> Vec>> { @@ -523,7 +523,7 @@ mod test { use rand_chacha::ChaChaRng; use super::Scalar; - use crate::{dev::TestParams, SchemeParams}; + use crate::{SchemeParams, dev::TestParams}; #[test_log::test] fn to_and_from_bytes() { diff --git a/src/curve/bip32.rs b/src/curve/bip32.rs index 8ceafca1..02f2364b 100644 --- a/src/curve/bip32.rs +++ b/src/curve/bip32.rs @@ -2,10 +2,10 @@ use alloc::vec::Vec; use bip32::{ChainCode, DerivationPath, PrivateKeyBytes, PublicKey as _}; use digest::Digest; -use ecdsa::{hazmat::DigestPrimitive, VerifyingKey}; +use ecdsa::{VerifyingKey, hazmat::DigestPrimitive}; use elliptic_curve::{ - sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, Curve, CurveArithmetic, PrimeCurve, + sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, }; /// Used for deriving child keys from a parent type. diff --git a/src/curve/ecdsa.rs b/src/curve/ecdsa.rs index 96472cab..aadcef77 100644 --- a/src/curve/ecdsa.rs +++ b/src/curve/ecdsa.rs @@ -7,9 +7,6 @@ use rand_core::CryptoRngCore; use super::arithmetic::{Point, Scalar}; use crate::SchemeParams; -#[cfg(test)] -use crate::tools::BoxedRng; - /// A wrapper for a signature and public key recovery info. #[derive(Debug, Clone)] pub struct RecoverableSignature { @@ -22,8 +19,8 @@ where P: SchemeParams, { #[cfg(test)] - pub(crate) fn random(rng: &mut dyn CryptoRngCore) -> Option { - let sk = ecdsa::SigningKey::random(&mut BoxedRng(rng)); + pub(crate) fn random(rng: &mut impl CryptoRngCore) -> Option { + let sk = ecdsa::SigningKey::random(rng); let (signature, recovery_id) = sk.sign_recoverable(b"test message").ok()?; Some(Self { signature, recovery_id }) } diff --git a/src/entities/full.rs b/src/entities/full.rs index 7f010eea..388bcfa5 100644 --- a/src/entities/full.rs +++ b/src/entities/full.rs @@ -10,7 +10,7 @@ use manul::{protocol::PartyId, session::LocalError, utils::SerializableMap}; use rand_core::CryptoRngCore; use crate::{ - curve::{secret_split, Point, Scalar}, + curve::{Point, Scalar, secret_split}, paillier::{ PublicKeyPaillier, PublicKeyPaillierWire, RPParams, RPParamsWire, SecretKeyPaillier, SecretKeyPaillierWire, }, @@ -382,7 +382,7 @@ mod tests { use rand_core::OsRng; use super::KeyShare; - use crate::{dev::TestParams, SchemeParams}; + use crate::{SchemeParams, dev::TestParams}; #[test] fn key_share_centralized() { @@ -397,8 +397,10 @@ mod tests { &ids, Some(&sk), ); - assert!(shares - .values() - .all(|share| &share.verifying_key() == sk.verifying_key())); + assert!( + shares + .values() + .all(|share| &share.verifying_key() == sk.verifying_key()) + ); } } diff --git a/src/entities/internal.rs b/src/entities/internal.rs index 3ac2c981..7aad8f97 100644 --- a/src/entities/internal.rs +++ b/src/entities/internal.rs @@ -4,7 +4,7 @@ use manul::protocol::PartyId; use serde::{Deserialize, Serialize}; use crate::{ - params::{chain_scheme_params, SchemeParams}, + params::{SchemeParams, chain_scheme_params}, tools::hashing::{Chain, HashOutput, Hasher}, }; diff --git a/src/entities/threshold.rs b/src/entities/threshold.rs index bf491657..2bd93a32 100644 --- a/src/entities/threshold.rs +++ b/src/entities/threshold.rs @@ -16,13 +16,13 @@ use crate::{ curve::{Point, Scalar}, params::SchemeParams, tools::{ - sss::{interpolation_coeff, shamir_evaluation_points, shamir_join_points, shamir_split, ShareId}, Secret, + sss::{ShareId, interpolation_coeff, shamir_evaluation_points, shamir_join_points, shamir_split}, }, }; #[cfg(feature = "bip32")] -use crate::curve::{apply_tweaks_public, derive_tweaks, DeriveChildKey, PublicTweakable, SecretTweakable}; +use crate::curve::{DeriveChildKey, PublicTweakable, SecretTweakable, apply_tweaks_public, derive_tweaks}; /// A threshold variant of the key share, where any `threshold` shares our of the total number /// is enough to perform signing. diff --git a/src/lib.rs b/src/lib.rs index 83ff5c3a..6e3cbe1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,9 +54,9 @@ pub use entities::{AuxInfo, KeyShare, KeyShareChange, ThresholdKeyShare}; pub use paillier::PaillierParams; pub use params::SchemeParams; pub use protocols::{ - AuxGen, AuxGenAssociatedData, AuxGenProtocol, InteractiveSigning, InteractiveSigningAssociatedData, - InteractiveSigningProtocol, KeyInit, KeyInitAssociatedData, KeyInitProtocol, KeyRefresh, KeyRefreshAssociatedData, - KeyRefreshProtocol, KeyResharing, KeyResharingProtocol, NewHolder, OldHolder, PrehashedMessage, + AuxGen, AuxGenProtocol, AuxGenSharedData, InteractiveSigning, InteractiveSigningProtocol, + InteractiveSigningSharedData, KeyInit, KeyInitProtocol, KeyInitSharedData, KeyRefresh, KeyRefreshProtocol, + KeyRefreshSharedData, KeyResharing, KeyResharingProtocol, NewHolder, OldHolder, PrehashedMessage, }; pub use uint::{BoxedEncoding, Extendable, MulWide}; diff --git a/src/paillier/encryption.rs b/src/paillier/encryption.rs index 70b88f91..9695f6fc 100644 --- a/src/paillier/encryption.rs +++ b/src/paillier/encryption.rs @@ -4,9 +4,9 @@ use core::{ }; use crypto_bigint::{ + Integer, Invert, Monty, modular::Retrieve, subtle::{Choice, ConditionallyNegatable}, - Integer, Invert, Monty, }; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; @@ -48,7 +48,7 @@ impl Randomizer

{ } } - pub fn random(rng: &mut dyn CryptoRngCore, pk: &PublicKeyPaillier

) -> Self { + pub fn random(rng: &mut impl CryptoRngCore, pk: &PublicKeyPaillier

) -> Self { let randomizer = Secret::init_with(|| pk.random_invertible_residue(rng)); Self::new(pk, randomizer) } @@ -197,7 +197,7 @@ impl Ciphertext

{ /// Encrypts the plaintext with a random randomizer. #[cfg(any(test, feature = "private-benches"))] - pub fn new(rng: &mut dyn CryptoRngCore, pk: &PublicKeyPaillier

, plaintext: &SecretSigned) -> Self { + pub fn new(rng: &mut impl CryptoRngCore, pk: &PublicKeyPaillier

, plaintext: &SecretSigned) -> Self { Self::new_with_randomizer(pk, plaintext, &Randomizer::random(rng, pk)) } @@ -368,7 +368,7 @@ where #[cfg(test)] mod tests { - use crypto_bigint::{subtle::ConditionallySelectable, AddMod, Bounded, Integer, NonZero}; + use crypto_bigint::{AddMod, Bounded, Integer, NonZero, subtle::ConditionallySelectable}; use rand_core::OsRng; use zeroize::Zeroize; diff --git a/src/paillier/keys.rs b/src/paillier/keys.rs index e7b2c707..fe84b5b9 100644 --- a/src/paillier/keys.rs +++ b/src/paillier/keys.rs @@ -1,10 +1,10 @@ use core::fmt::Debug; use crypto_bigint::{ - modular::Retrieve, - subtle::{Choice, ConditionallySelectable}, CheckedAdd, CheckedSub, Integer, InvMod, Invert, Monty, NonZero, Odd, PowBoundedExp, ShrVartime, Square, WrappingAdd, + modular::Retrieve, + subtle::{Choice, ConditionallySelectable}, }; use digest::XofReader; use rand_core::CryptoRngCore; @@ -26,13 +26,13 @@ pub(crate) struct SecretKeyPaillierWire { impl SecretKeyPaillierWire

{ #[cfg(test)] - pub fn random_small(rng: &mut dyn CryptoRngCore) -> Self { + pub fn random_small(rng: &mut impl CryptoRngCore) -> Self { Self { primes: SecretPrimesWire::

::random_small_paillier_blum(rng), } } - pub fn random(rng: &mut dyn CryptoRngCore) -> Self { + pub fn random(rng: &mut impl CryptoRngCore) -> Self { Self { primes: SecretPrimesWire::

::random_paillier_blum(rng), } @@ -264,7 +264,7 @@ where /// Returns a random invertible $w ∈ [0, N)$ such that $w$ is not a square modulo $N$, /// where $N$ is the public key /// (or, equivalently, such that the Jacobi symbol $(w|N) = -1$). - pub fn random_nonsquare_residue(&self, rng: &mut dyn CryptoRngCore) -> P::Uint { + pub fn random_nonsquare_residue(&self, rng: &mut impl CryptoRngCore) -> P::Uint { /* (The sampling method and the explanation by Thomas Pornin) @@ -387,7 +387,7 @@ impl PublicKeyPaillier

{ } /// Returns a uniformly chosen number in range $[0, N)$ such that it is invertible modulo $N$, in Montgomery form. - pub fn random_invertible_residue(&self, rng: &mut dyn CryptoRngCore) -> P::Uint { + pub fn random_invertible_residue(&self, rng: &mut impl CryptoRngCore) -> P::Uint { self.modulus.random_invertible_residue(rng) } diff --git a/src/paillier/params.rs b/src/paillier/params.rs index b64e253a..bb40eb25 100644 --- a/src/paillier/params.rs +++ b/src/paillier/params.rs @@ -1,7 +1,7 @@ use crypto_bigint::{ + Bounded, Gcd, Integer, InvMod, Invert, PowBoundedExp, RandomBits, RandomMod, modular::Retrieve, subtle::{ConditionallyNegatable, ConditionallySelectable, ConstantTimeGreater, CtOption}, - Bounded, Gcd, Integer, InvMod, Invert, PowBoundedExp, RandomBits, RandomMod, }; use crypto_primes::RandomPrimeWithRng; use zeroize::Zeroize; diff --git a/src/paillier/ring_pedersen.rs b/src/paillier/ring_pedersen.rs index 66b2a785..f3b495be 100644 --- a/src/paillier/ring_pedersen.rs +++ b/src/paillier/ring_pedersen.rs @@ -1,13 +1,13 @@ /// Implements the Definition 3.3 from the CGGMP'21 paper and related operations. use core::ops::Mul; -use crypto_bigint::{modular::Retrieve, Integer, Monty, NonZero, RandomMod, ShrVartime}; +use crypto_bigint::{Integer, Monty, NonZero, RandomMod, ShrVartime, modular::Retrieve}; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; use super::{ - rsa::{PublicModulus, PublicModulusWire, SecretPrimes, SecretPrimesWire}, PaillierParams, + rsa::{PublicModulus, PublicModulusWire, SecretPrimes, SecretPrimesWire}, }; use crate::{ tools::Secret, @@ -23,7 +23,7 @@ pub(crate) struct RPSecret { impl RPSecret

{ #[cfg(test)] - pub fn random_small(rng: &mut dyn CryptoRngCore) -> Self { + pub fn random_small(rng: &mut impl CryptoRngCore) -> Self { let primes = SecretPrimesWire::

::random_small_safe(rng).into_precomputed(); let bound = NonZero::new(primes.totient().expose_secret().wrapping_shr_vartime(2)) .expect("totient / 4 is still non-zero because p, q >= 5"); @@ -36,7 +36,7 @@ impl RPSecret

{ Self { primes, lambda } } - pub fn random(rng: &mut dyn CryptoRngCore) -> Self { + pub fn random(rng: &mut impl CryptoRngCore) -> Self { let primes = SecretPrimesWire::

::random_safe(rng).into_precomputed(); let bound = Secret::init_with(|| { @@ -56,7 +56,7 @@ impl RPSecret

{ &self.lambda } - pub fn random_residue_mod_totient(&self, rng: &mut dyn CryptoRngCore) -> SecretUnsigned { + pub fn random_residue_mod_totient(&self, rng: &mut impl CryptoRngCore) -> SecretUnsigned { self.primes.random_residue_mod_totient(rng) } @@ -85,17 +85,17 @@ pub(crate) struct RPParams { impl RPParams

{ #[cfg(test)] - pub fn random_small(rng: &mut dyn CryptoRngCore) -> Self { + pub fn random_small(rng: &mut impl CryptoRngCore) -> Self { let secret = RPSecret::random_small(rng); Self::random_with_secret(rng, &secret) } - pub fn random(rng: &mut dyn CryptoRngCore) -> Self { + pub fn random(rng: &mut impl CryptoRngCore) -> Self { let secret = RPSecret::random(rng); Self::random_with_secret(rng, &secret) } - pub fn random_with_secret(rng: &mut dyn CryptoRngCore, secret: &RPSecret

) -> Self { + pub fn random_with_secret(rng: &mut impl CryptoRngCore, secret: &RPSecret

) -> Self { let modulus = secret.primes.modulus_wire().into_precomputed(); let base_randomizer = modulus.random_quadratic_residue(rng); // $t$ diff --git a/src/paillier/rsa.rs b/src/paillier/rsa.rs index ef0e5beb..d5a0050d 100644 --- a/src/paillier/rsa.rs +++ b/src/paillier/rsa.rs @@ -1,7 +1,8 @@ use crypto_bigint::{BitOps, CheckedSub, Integer, Monty, NonZero, Odd, RandomMod, Square}; use crypto_primes::{ + RandomPrimeWithRng, hazmat::{SetBits, SmallPrimesSieveFactory}, - is_prime_with_rng, sieve_and_find, RandomPrimeWithRng, + is_prime_with_rng, sieve_and_find, }; use digest::XofReader; use rand_core::CryptoRngCore; @@ -17,7 +18,7 @@ use crate::{ }; #[cfg(test)] -fn random_small_paillier_blum_prime(rng: &mut dyn CryptoRngCore) -> P::HalfUint { +fn random_small_paillier_blum_prime(rng: &mut impl CryptoRngCore) -> P::HalfUint { loop { let sieve = SmallPrimesSieveFactory::::new(P::PRIME_BITS - 2, SetBits::TwoMsb); let prime:

::HalfUint = @@ -28,7 +29,7 @@ fn random_small_paillier_blum_prime(rng: &mut dyn CryptoRngCo } } -fn random_paillier_blum_prime(rng: &mut dyn CryptoRngCore) -> P::HalfUint { +fn random_paillier_blum_prime(rng: &mut impl CryptoRngCore) -> P::HalfUint { loop { let sieve = SmallPrimesSieveFactory::::new(P::PRIME_BITS, SetBits::TwoMsb); let prime:

::HalfUint = @@ -66,7 +67,7 @@ impl SecretPrimesWire

{ /// Creates smaller than required primes to trigger an error during tests. #[cfg(test)] - pub fn random_small_paillier_blum(rng: &mut dyn CryptoRngCore) -> Self { + pub fn random_small_paillier_blum(rng: &mut impl CryptoRngCore) -> Self { Self::new( Secret::init_with(|| random_small_paillier_blum_prime::

(rng)), Secret::init_with(|| random_small_paillier_blum_prime::

(rng)), @@ -75,7 +76,7 @@ impl SecretPrimesWire

{ /// Creates the primes for a Paillier-Blum modulus, /// that is `p` and `q` are regular primes with an additional condition `p, q mod 3 = 4`. - pub fn random_paillier_blum(rng: &mut dyn CryptoRngCore) -> Self { + pub fn random_paillier_blum(rng: &mut impl CryptoRngCore) -> Self { Self::new( Secret::init_with(|| random_paillier_blum_prime::

(rng)), Secret::init_with(|| random_paillier_blum_prime::

(rng)), @@ -84,7 +85,7 @@ impl SecretPrimesWire

{ /// Creates smaller than required primes to trigger an error during tests. #[cfg(test)] - pub fn random_small_safe(rng: &mut dyn CryptoRngCore) -> Self { + pub fn random_small_safe(rng: &mut impl CryptoRngCore) -> Self { Self::new( Secret::init_with(|| P::HalfUint::generate_safe_prime_with_rng(rng, P::PRIME_BITS - 2)), Secret::init_with(|| P::HalfUint::generate_safe_prime_with_rng(rng, P::PRIME_BITS - 2)), @@ -92,7 +93,7 @@ impl SecretPrimesWire

{ } /// Creates a pair of safe primes. - pub fn random_safe(rng: &mut dyn CryptoRngCore) -> Self { + pub fn random_safe(rng: &mut impl CryptoRngCore) -> Self { Self::new( Secret::init_with(|| P::HalfUint::generate_safe_prime_with_rng(rng, P::PRIME_BITS)), Secret::init_with(|| P::HalfUint::generate_safe_prime_with_rng(rng, P::PRIME_BITS)), @@ -211,7 +212,7 @@ impl SecretPrimes

{ } /// Returns a random in range `[0, \phi(N))`. - pub fn random_residue_mod_totient(&self, rng: &mut dyn CryptoRngCore) -> SecretUnsigned { + pub fn random_residue_mod_totient(&self, rng: &mut impl CryptoRngCore) -> SecretUnsigned { SecretUnsigned::new( Secret::init_with(|| P::Uint::random_mod(rng, self.totient_nonzero().expose_secret())), P::MODULUS_BITS, @@ -293,7 +294,7 @@ impl PublicModulus

{ } /// Returns a uniformly chosen number in range $[0, N)$ such that it is invertible modulo $N$. - pub fn random_invertible_residue(&self, rng: &mut dyn CryptoRngCore) -> P::Uint { + pub fn random_invertible_residue(&self, rng: &mut impl CryptoRngCore) -> P::Uint { let modulus = self.modulus_nonzero(); loop { let r = P::Uint::random_mod(rng, &modulus); @@ -316,7 +317,7 @@ impl PublicModulus

{ } /// Returns a uniformly chosen invertible quadratic residue modulo $N$, in Montgomery form. - pub fn random_quadratic_residue(&self, rng: &mut dyn CryptoRngCore) -> ::Monty { + pub fn random_quadratic_residue(&self, rng: &mut impl CryptoRngCore) -> ::Monty { self.random_invertible_residue(rng) .to_montgomery(&self.monty_params_mod_n) .square() diff --git a/src/params/conversion.rs b/src/params/conversion.rs index 1c095e6d..6a60b258 100644 --- a/src/params/conversion.rs +++ b/src/params/conversion.rs @@ -1,5 +1,5 @@ use crypto_bigint::{BitOps, NonZero, Zero}; -use elliptic_curve::{bigint::Encoding, Curve, CurveArithmetic, PrimeField}; +use elliptic_curve::{Curve, CurveArithmetic, PrimeField, bigint::Encoding}; use secrecy::{ExposeSecret, ExposeSecretMut, SecretBox}; use super::traits::SchemeParams; @@ -80,22 +80,14 @@ pub(crate) fn scalar_from_signed( value: &PublicSigned<::Uint>, ) -> Scalar

{ let abs_value = scalar_from_wide_uint::

(&value.abs().to_wide()); - if value.is_negative() { - -abs_value - } else { - abs_value - } + if value.is_negative() { -abs_value } else { abs_value } } pub(crate) fn scalar_from_wide_signed( value: &PublicSigned<::WideUint>, ) -> Scalar

{ let abs_value = scalar_from_wide_uint::

(&value.abs()); - if value.is_negative() { - -abs_value - } else { - abs_value - } + if value.is_negative() { -abs_value } else { abs_value } } /// Converts a secret-wrapped uint to a secret-wrapped [`Scalar`], reducing the value modulo curve order. diff --git a/src/params/dev.rs b/src/params/dev.rs index 7f88301a..f3e79fb4 100644 --- a/src/params/dev.rs +++ b/src/params/dev.rs @@ -1,6 +1,6 @@ //! Parameters intended for testing, scaled down to small curve orders and integer sizes. -use crypto_bigint::{nlimbs, Uint}; +use crypto_bigint::{Uint, nlimbs}; use serde::{Deserialize, Serialize}; use sha3::Shake256; use tiny_curve::TinyCurve32; diff --git a/src/params/k256.rs b/src/params/k256.rs index 1ef8972b..5be92034 100644 --- a/src/params/k256.rs +++ b/src/params/k256.rs @@ -1,4 +1,4 @@ -//! Scheme parametes for Secp256k1 curve (as implemented by [`k256']). +//! Scheme parametes for Secp256k1 curve (as implemented by [`k256`]). use core::fmt::Debug; @@ -6,7 +6,7 @@ use core::fmt::Debug; // and `k256` depends on the released one. // So as long as that is the case, `k256` `Uint` is separate // from the one used throughout the crate. -use crypto_bigint::{nlimbs, Uint}; +use crypto_bigint::{Uint, nlimbs}; use serde::{Deserialize, Serialize}; use sha3::Shake256; diff --git a/src/params/traits.rs b/src/params/traits.rs index 14ba53f9..76f2640b 100644 --- a/src/params/traits.rs +++ b/src/params/traits.rs @@ -4,21 +4,21 @@ use core::{fmt::Debug, ops::Add}; // and `k256` depends on the released one. // So as long as that is the case, `k256` `Uint` is separate // from the one used throughout the crate. -use crypto_bigint::{subtle::ConditionallySelectable, Bounded, Integer, PowBoundedExp, RandomMod}; +use crypto_bigint::{Bounded, Integer, PowBoundedExp, RandomMod, subtle::ConditionallySelectable}; use digest::{ExtendableOutput, Update}; use ecdsa::hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}; use elliptic_curve::{ + Curve, CurveArithmetic, PrimeCurve, PrimeField, bigint::{self as bigintv05, Concat, Split}, generic_array::ArrayLength, point::DecompressPoint, sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, - Curve, CurveArithmetic, PrimeCurve, PrimeField, }; use zeroize::Zeroize; use crate::{ curve::chain_curve, - paillier::{chain_paillier_params, PaillierParams}, + paillier::{PaillierParams, chain_paillier_params}, tools::hashing::Chain, uint::{BoxedEncoding, Extendable}, }; @@ -56,9 +56,9 @@ where const EPS_BOUND: u32 = Self::Paillier::PRIME_BITS - 2 * Self::L_BOUND; // $\eps$ /// The parameters of the Paillier encryption. type Paillier: PaillierParams< - WideUint: Extendable, - Uint: Integer>, - >; + WideUint: Extendable, + Uint: Integer>, + >; /// An integer that fits the squared RSA modulus times a small factor. /// Used in some ZK proofs. diff --git a/src/private_benches/paillier.rs b/src/private_benches/paillier.rs index da4d558c..d28caeb9 100644 --- a/src/private_benches/paillier.rs +++ b/src/private_benches/paillier.rs @@ -3,7 +3,7 @@ use crate::{ params::SchemeParams, uint::SecretSigned, }; -use criterion::{black_box, BatchSize, Bencher, Criterion}; +use criterion::{BatchSize, Bencher, Criterion, black_box}; use rand::SeedableRng; type Params = crate::k256::ProductionParams112; diff --git a/src/private_benches/secret_is_ct.rs b/src/private_benches/secret_is_ct.rs index a5a10eec..f305b09c 100644 --- a/src/private_benches/secret_is_ct.rs +++ b/src/private_benches/secret_is_ct.rs @@ -1,6 +1,6 @@ use core::ops::AddAssign; -use crypto_bigint::{NonZero, Random, RandomMod, WrappingAdd, U4096}; +use crypto_bigint::{NonZero, Random, RandomMod, U4096, WrappingAdd}; use dudect_bencher::{BenchRng, Class, CtRunner}; use rand::Rng; diff --git a/src/private_benches/zk_proofs.rs b/src/private_benches/zk_proofs.rs index bb53bf1f..7ee6b1e3 100644 --- a/src/private_benches/zk_proofs.rs +++ b/src/private_benches/zk_proofs.rs @@ -6,7 +6,7 @@ use crate::{ curve::Scalar, k256::ProductionParams112, paillier::{Ciphertext, PaillierParams, RPParams, RPSecret, Randomizer, SecretKeyPaillier, SecretKeyPaillierWire}, - params::{secret_scalar_from_signed, SchemeParams}, + params::{SchemeParams, secret_scalar_from_signed}, tools::Secret, uint::SecretSigned, zk::{ diff --git a/src/private_benches/zk_proofs_ct.rs b/src/private_benches/zk_proofs_ct.rs index a1f8785a..d32e5284 100644 --- a/src/private_benches/zk_proofs_ct.rs +++ b/src/private_benches/zk_proofs_ct.rs @@ -5,11 +5,11 @@ use rand::Rng; use rand_core::CryptoRngCore; use crate::{ + Extendable, MulWide, SchemeParams, k256::{PaillierProduction112, ProductionParams112}, paillier::{PaillierParams, RPParams, SecretKeyPaillier, SecretKeyPaillierWire}, tools::Secret, uint::{PublicSigned, SecretSigned}, - Extendable, MulWide, SchemeParams, }; type Pai = PaillierProduction112; diff --git a/src/protocols.rs b/src/protocols.rs index 92c5664b..46f07b89 100644 --- a/src/protocols.rs +++ b/src/protocols.rs @@ -16,10 +16,10 @@ mod key_resharing; #[cfg(test)] mod misbehavior_tests; -pub use aux_gen::{AuxGen, AuxGenAssociatedData, AuxGenProtocol}; +pub use aux_gen::{AuxGen, AuxGenProtocol, AuxGenSharedData}; pub use interactive_signing::{ - InteractiveSigning, InteractiveSigningAssociatedData, InteractiveSigningProtocol, PrehashedMessage, + InteractiveSigning, InteractiveSigningProtocol, InteractiveSigningSharedData, PrehashedMessage, }; -pub use key_init::{KeyInit, KeyInitAssociatedData, KeyInitProtocol}; -pub use key_refresh::{KeyRefresh, KeyRefreshAssociatedData, KeyRefreshProtocol}; +pub use key_init::{KeyInit, KeyInitProtocol, KeyInitSharedData}; +pub use key_refresh::{KeyRefresh, KeyRefreshProtocol, KeyRefreshSharedData}; pub use key_resharing::{KeyResharing, KeyResharingProtocol, NewHolder, OldHolder}; diff --git a/src/protocols/aux_gen.rs b/src/protocols/aux_gen.rs index b2c75f6f..d3c9cffe 100644 --- a/src/protocols/aux_gen.rs +++ b/src/protocols/aux_gen.rs @@ -3,20 +3,19 @@ //! This is a subset of the protocol that generates the auxiliary data, with share update bits removed. use alloc::{ - boxed::Box, collections::{BTreeMap, BTreeSet}, + string::String, }; -use core::{ - fmt::{self, Debug, Display}, - marker::PhantomData, -}; +use core::{fmt::Debug, marker::PhantomData}; use crypto_bigint::BitOps; -use manul::protocol::{ - Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EntryPoint, FinalizeOutcome, - LocalError, MessageValidationError, NormalBroadcast, PartyId, Payload, Protocol, ProtocolError, ProtocolMessage, - ProtocolMessagePart, ProtocolValidationError, ReceiveError, RequiredMessageParts, RequiredMessages, Round, RoundId, - TransitionInfo, +use manul::{ + protocol::{ + BoxedRound, CommunicationInfo, EntryPoint, EvidenceError, EvidenceMessages, FinalizeOutcome, LocalError, + NoArtifact, NoMessage, NoProtocolErrors, PartyId, Protocol, ProtocolError, ProtocolMessage, ReceiveError, + RequiredMessageParts, RequiredMessages, Round, RoundId, RoundInfo, TransitionInfo, + }, + utils::{GetOrInvalidEvidence, GetOrLocalError, MapValues, Without, verify_that}, }; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; @@ -31,7 +30,6 @@ use crate::{ tools::{ bitvec::BitVec, hashing::{Chain, HashOutput, Hasher}, - protocol_shortcuts::{verify_that, DeserializeAll, DowncastMap, GetRound, MapValues, SafeGet, Without}, }, zk::{FacProof, ModProof, PrmProof}, }; @@ -42,176 +40,87 @@ pub struct AuxGenProtocol(PhantomData<(P, Id)>); impl Protocol for AuxGenProtocol { type Result = AuxInfo; - type ProtocolError = AuxGenError; - - fn verify_direct_message_is_invalid( - format: &BoxedFormat, - round_id: &RoundId, - message: &DirectMessage, - ) -> Result<(), MessageValidationError> { + type SharedData = AuxGenSharedData; + fn round_info(round_id: &RoundId) -> Option> { match round_id { - r if r == &1 => message.verify_is_some(), - r if r == &2 => message.verify_is_some(), - r if r == &3 => message.verify_is_not::>(format), - _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), - } - } - - fn verify_echo_broadcast_is_invalid( - format: &BoxedFormat, - round_id: &RoundId, - message: &EchoBroadcast, - ) -> Result<(), MessageValidationError> { - match round_id { - r if r == &1 => message.verify_is_not::(format), - r if r == &2 => message.verify_is_not::>(format), - r if r == &3 => message.verify_is_some(), - _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), - } - } - - fn verify_normal_broadcast_is_invalid( - format: &BoxedFormat, - round_id: &RoundId, - message: &NormalBroadcast, - ) -> Result<(), MessageValidationError> { - match round_id { - r if r == &1 => message.verify_is_some(), - r if r == &2 => message.verify_is_not::>(format), - r if r == &3 => message.verify_is_not::>(format), - _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), + _ if round_id == 1 => Some(RoundInfo::new::>()), + _ if round_id == 2 => Some(RoundInfo::new::>()), + _ if round_id == 3 => Some(RoundInfo::new::>()), + _ => None, } } } -/// Provable AuxGen faults. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct AuxGenError { - error: Error, - phantom: PhantomData

, +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub(super) struct R2Error

{ + error: R2ErrorEnum, + phantom: PhantomData P>, } -impl From> for AuxGenError { - fn from(source: Error) -> Self { +impl

From for R2Error

{ + fn from(error: R2ErrorEnum) -> Self { Self { - error: source, + error, phantom: PhantomData, } } } -impl Display for AuxGenError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - f, - "{}", - match self.error { - Error::R2HashMismatch => "Round 2: the previously sent hash does not match the public data.", - Error::R2PaillierModulusTooSmall => "Round 2: Paillier modulus is too small.", - Error::R2RPModulusTooSmall => "Round 2: ring-Pedersent modulus is too small.", - Error::R2PrmFailed => "Round 2: `П^{prm}` verification failed.", - Error::R3ModFailed => "Round 3: `П^{mod}` verification failed.", - Error::R3FacFailed { .. } => "Round 3: `П^{fac}` verification failed.", - } - ) - } +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +enum R2ErrorEnum { + HashMismatch, + PaillierModulusTooSmall, + RPModulusTooSmall, + PrmFailed, } -/// AuxGen error -#[derive(Debug, Clone, Serialize, Deserialize)] -enum Error { - R2HashMismatch, - R2PaillierModulusTooSmall, - R2RPModulusTooSmall, - R2PrmFailed, - R3ModFailed, - R3FacFailed { - /// The index $i$ of the node that produced the evidence. - reported_by: Id, - }, -} +impl ProtocolError for R2Error

{ + type Round = Round2; -/// Reconstruct `rid` from echoed messages -fn reconstruct_rid( - format: &BoxedFormat, - previous_messages: &BTreeMap, - combined_echos: &BTreeMap>, -) -> Result { - let r2_ebs = combined_echos - .get_round(2)? - .deserialize_all::>(format)?; - let r2_eb = previous_messages - .get_round(2)? - .echo_broadcast - .deserialize::>(format)?; - let mut rid_combined = r2_eb.rid; - for message in r2_ebs.values() { - rid_combined ^= &message.rid; + fn description(&self) -> String { + match self.error { + R2ErrorEnum::HashMismatch => "The previously sent hash does not match the public data.", + R2ErrorEnum::PaillierModulusTooSmall => "Paillier modulus is too small.", + R2ErrorEnum::RPModulusTooSmall => "Ring-Pedersen modulus is too small.", + R2ErrorEnum::PrmFailed => "`П^{prm}` verification failed.", + } + .into() } - Ok(rid_combined) -} -/// Associated data for AuxGen protocol. -#[derive(Debug, Clone)] -pub struct AuxGenAssociatedData { - /// IDs of all participating nodes. - pub ids: BTreeSet, -} - -impl ProtocolError for AuxGenError { - type AssociatedData = AuxGenAssociatedData; - - fn required_messages(&self) -> RequiredMessages { + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { match self.error { - Error::R2HashMismatch => RequiredMessages::new( + R2ErrorEnum::HashMismatch => RequiredMessages::new( RequiredMessageParts::normal_broadcast().and_echo_broadcast(), Some([(1.into(), RequiredMessageParts::echo_broadcast())].into()), None, ), - Error::R2PaillierModulusTooSmall => { + R2ErrorEnum::PaillierModulusTooSmall => { RequiredMessages::new(RequiredMessageParts::normal_broadcast(), None, None) } - Error::R2RPModulusTooSmall => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), - Error::R2PrmFailed => RequiredMessages::new( + R2ErrorEnum::RPModulusTooSmall => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), + R2ErrorEnum::PrmFailed => RequiredMessages::new( RequiredMessageParts::echo_broadcast().and_normal_broadcast(), None, None, ), - Error::R3ModFailed => RequiredMessages::new( - RequiredMessageParts::normal_broadcast(), - Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), - Some([2.into()].into()), - ), - Error::R3FacFailed { .. } => RequiredMessages::new( - RequiredMessageParts::direct_message(), - Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), - Some([2.into()].into()), - ), } } - fn verify_messages_constitute_error( + fn verify_evidence( &self, - format: &BoxedFormat, + _round_id: &RoundId, guilty_party: &Id, shared_randomness: &[u8], - associated_data: &Self::AssociatedData, - message: ProtocolMessage, - previous_messages: BTreeMap, - combined_echos: BTreeMap>, - ) -> Result<(), ProtocolValidationError> { - let sid = Sid::new::(shared_randomness, &associated_data.ids); + shared_data: &<>::Protocol as Protocol>::SharedData, + messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + let sid = Sid::new::(shared_randomness, &shared_data.ids); match &self.error { - Error::R2HashMismatch => { - let r1_eb = previous_messages - .get_round(1)? - .echo_broadcast - .deserialize::(format)?; - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; - let r2_eb = message.echo_broadcast.deserialize::>(format)?; + R2ErrorEnum::HashMismatch => { + let r1_eb = messages.previous_echo_broadcast::>(1)?; + let r2_nb = messages.normal_broadcast()?; + let r2_eb = messages.echo_broadcast()?; let data = PublicData { paillier_pk: r2_nb.paillier_pk.into_precomputed(), @@ -222,56 +131,111 @@ impl ProtocolError for AuxGenError { }; verify_that(data.hash(&sid, guilty_party) != r1_eb.cap_v) } - Error::R2PaillierModulusTooSmall => { - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; + R2ErrorEnum::PaillierModulusTooSmall => { + let r2_nb = messages.normal_broadcast()?; verify_that( r2_nb.paillier_pk.modulus().bits_vartime() < ::MODULUS_BITS - 2, ) } - Error::R2RPModulusTooSmall => { - let r2_eb = message.echo_broadcast.deserialize::>(format)?; + R2ErrorEnum::RPModulusTooSmall => { + let r2_eb = messages.echo_broadcast()?; verify_that( r2_eb.rp_params.modulus().bits_vartime() < ::MODULUS_BITS - 2, ) } - Error::R2PrmFailed => { - let r2_eb = message.echo_broadcast.deserialize::>(format)?; - let r2_bc = message - .normal_broadcast - .deserialize::>(format)?; + R2ErrorEnum::PrmFailed => { + let r2_eb = messages.echo_broadcast()?; + let r2_nb = messages.normal_broadcast()?; let aux = (&sid, guilty_party); let rp_params = r2_eb.rp_params.to_precomputed(); - verify_that(!r2_bc.psi.verify(&rp_params, &aux)) + verify_that(!r2_nb.psi.verify(&rp_params, &aux)) } - Error::R3ModFailed => { - let rid = reconstruct_rid::(format, &previous_messages, &combined_echos)?; + } + } +} + +#[derive(Debug, Clone, Copy)] +#[derive_where::derive_where(Serialize, Deserialize)] +pub(super) struct R3Error { + error: R3ErrorEnum, + phantom: PhantomData P>, +} + +impl From> for R3Error { + fn from(error: R3ErrorEnum) -> Self { + Self { + error, + phantom: PhantomData, + } + } +} + +#[derive(Debug, Clone, Copy)] +#[derive_where::derive_where(Serialize, Deserialize)] +enum R3ErrorEnum { + ModFailed, + FacFailed { + /// The index $i$ of the node that produced the evidence. + reported_by: Id, + }, +} + +impl ProtocolError for R3Error { + type Round = Round3; + + fn description(&self) -> String { + match self.error { + R3ErrorEnum::ModFailed => "`П^{mod}` verification failed.", + R3ErrorEnum::FacFailed { .. } => "`П^{fac}` verification failed.", + } + .into() + } + + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { + match self.error { + R3ErrorEnum::ModFailed => RequiredMessages::new( + RequiredMessageParts::normal_broadcast(), + Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), + Some([2.into()].into()), + ), + R3ErrorEnum::FacFailed { .. } => RequiredMessages::new( + RequiredMessageParts::direct_message(), + Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), + Some([2.into()].into()), + ), + } + } + + fn verify_evidence( + &self, + _round_id: &RoundId, + guilty_party: &Id, + shared_randomness: &[u8], + shared_data: &<>::Protocol as Protocol>::SharedData, + messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + let sid = Sid::new::(shared_randomness, &shared_data.ids); + + match &self.error { + R3ErrorEnum::ModFailed => { + let rid = reconstruct_rid::(&messages)?; let aux = (&sid, guilty_party, &rid); - let r2_bc = previous_messages - .get_round(2)? - .normal_broadcast - .deserialize::>(format)?; - let r3_bc = message - .normal_broadcast - .deserialize::>(format)?; - let paillier_pk = r2_bc.paillier_pk.into_precomputed(); - verify_that(!r3_bc.psi_prime.verify(&paillier_pk, &aux)) + let r2_nb = messages.previous_normal_broadcast::>(2)?; + let r3_nb = messages.normal_broadcast()?; + let paillier_pk = r2_nb.paillier_pk.into_precomputed(); + verify_that(!r3_nb.psi_prime.verify(&paillier_pk, &aux)) } - Error::R3FacFailed { reported_by } => { - let rid = reconstruct_rid::(format, &previous_messages, &combined_echos)?; + R3ErrorEnum::FacFailed { reported_by } => { + let rid = reconstruct_rid::(&messages)?; let aux = (&sid, guilty_party, &rid); - let r2_eb = combined_echos - .get_round(2)? - .try_get("combined echos for Round 2", reported_by)? - .deserialize::>(format)?; - let r2_bc = previous_messages - .get_round(2)? - .normal_broadcast - .deserialize::>(format)?; - let r3_dm = message.direct_message.deserialize::>(format)?; - let paillier_pk = r2_bc.paillier_pk.into_precomputed(); + let r2_eb = messages + .combined_echos::>(2)? + .get_or_invalid_evidence("combined echos for Round 2", reported_by)? + .clone(); + let r2_nb = messages.previous_normal_broadcast::>(2)?; + let r3_dm = messages.direct_message()?; + let paillier_pk = r2_nb.paillier_pk.into_precomputed(); let rp_params = r2_eb.rp_params.to_precomputed(); verify_that(!r3_dm.psi.verify(&paillier_pk, &rp_params, &aux)) } @@ -279,6 +243,26 @@ impl ProtocolError for AuxGenError { } } +/// Reconstruct `rid` from echoed messages +fn reconstruct_rid( + messages: &EvidenceMessages<'_, Id, Round3>, +) -> Result { + let r2_ebs = messages.combined_echos::>(2)?; + let r2_eb = messages.previous_echo_broadcast::>(2)?; + let mut rid_combined = r2_eb.rid; + for message in r2_ebs.values() { + rid_combined ^= &message.rid; + } + Ok(rid_combined) +} + +/// Associated data for AuxGen protocol. +#[derive(Debug, Clone)] +pub struct AuxGenSharedData { + /// IDs of all participating nodes. + pub ids: BTreeSet, +} + #[derive(Debug, Clone)] pub(super) struct PublicData { pub(super) paillier_pk: PublicKeyPaillier, // $N_i$ @@ -333,7 +317,7 @@ where fn make_round( self, - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, shared_randomness: &[u8], id: &Id, ) -> Result, LocalError> { @@ -380,7 +364,7 @@ where let round = Round1 { context, public_data }; - Ok(BoxedRound::new_dynamic(round)) + Ok(BoxedRound::new(round)) } } @@ -404,13 +388,22 @@ pub(super) struct Round1EchoBroadcast { pub(super) cap_v: HashOutput, } -struct Round1Payload { +pub(super) struct Round1Payload { cap_v: HashOutput, } impl Round for Round1 { type Protocol = AuxGenProtocol; + type DirectMessage = NoMessage; + type NormalBroadcast = NoMessage; + type EchoBroadcast = Round1EchoBroadcast; + + type Payload = Round1Payload; + type Artifact = NoArtifact; + + type ProtocolError = NoProtocolErrors; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear(1) } @@ -419,51 +412,40 @@ impl Round for Round1 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_echo_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - let message = Round1EchoBroadcast { + fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round1EchoBroadcast { cap_v: self.public_data.hash(&self.context.sid, &self.context.my_id), - }; - EchoBroadcast::new(format, message) + }) } fn receive_message( &self, - format: &BoxedFormat, _from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.normal_broadcast.assert_is_none()?; - message.direct_message.assert_is_none()?; - let echo_broadcast = message.echo_broadcast.deserialize::(format)?; - let payload = Round1Payload { - cap_v: echo_broadcast.cap_v, - }; - Ok(Payload::new(payload)) + message: ProtocolMessage, + ) -> Result> { + Ok(Round1Payload { + cap_v: message.echo_broadcast.cap_v, + }) } fn finalize( - self: Box, - _rng: &mut dyn CryptoRngCore, - payloads: BTreeMap, - _artifacts: BTreeMap, + self, + _rng: &mut impl CryptoRngCore, + payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { - let payloads = payloads.downcast_all::()?; let cap_vs = payloads.map_values(|payload| payload.cap_v); let next_round = Round2 { context: self.context, public_data: self.public_data, cap_vs, }; - Ok(FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(next_round))) + Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(next_round))) } } #[derive(Debug)] -struct Round2 { +pub(super) struct Round2 { context: Context, public_data: PublicData

, cap_vs: BTreeMap, @@ -484,7 +466,7 @@ pub(super) struct Round2EchoBroadcast { } #[derive(Debug)] -struct Round2Payload { +pub(super) struct Round2Payload { paillier_pk: PublicKeyPaillier, // $N_i$ rp_params: RPParams, // $\hat{N}_i$, $s_i$, and $t_i$ rid: BitVec, @@ -493,6 +475,15 @@ struct Round2Payload { impl Round for Round2 { type Protocol = AuxGenProtocol; + type DirectMessage = NoMessage; + type NormalBroadcast = Round2NormalBroadcast

; + type EchoBroadcast = Round2EchoBroadcast

; + + type Payload = Round2Payload

; + type Artifact = NoArtifact; + + type ProtocolError = R2Error

; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear(2) } @@ -501,42 +492,28 @@ impl Round for Round2 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_normal_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - let message = Round2NormalBroadcast { + fn make_normal_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round2NormalBroadcast { paillier_pk: self.public_data.paillier_pk.clone().into_wire(), psi: self.public_data.psi.clone(), u: self.public_data.u.clone(), - }; - NormalBroadcast::new(format, message) + }) } - fn make_echo_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - let message = Round2EchoBroadcast::

{ + fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round2EchoBroadcast::

{ rid: self.public_data.rid.clone(), rp_params: self.public_data.rp_params.to_wire(), - }; - EchoBroadcast::new(format, message) + }) } fn receive_message( &self, - format: &BoxedFormat, from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.direct_message.assert_is_none()?; - let echo_broadcast = message.echo_broadcast.deserialize::>(format)?; - let normal_broadcast = message - .normal_broadcast - .deserialize::>(format)?; + message: ProtocolMessage, + ) -> Result> { + let echo_broadcast = message.echo_broadcast; + let normal_broadcast = message.normal_broadcast; let data = PublicData { paillier_pk: normal_broadcast.paillier_pk.into_precomputed(), @@ -546,41 +523,39 @@ impl Round for Round2 { u: normal_broadcast.u, }; - let cap_v = self.cap_vs.safe_get("other nodes' `V`", from)?; + let cap_v = self.cap_vs.get_or_local_error("other nodes' `V`", from)?; if &data.hash(&self.context.sid, from) != cap_v { - return Err(ReceiveError::protocol(Error::R2HashMismatch.into())); + return Err(ReceiveError::Protocol(R2ErrorEnum::HashMismatch.into())); } if data.paillier_pk.modulus().bits_vartime() < ::MODULUS_BITS - 2 { - return Err(ReceiveError::protocol(Error::R2PaillierModulusTooSmall.into())); + return Err(ReceiveError::Protocol(R2ErrorEnum::PaillierModulusTooSmall.into())); } if data.rp_params.modulus().bits_vartime() < ::MODULUS_BITS - 2 { - return Err(ReceiveError::protocol(Error::R2RPModulusTooSmall.into())); + return Err(ReceiveError::Protocol(R2ErrorEnum::RPModulusTooSmall.into())); } let aux = (&self.context.sid, &from); if !data.psi.verify(&data.rp_params, &aux) { - return Err(ReceiveError::protocol(Error::R2PrmFailed.into())); + return Err(ReceiveError::Protocol(R2ErrorEnum::PrmFailed.into())); } - let payload = Round2Payload::

{ + Ok(Round2Payload::

{ paillier_pk: data.paillier_pk, rp_params: data.rp_params, rid: data.rid, - }; - - Ok(Payload::new(payload)) + }) } fn finalize( - self: Box, - rng: &mut dyn CryptoRngCore, - payloads: BTreeMap, - _artifacts: BTreeMap, + self, + rng: &mut impl CryptoRngCore, + payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { - let mut payloads = payloads.downcast_all::>()?; + let mut payloads = payloads; let mut rid_combined = self.public_data.rid.clone(); for payload in payloads.values() { @@ -606,12 +581,12 @@ impl Round for Round2 { psi_prime, }; - Ok(FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(next_round))) + Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(next_round))) } } #[derive(Debug)] -struct Round3 { +pub(super) struct Round3 { context: Context, rid_combined: BitVec, r2_payloads: BTreeMap>, @@ -633,6 +608,15 @@ pub(super) struct Round3DirectMessage { impl Round for Round3 { type Protocol = AuxGenProtocol; + type DirectMessage = Round3DirectMessage

; + type NormalBroadcast = Round3NormalBroadcast

; + type EchoBroadcast = NoMessage; + + type Payload = (); + type Artifact = (); + + type ProtocolError = R3Error; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear_terminating(3) } @@ -641,76 +625,64 @@ impl Round for Round3 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_normal_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - let message = Round3NormalBroadcast { + fn make_normal_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round3NormalBroadcast { psi_prime: self.psi_prime.clone(), - }; - NormalBroadcast::new(format, message) + }) } fn make_direct_message( &self, - rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, + rng: &mut impl CryptoRngCore, destination: &Id, - ) -> Result<(DirectMessage, Option), LocalError> { + ) -> Result<(Self::DirectMessage, Self::Artifact), LocalError> { let my_id = &self.context.my_id; let aux = (&self.context.sid, my_id, &self.rid_combined); - let r2_payload = self.r2_payloads.safe_get("Round 2 payloads", destination)?; + let r2_payload = self.r2_payloads.get_or_local_error("Round 2 payloads", destination)?; let psi = FacProof::

::new(rng, &self.context.paillier_sk, &r2_payload.rp_params, &aux); - let message = Round3DirectMessage { psi }; - let dm = DirectMessage::new(format, message)?; - Ok((dm, None)) + Ok((Round3DirectMessage { psi }, ())) } fn receive_message( &self, - format: &BoxedFormat, from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.echo_broadcast.assert_is_none()?; - let normal_broadcast = message - .normal_broadcast - .deserialize::>(format)?; - let direct_message = message.direct_message.deserialize::>(format)?; + message: ProtocolMessage, + ) -> Result> { + let normal_broadcast = message.normal_broadcast; + let direct_message = message.direct_message; let my_id = &self.context.my_id; - let r2_payload = self.r2_payloads.safe_get("Round 2 payloads", from)?; + let r2_payload = self.r2_payloads.get_or_local_error("Round 2 payloads", from)?; let aux = (&self.context.sid, from, &self.rid_combined); if !normal_broadcast.psi_prime.verify(&r2_payload.paillier_pk, &aux) { - return Err(ReceiveError::protocol(Error::R3ModFailed.into())); + return Err(ReceiveError::Protocol(R3ErrorEnum::ModFailed.into())); } if !direct_message .psi .verify(&r2_payload.paillier_pk, &self.context.rp_params, &aux) { - return Err(ReceiveError::protocol( - Error::R3FacFailed { + return Err(ReceiveError::Protocol( + R3ErrorEnum::FacFailed { reported_by: my_id.clone(), } .into(), )); } - Ok(Payload::empty()) + Ok(()) } fn finalize( - self: Box, - _rng: &mut dyn CryptoRngCore, - _payloads: BTreeMap, - _artifacts: BTreeMap, + self, + _rng: &mut impl CryptoRngCore, + _payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { let my_id = &self.context.my_id; @@ -738,7 +710,7 @@ mod tests { use alloc::collections::BTreeSet; use manul::{ - dev::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, + dev::{BinaryFormat, TestSessionParams, TestSigner, TestVerifier, run_sync}, signature::Keypair, }; use rand_core::OsRng; diff --git a/src/protocols/interactive_signing.rs b/src/protocols/interactive_signing.rs index 5a690562..21604c87 100644 --- a/src/protocols/interactive_signing.rs +++ b/src/protocols/interactive_signing.rs @@ -5,23 +5,19 @@ //! - Failed Chi error round (Section 4.3.1) - Round 6. use alloc::{ - boxed::Box, collections::{BTreeMap, BTreeSet}, + string::String, }; -use core::{ - fmt::{self, Debug, Display}, - marker::PhantomData, -}; +use core::{fmt::Debug, marker::PhantomData}; use elliptic_curve::{Curve, FieldBytes}; use manul::{ protocol::{ - Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EntryPoint, - FinalizeOutcome, LocalError, MessageValidationError, NormalBroadcast, PartyId, Payload, Protocol, - ProtocolError, ProtocolMessage, ProtocolMessagePart, ProtocolValidationError, ReceiveError, - RequiredMessageParts, RequiredMessages, Round, RoundId, TransitionInfo, + BoxedRound, CommunicationInfo, EntryPoint, EvidenceError, EvidenceMessages, FinalizeOutcome, LocalError, + NoArtifact, NoMessage, PartyId, Protocol, ProtocolError, ProtocolMessage, ReceiveError, RequiredMessageParts, + RequiredMessages, Round, RoundId, RoundInfo, TransitionInfo, }, - utils::SerializableMap, + utils::{GetOrInvalidEvidence, GetOrLocalError, MapValuesRef, SerializableMap, Without, verify_that}, }; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; @@ -30,14 +26,10 @@ use crate::{ curve::{Point, RecoverableSignature, Scalar}, entities::{AuxInfo, AuxInfoPrecomputed, KeyShare, PublicAuxInfoPrecomputed, PublicAuxInfos, PublicKeyShares}, paillier::{Ciphertext, CiphertextWire, PaillierParams, Randomizer}, - params::{chain_scheme_params, secret_scalar_from_signed, secret_signed_from_scalar, SchemeParams}, + params::{SchemeParams, chain_scheme_params, secret_scalar_from_signed, secret_signed_from_scalar}, tools::{ - hashing::{Chain, HashOutput, Hasher}, - protocol_shortcuts::{ - sum_non_empty, sum_non_empty_ref, verify_that, DeserializeAll, DowncastMap, GetRound, MapValues, SafeGet, - Without, - }, Secret, + hashing::{Chain, HashOutput, Hasher}, }, uint::SecretSigned, zk::{ @@ -47,6 +39,35 @@ use crate::{ }, }; +/// Analogous to `Iterator::sum()`, but requires a non-empty iterator +/// (so that it can be used for types with no `default()`, like `Ciphertext`) +fn sum_non_empty(mut iter: I, empty_error: E) -> Result +where + I: Iterator>, + T: core::ops::Add, +{ + let mut result = iter.next().ok_or(empty_error)??; + for item in iter { + result = result + item?; + } + Ok(result) +} + +/// Analogous to `Iterator::sum()`, but requires a non-empty iterator +/// (so that it can be used for types with no `default()`, like `Ciphertext`) +fn sum_non_empty_ref<'x, T, I, E>(mut iter: I, empty_error: E) -> Result +where + I: Iterator>, + T: 'x + Clone, + for<'a> T: core::ops::Add<&'a T, Output = T>, +{ + let mut result = iter.next().ok_or(empty_error)??.clone(); + for item in iter { + result = result + item?; + } + Ok(result) +} + /// Prehashed message to sign. // TODO: Type aliases are not enforced by the compiler, but they should be. Maybe one? #[allow(type_alias_bounds)] @@ -71,303 +92,73 @@ pub struct InteractiveSigningProtocol(PhantomData<(P impl Protocol for InteractiveSigningProtocol { type Result = RecoverableSignature

; - type ProtocolError = InteractiveSigningError; - - fn verify_direct_message_is_invalid( - format: &BoxedFormat, - round_id: &RoundId, - message: &DirectMessage, - ) -> Result<(), MessageValidationError> { - match round_id { - r if r == &1 => message.verify_is_not::>(format), - r if r == &2 => message.verify_is_some(), - r if r == &3 => message.verify_is_some(), - r if r == &4 => message.verify_is_some(), - r if r == &5 => message.verify_is_some(), - r if r == &6 => message.verify_is_some(), - _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), - } - } - - fn verify_echo_broadcast_is_invalid( - format: &BoxedFormat, - round_id: &RoundId, - message: &EchoBroadcast, - ) -> Result<(), MessageValidationError> { + type SharedData = InteractiveSigningSharedData; + fn round_info(round_id: &RoundId) -> Option> { match round_id { - r if r == &1 => message.verify_is_not::>(format), - r if r == &2 => message.verify_is_not::>(format), - r if r == &3 => message.verify_is_not::>(format), - r if r == &4 => message.verify_is_some(), - r if r == &5 => message.verify_is_not::>(format), - r if r == &6 => message.verify_is_not::>(format), - _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), - } - } - - fn verify_normal_broadcast_is_invalid( - format: &BoxedFormat, - round_id: &RoundId, - message: &NormalBroadcast, - ) -> Result<(), MessageValidationError> { - match round_id { - r if r == &1 => message.verify_is_some(), - r if r == &2 => message.verify_is_not::>(format), - r if r == &3 => message.verify_is_not::>(format), - r if r == &4 => message.verify_is_not::>(format), - r if r == &5 => message.verify_is_some(), - r if r == &6 => message.verify_is_some(), - _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), + _ if round_id == 1 => Some(RoundInfo::new::>()), + _ if round_id == 2 => Some(RoundInfo::new::>()), + _ if round_id == 3 => Some(RoundInfo::new::>()), + _ if round_id == 4 => Some(RoundInfo::new::>()), + _ if round_id == 5 => Some(RoundInfo::new::>()), + _ if round_id == 6 => Some(RoundInfo::new::>()), + _ => None, } } } -/// Possible verifiable errors of the InteractiveSigning protocol. #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct InteractiveSigningError { - error: Error, - phantom: PhantomData

, +pub(super) struct R1Error { + error: R1ErrorEnum, + phantom: PhantomData (P, Id)>, } -impl Display for InteractiveSigningError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - f, - "{}", - match self.error { - Error::R1EncElg0Failed => "Round 1: failed to verify `\\psi^0` (`П^{enc-elg}` proof).", - Error::R1EncElg1Failed => "Round 1: failed to verify `\\psi^1` (`П^{enc-elg}` proof).", - Error::R2WrongIdsD => "Round 2: wrong IDs in `D` map.", - Error::R2WrongIdsF => "Round 2: wrong IDs in `F` map.", - Error::R2WrongIdsPsi => "Round 2: wrong IDs in `\\psi` map (`П^{aff-g}` proofs for `D`).", - Error::R2AffGPsiFailed { .. } => "Round 2: failed to verify `\\psi` (`П^{aff-g}` proof for `D`).", - Error::R2AffGHatPsiFailed { .. } => - "Round 2: failed to verify `\\hat{psi}` (`П^{aff-g}` proof for `\\hat{D}`).", - Error::R2ElogFailed => "Round 2: failed to verify `П^{elog}` proof.", - Error::R3ElogFailed => "Round 3: failed to verify `П^{elog}` proof.", - Error::R4InvalidSignatureShare => "Round 4: signature share verification failed.", - Error::R5DecFailed => "Round 5: `П^{dec}` proof verification failed.", - Error::R5WrongIdsPsi => "Round 5: wrong IDs in `П^{aff-g*}` proof map.", - Error::R5AffGStarFailed { .. } => "Round 5: `П^{aff-g*}` proof verification failed.", - Error::R6DecFailed => "Round 6: `П^{dec}` proof verification failed.", - Error::R6WrongIdsPsi => "Round 6: wrong IDs in `П^{aff-g*}` proof map.", - Error::R6AffGStarFailed { .. } => "Round 6: `П^{aff-g*}` proof verification failed.", - } - ) - } -} - -/// Possible verifiable errors of the InteractiveSigning protocol. #[derive(Debug, Clone, Serialize, Deserialize)] -enum Error { - R1EncElg0Failed, - R1EncElg1Failed, - R2WrongIdsD, - R2WrongIdsF, - R2WrongIdsPsi, - R2AffGPsiFailed { - /// The index $k$ for which the verification of $\psi_{k,j}$ failed - /// (where $j$ is the index of the guilty party). - failed_for: Id, - }, - R2AffGHatPsiFailed { - /// The index $k$ for which the verification of $\hat{\psi}_{k,j}$ failed - /// (where $j$ is the index of the guilty party). - failed_for: Id, - }, - R2ElogFailed, - R3ElogFailed, - R4InvalidSignatureShare, - R5DecFailed, - R5WrongIdsPsi, - R5AffGStarFailed { - /// The index $\ell$ for which the verification of $\psi_{j,\ell}$ failed - /// (where $j$ is the index of the guilty party). - failed_for: Id, - }, - R6DecFailed, - R6WrongIdsPsi, - R6AffGStarFailed { - /// The index $\ell$ for which the verification of $\hat{\psi}_{j,\ell}$ failed - /// (where $j$ is the index of the guilty party). - failed_for: Id, - }, -} - -impl From> for InteractiveSigningError { - fn from(source: Error) -> Self { - Self { - error: source, - phantom: PhantomData, - } - } -} - -/// Associated data for InteractiveSigning protocol. -#[derive(Debug, Clone)] -pub struct InteractiveSigningAssociatedData { - /// Public shares of all participating nodes. - pub shares: PublicKeyShares, - /// Auxiliary data of all participating nodes. - pub aux: PublicAuxInfos, - /// The message to be signed. - pub message: PrehashedMessage, +enum R1ErrorEnum { + EncElg0Failed, + EncElg1Failed, } -impl InteractiveSigningAssociatedData { - /// Creates the associated data for evidence verification of InteractiveSigning. - pub fn new( - message: PrehashedMessage, - public_key_shares: PublicKeyShares, - public_aux_infos: PublicAuxInfos, - ) -> Result { - let key_share_keys = public_key_shares.as_map().keys().collect::>(); - let aux_info_keys = public_aux_infos.as_map().keys().collect::>(); +impl ProtocolError for R1Error { + type Round = Round1; - if key_share_keys != aux_info_keys { - return Err(LocalError::new( - "The key share and the auxiliary info must have information for the same set of parties", - )); + fn description(&self) -> String { + match self.error { + R1ErrorEnum::EncElg0Failed => "failed to verify `\\psi^0` (`П^{enc-elg}` proof).", + R1ErrorEnum::EncElg1Failed => "failed to verify `\\psi^1` (`П^{enc-elg}` proof).", } - - Ok(Self { - shares: public_key_shares, - aux: public_aux_infos, - message, - }) + .into() } -} - -/// The epoch identifier (see Remark 4.1 in the paper). -/// -/// The epoch identifier is tied to the key-refresh epoch and the auxiliary key material of the parties for that epoch. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct Epid(HashOutput); -impl Epid { - fn new( - shared_randomness: &[u8], - associated_data: &InteractiveSigningAssociatedData, - ) -> Self { - let digest = Hasher::::new_with_dst(b"EPID"); - let digest = chain_scheme_params::(digest); - let digest = digest - .chain(&shared_randomness) - .chain(&associated_data.shares) - .chain(&associated_data.aux); - - Self(digest.finalize(P::SECURITY_BITS)) - } -} - -impl ProtocolError for InteractiveSigningError { - type AssociatedData = InteractiveSigningAssociatedData; - - fn required_messages(&self) -> RequiredMessages { + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { match self.error { - Error::R1EncElg0Failed => { + R1ErrorEnum::EncElg0Failed => { RequiredMessages::new(RequiredMessageParts::echo_broadcast().and_direct_message(), None, None) } - Error::R1EncElg1Failed => { + R1ErrorEnum::EncElg1Failed => { RequiredMessages::new(RequiredMessageParts::echo_broadcast().and_direct_message(), None, None) } - Error::R2WrongIdsD => RequiredMessages::new(RequiredMessageParts::normal_broadcast(), None, None), - Error::R2WrongIdsF => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), - Error::R2WrongIdsPsi => RequiredMessages::new(RequiredMessageParts::normal_broadcast(), None, None), - Error::R2AffGPsiFailed { .. } => RequiredMessages::new( - RequiredMessageParts::echo_broadcast().and_normal_broadcast(), - None, - Some([1.into()].into()), - ), - Error::R2AffGHatPsiFailed { .. } => RequiredMessages::new( - RequiredMessageParts::echo_broadcast().and_normal_broadcast(), - None, - Some([1.into()].into()), - ), - Error::R2ElogFailed => RequiredMessages::new( - RequiredMessageParts::echo_broadcast().and_normal_broadcast(), - Some([(1.into(), RequiredMessageParts::echo_broadcast())].into()), - None, - ), - Error::R3ElogFailed => RequiredMessages::new( - RequiredMessageParts::normal_broadcast(), - Some( - [ - (1.into(), RequiredMessageParts::echo_broadcast()), - (2.into(), RequiredMessageParts::echo_broadcast()), - ] - .into(), - ), - None, - ), - Error::R4InvalidSignatureShare => RequiredMessages::new( - RequiredMessageParts::normal_broadcast(), - Some( - [ - (2.into(), RequiredMessageParts::echo_broadcast()), - (3.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast()), - ] - .into(), - ), - Some([2.into(), 3.into()].into()), - ), - Error::R5DecFailed => RequiredMessages::new( - RequiredMessageParts::echo_broadcast(), - Some( - [ - (1.into(), RequiredMessageParts::echo_broadcast()), - (2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast()), - (3.into(), RequiredMessageParts::normal_broadcast()), - ] - .into(), - ), - Some([2.into()].into()), - ), - Error::R5WrongIdsPsi => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), - Error::R5AffGStarFailed { .. } => RequiredMessages::new( - RequiredMessageParts::echo_broadcast(), - Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), - Some([1.into(), 2.into()].into()), - ), - Error::R6DecFailed => RequiredMessages::new( - RequiredMessageParts::echo_broadcast(), - Some( - [ - (1.into(), RequiredMessageParts::echo_broadcast()), - (2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast()), - (3.into(), RequiredMessageParts::normal_broadcast()), - ] - .into(), - ), - Some([2.into()].into()), - ), - Error::R6WrongIdsPsi => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), - Error::R6AffGStarFailed { .. } => RequiredMessages::new( - RequiredMessageParts::echo_broadcast(), - None, - Some([1.into(), 2.into()].into()), - ), } } - fn verify_messages_constitute_error( + fn verify_evidence( &self, - format: &BoxedFormat, + _round_id: &RoundId, guilty_party: &Id, shared_randomness: &[u8], - associated_data: &Self::AssociatedData, - message: ProtocolMessage, - previous_messages: BTreeMap, - combined_echos: BTreeMap>, - ) -> Result<(), ProtocolValidationError> { - let epid = Epid::new::(shared_randomness, associated_data); + shared_data: &<>::Protocol as Protocol>::SharedData, + messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + let epid = Epid::new::(shared_randomness, shared_data); match &self.error { - Error::R1EncElg0Failed => { - let r1_dm = message.direct_message.deserialize::>(format)?; - let r1_eb = message.echo_broadcast.deserialize::>(format)?; + R1ErrorEnum::EncElg0Failed => { + let r1_dm = messages.direct_message()?; + let r1_eb = messages.echo_broadcast()?; - let public_aux = &associated_data.aux.as_map().try_get("aux infos", guilty_party)?; + let public_aux = &shared_data + .aux + .as_map() + .get_or_invalid_evidence("aux infos", guilty_party)?; let pk = public_aux.paillier_pk.clone().into_precomputed(); let rp = public_aux.rp_params.to_precomputed(); @@ -385,11 +176,14 @@ impl ProtocolError for InteractiveSigningError &aux, )) } - Error::R1EncElg1Failed => { - let r1_dm = message.direct_message.deserialize::>(format)?; - let r1_eb = message.echo_broadcast.deserialize::>(format)?; + R1ErrorEnum::EncElg1Failed => { + let r1_dm = messages.direct_message()?; + let r1_eb = messages.echo_broadcast()?; - let public_aux = &associated_data.aux.as_map().try_get("aux infos", guilty_party)?; + let public_aux = &shared_data + .aux + .as_map() + .get_or_invalid_evidence("aux infos", guilty_party)?; let pk = public_aux.paillier_pk.clone().into_precomputed(); let rp = public_aux.rp_params.to_precomputed(); @@ -407,11 +201,86 @@ impl ProtocolError for InteractiveSigningError &aux, )) } - Error::R2WrongIdsD => { - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; - let expected_ids = associated_data + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(super) struct R2Error { + error: R2ErrorEnum, + phantom: PhantomData P>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +enum R2ErrorEnum { + WrongIdsD, + WrongIdsF, + WrongIdsPsi, + AffGPsiFailed { + /// The index $k$ for which the verification of $\psi_{k,j}$ failed + /// (where $j$ is the index of the guilty party). + failed_for: Id, + }, + AffGHatPsiFailed { + /// The index $k$ for which the verification of $\hat{\psi}_{k,j}$ failed + /// (where $j$ is the index of the guilty party). + failed_for: Id, + }, + ElogFailed, +} + +impl ProtocolError for R2Error { + type Round = Round2; + + fn description(&self) -> String { + match self.error { + R2ErrorEnum::WrongIdsD => "wrong IDs in `D` map.", + R2ErrorEnum::WrongIdsF => "wrong IDs in `F` map.", + R2ErrorEnum::WrongIdsPsi => "wrong IDs in `\\psi` map (`П^{aff-g}` proofs for `D`).", + R2ErrorEnum::AffGPsiFailed { .. } => "failed to verify `\\psi` (`П^{aff-g}` proof for `D`).", + R2ErrorEnum::AffGHatPsiFailed { .. } => "failed to verify `\\hat{psi}` (`П^{aff-g}` proof for `\\hat{D}`).", + R2ErrorEnum::ElogFailed => "failed to verify `П^{elog}` proof.", + } + .into() + } + + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { + match self.error { + R2ErrorEnum::WrongIdsD => RequiredMessages::new(RequiredMessageParts::normal_broadcast(), None, None), + R2ErrorEnum::WrongIdsF => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), + R2ErrorEnum::WrongIdsPsi => RequiredMessages::new(RequiredMessageParts::normal_broadcast(), None, None), + R2ErrorEnum::AffGPsiFailed { .. } => RequiredMessages::new( + RequiredMessageParts::echo_broadcast().and_normal_broadcast(), + None, + Some([1.into()].into()), + ), + R2ErrorEnum::AffGHatPsiFailed { .. } => RequiredMessages::new( + RequiredMessageParts::echo_broadcast().and_normal_broadcast(), + None, + Some([1.into()].into()), + ), + R2ErrorEnum::ElogFailed => RequiredMessages::new( + RequiredMessageParts::echo_broadcast().and_normal_broadcast(), + Some([(1.into(), RequiredMessageParts::echo_broadcast())].into()), + None, + ), + } + } + + fn verify_evidence( + &self, + _round_id: &RoundId, + guilty_party: &Id, + shared_randomness: &[u8], + shared_data: &<>::Protocol as Protocol>::SharedData, + messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + let epid = Epid::new::(shared_randomness, shared_data); + + match &self.error { + R2ErrorEnum::WrongIdsD => { + let r2_nb = messages.normal_broadcast()?; + let expected_ids = shared_data .aux .as_map() .keys() @@ -419,11 +288,9 @@ impl ProtocolError for InteractiveSigningError .without(&guilty_party); verify_that(r2_nb.cap_ds.keys().collect::>() != expected_ids) } - Error::R2WrongIdsF => { - let r2_eb = message - .echo_broadcast - .deserialize::>(format)?; - let expected_ids = associated_data + R2ErrorEnum::WrongIdsF => { + let r2_eb = messages.echo_broadcast()?; + let expected_ids = shared_data .aux .as_map() .keys() @@ -431,11 +298,9 @@ impl ProtocolError for InteractiveSigningError .without(&guilty_party); verify_that(r2_eb.cap_fs.keys().collect::>() != expected_ids) } - Error::R2WrongIdsPsi => { - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; - let expected_ids = associated_data + R2ErrorEnum::WrongIdsPsi => { + let r2_nb = messages.normal_broadcast()?; + let expected_ids = shared_data .aux .as_map() .keys() @@ -443,20 +308,22 @@ impl ProtocolError for InteractiveSigningError .without(&guilty_party); verify_that(r2_nb.psis.keys().collect::>() != expected_ids) } - Error::R2AffGPsiFailed { failed_for } => { - let r1_eb = combined_echos - .get_round(1)? - .try_get("combined echos for Round 1", failed_for)? - .deserialize::>(format)?; - let r2_eb = message - .echo_broadcast - .deserialize::>(format)?; - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; - - let failed_for_aux = &associated_data.aux.as_map().try_get("aux infos", failed_for)?; - let guilty_party_aux = &associated_data.aux.as_map().try_get("aux infos", guilty_party)?; + R2ErrorEnum::AffGPsiFailed { failed_for } => { + let r1_eb = messages + .combined_echos::>(1)? + .get_or_invalid_evidence("combined echos for Round 1", failed_for)? + .clone(); + let r2_eb = messages.echo_broadcast()?; + let r2_nb = messages.normal_broadcast()?; + + let failed_for_aux = &shared_data + .aux + .as_map() + .get_or_invalid_evidence("aux infos", failed_for)?; + let guilty_party_aux = &shared_data + .aux + .as_map() + .get_or_invalid_evidence("aux infos", guilty_party)?; let rp = failed_for_aux.rp_params.to_precomputed(); let aux = (&epid, guilty_party); @@ -465,10 +332,16 @@ impl ProtocolError for InteractiveSigningError let from_pk = guilty_party_aux.paillier_pk.clone().into_precomputed(); let cap_k = r1_eb.cap_k.to_precomputed(&for_pk); - let cap_d = r2_nb.cap_ds.safe_get("`D` map", failed_for)?.to_precomputed(&for_pk); - let cap_f = r2_eb.cap_fs.safe_get("`F` map", failed_for)?.to_precomputed(&from_pk); + let cap_d = r2_nb + .cap_ds + .get_or_invalid_evidence("`D` map", failed_for)? + .to_precomputed(&for_pk); + let cap_f = r2_eb + .cap_fs + .get_or_invalid_evidence("`F` map", failed_for)? + .to_precomputed(&from_pk); - let psi = r2_nb.psis.try_get("`psi` map", failed_for)?; + let psi = r2_nb.psis.get_or_invalid_evidence("`psi` map", failed_for)?; verify_that(!psi.verify( AffGPublicInputs { pk0: &for_pk, @@ -482,22 +355,27 @@ impl ProtocolError for InteractiveSigningError &aux, )) } - Error::R2AffGHatPsiFailed { failed_for } => { - let r1_eb = combined_echos - .get_round(1)? - .try_get("combined echos for Round 1", failed_for)? - .deserialize::>(format)?; - let r2_eb = message - .echo_broadcast - .deserialize::>(format)?; - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; - - let cap_x = associated_data.shares.as_map().try_get("shares", failed_for)?; - - let failed_for_aux = &associated_data.aux.as_map().try_get("aux infos", failed_for)?; - let guilty_party_aux = &associated_data.aux.as_map().try_get("aux infos", guilty_party)?; + R2ErrorEnum::AffGHatPsiFailed { failed_for } => { + let r1_eb = messages + .combined_echos::>(1)? + .get_or_invalid_evidence("combined echos for Round 1", failed_for)? + .clone(); + let r2_eb = messages.echo_broadcast()?; + let r2_nb = messages.normal_broadcast()?; + + let cap_x = shared_data + .shares + .as_map() + .get_or_invalid_evidence("shares", failed_for)?; + + let failed_for_aux = &shared_data + .aux + .as_map() + .get_or_invalid_evidence("aux infos", failed_for)?; + let guilty_party_aux = &shared_data + .aux + .as_map() + .get_or_invalid_evidence("aux infos", guilty_party)?; let rp = failed_for_aux.rp_params.to_precomputed(); let aux = (&epid, guilty_party); @@ -508,14 +386,14 @@ impl ProtocolError for InteractiveSigningError let cap_k = r1_eb.cap_k.to_precomputed(&for_pk); let hat_cap_d = r2_nb .hat_cap_ds - .safe_get("`\\hat{D}` map", failed_for)? + .get_or_invalid_evidence("`\\hat{D}` map", failed_for)? .to_precomputed(&for_pk); let hat_cap_f = r2_eb .hat_cap_fs - .safe_get("`\\hat{F}` map", failed_for)? + .get_or_invalid_evidence("`\\hat{F}` map", failed_for)? .to_precomputed(&from_pk); - let hat_psi = r2_nb.hat_psis.try_get("`\\hat{psi}` map", failed_for)?; + let hat_psi = r2_nb.hat_psis.get_or_invalid_evidence("`\\hat{psi}` map", failed_for)?; verify_that(!hat_psi.verify( AffGPublicInputs { pk0: &for_pk, @@ -529,17 +407,10 @@ impl ProtocolError for InteractiveSigningError &aux, )) } - Error::R2ElogFailed => { - let r1_eb = previous_messages - .get_round(1)? - .echo_broadcast - .deserialize::>(format)?; - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; - let r2_eb = message - .echo_broadcast - .deserialize::>(format)?; + R2ErrorEnum::ElogFailed => { + let r1_eb = messages.previous_echo_broadcast::>(1)?; + let r2_nb = messages.normal_broadcast()?; + let r2_eb = messages.echo_broadcast()?; let aux = (&epid, guilty_party); verify_that(!r2_nb.psi_elog.verify( @@ -553,18 +424,62 @@ impl ProtocolError for InteractiveSigningError &aux, )) } - Error::R3ElogFailed => { - let r1_eb = previous_messages - .get_round(1)? - .echo_broadcast - .deserialize::>(format)?; - let r2_eb = previous_messages - .get_round(2)? - .echo_broadcast - .deserialize::>(format)?; - let r3_nb = message - .normal_broadcast - .deserialize::>(format)?; + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(super) struct R3Error { + error: R3ErrorEnum, + phantom: PhantomData (P, Id)>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +enum R3ErrorEnum { + ElogFailed, +} + +impl ProtocolError for R3Error { + type Round = Round3; + + fn description(&self) -> String { + match self.error { + R3ErrorEnum::ElogFailed => "failed to verify `П^{elog}` proof.", + } + .into() + } + + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { + match self.error { + R3ErrorEnum::ElogFailed => RequiredMessages::new( + RequiredMessageParts::normal_broadcast(), + Some( + [ + (1.into(), RequiredMessageParts::echo_broadcast()), + (2.into(), RequiredMessageParts::echo_broadcast()), + ] + .into(), + ), + None, + ), + } + } + + fn verify_evidence( + &self, + _round_id: &RoundId, + guilty_party: &Id, + shared_randomness: &[u8], + shared_data: &<>::Protocol as Protocol>::SharedData, + messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + let epid = Epid::new::(shared_randomness, shared_data); + + match &self.error { + R3ErrorEnum::ElogFailed => { + let r1_eb = messages.previous_echo_broadcast::>(1)?; + let r2_eb = messages.previous_echo_broadcast::>(2)?; + let r3_nb = messages.normal_broadcast()?; let aux = (&epid, guilty_party); verify_that(!r3_nb.psi_prime.verify( @@ -578,63 +493,149 @@ impl ProtocolError for InteractiveSigningError &aux, )) } - Error::R4InvalidSignatureShare => { - let r2_ebs = combined_echos - .get_round(2)? - .deserialize_all::>(format)?; - let r2_eb = previous_messages - .get_round(2)? - .echo_broadcast - .deserialize::>(format)?; - let r3_nb = previous_messages - .get_round(3)? - .normal_broadcast - .deserialize::>(format)?; - let r3_ebs = combined_echos - .get_round(3)? - .deserialize_all::>(format)?; - let r3_eb = previous_messages - .get_round(3)? - .echo_broadcast - .deserialize::>(format)?; - let r4_nb = message - .normal_broadcast - .deserialize::>(format)?; + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(super) struct R4Error { + error: R4ErrorEnum, + phantom: PhantomData (P, Id)>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +enum R4ErrorEnum { + InvalidSignatureShare, +} + +impl ProtocolError for R4Error { + type Round = Round4; + + fn description(&self) -> String { + match self.error { + R4ErrorEnum::InvalidSignatureShare => "signature share verification failed.", + } + .into() + } + + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { + match self.error { + R4ErrorEnum::InvalidSignatureShare => RequiredMessages::new( + RequiredMessageParts::normal_broadcast(), + Some( + [ + (2.into(), RequiredMessageParts::echo_broadcast()), + (3.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast()), + ] + .into(), + ), + Some([2.into(), 3.into()].into()), + ), + } + } + + fn verify_evidence( + &self, + _round_id: &RoundId, + _guilty_party: &Id, + _shared_randomness: &[u8], + shared_data: &<>::Protocol as Protocol>::SharedData, + messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + match &self.error { + R4ErrorEnum::InvalidSignatureShare => { + let r2_ebs = messages.combined_echos::>(2)?; + let r2_eb = messages.previous_echo_broadcast::>(2)?; + let r3_nb = messages.previous_normal_broadcast::>(3)?; + let r3_ebs = messages.combined_echos::>(3)?; + let r3_eb = messages.previous_echo_broadcast::>(3)?; + let r4_nb = messages.normal_broadcast()?; let cap_gamma = r2_eb.cap_gamma + r2_ebs.values().map(|eb| eb.cap_gamma).sum(); let nonce = cap_gamma.x_coordinate(); let delta = r3_eb.delta + r3_ebs.values().map(|eb| eb.delta).sum::>(); let delta_inv = Option::>::from(delta.invert()) - .ok_or_else(|| ProtocolValidationError::InvalidEvidence("`delta` is not invertible".into()))?; + .ok_or_else(|| EvidenceError::InvalidEvidence("`delta` is not invertible".into()))?; let tilde_cap_delta = r3_nb.cap_delta * delta_inv; let tilde_cap_s = r3_nb.cap_s * delta_inv; - let scalar_message = Scalar::from_reduced_bytes(associated_data.message.clone()); + let scalar_message = Scalar::from_reduced_bytes(shared_data.message.clone()); verify_that(cap_gamma * r4_nb.sigma != tilde_cap_delta * scalar_message + tilde_cap_s * nonce) } - Error::R5DecFailed => { - let r1_eb = previous_messages - .get_round(1)? - .echo_broadcast - .deserialize::>(format)?; - let r2_ebs = combined_echos - .get_round(2)? - .deserialize_all::>(format)?; - let r2_nb = previous_messages - .get_round(2)? - .normal_broadcast - .deserialize::>(format)?; - let r2_eb = previous_messages - .get_round(2)? - .echo_broadcast - .deserialize::>(format)?; - let r3_nb = previous_messages - .get_round(3)? - .normal_broadcast - .deserialize::>(format)?; - let r5_eb = message - .echo_broadcast - .deserialize::>(format)?; + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(super) struct R5Error { + error: R5ErrorEnum, + phantom: PhantomData P>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +enum R5ErrorEnum { + DecFailed, + WrongIdsPsi, + AffGStarFailed { + /// The index $\ell$ for which the verification of $\psi_{j,\ell}$ failed + /// (where $j$ is the index of the guilty party). + failed_for: Id, + }, +} + +impl ProtocolError for R5Error { + type Round = Round5; + + fn description(&self) -> String { + match self.error { + R5ErrorEnum::DecFailed => "`П^{dec}` proof verification failed.", + R5ErrorEnum::WrongIdsPsi => "wrong IDs in `П^{aff-g*}` proof map.", + R5ErrorEnum::AffGStarFailed { .. } => "`П^{aff-g*}` proof verification failed.", + } + .into() + } + + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { + match self.error { + R5ErrorEnum::DecFailed => RequiredMessages::new( + RequiredMessageParts::echo_broadcast(), + Some( + [ + (1.into(), RequiredMessageParts::echo_broadcast()), + (2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast()), + (3.into(), RequiredMessageParts::normal_broadcast()), + ] + .into(), + ), + Some([2.into()].into()), + ), + R5ErrorEnum::WrongIdsPsi => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), + R5ErrorEnum::AffGStarFailed { .. } => RequiredMessages::new( + RequiredMessageParts::echo_broadcast(), + Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), + Some([1.into(), 2.into()].into()), + ), + } + } + + fn verify_evidence( + &self, + _round_id: &RoundId, + guilty_party: &Id, + shared_randomness: &[u8], + shared_data: &<>::Protocol as Protocol>::SharedData, + messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + let epid = Epid::new::(shared_randomness, shared_data); + + match &self.error { + R5ErrorEnum::DecFailed => { + let r1_eb = messages.previous_echo_broadcast::>(1)?; + let r2_ebs = messages.combined_echos::>(2)?; + let r2_nb = messages.previous_normal_broadcast::>(2)?; + let r2_eb = messages.previous_echo_broadcast::>(2)?; + let r3_nb = messages.previous_normal_broadcast::>(3)?; + let r5_eb = messages.echo_broadcast()?; // Calculate `D_j` where `j = guilty_party`. // `D_j = sum_{l != j}(D_{l,j} + F_{j,l}) @@ -644,12 +645,15 @@ impl ProtocolError for InteractiveSigningError // r2_ebs[i], i != j: contains D_{l,i}, F_{l,i} for l != i // => F_{j,l} = r2_ebs[l].cap_fs[j] - let public_aux = &associated_data.aux.as_map().try_get("aux infos", guilty_party)?; + let public_aux = &shared_data + .aux + .as_map() + .get_or_invalid_evidence("aux infos", guilty_party)?; let pk = public_aux.paillier_pk.clone().into_precomputed(); let rp = public_aux.rp_params.to_precomputed(); let aux = (&epid, guilty_party); - let ids = associated_data + let ids = shared_data .aux .as_map() .keys() @@ -658,17 +662,17 @@ impl ProtocolError for InteractiveSigningError let cap_d = sum_non_empty( ids.iter() - .map(|id| Ok(r2_nb.cap_ds.try_get("`D` map", id)?.to_precomputed(&pk))), - ProtocolValidationError::InvalidEvidence("There must be at least two parties".into()), + .map(|id| Ok(r2_nb.cap_ds.get_or_invalid_evidence("`D` map", id)?.to_precomputed(&pk))), + EvidenceError::InvalidEvidence("There must be at least two parties".into()), )? + sum_non_empty( ids.iter().map(|id| { Ok(r2_ebs - .try_get("Round 2 echo broadcasts", id)? + .get_or_invalid_evidence("Round 2 echo broadcasts", id)? .cap_fs - .try_get("`F` map", guilty_party)? + .get_or_invalid_evidence("`F` map", guilty_party)? .to_precomputed(&pk)) }), - ProtocolValidationError::InvalidEvidence("There must be at least two parties".into()), + EvidenceError::InvalidEvidence("There must be at least two parties".into()), )?; let cap_k = r1_eb.cap_k.to_precomputed(&pk); @@ -681,47 +685,39 @@ impl ProtocolError for InteractiveSigningError cap_d: &cap_d, cap_s: &r3_nb.cap_delta, cap_g: &Point::generator(), - num_parties: associated_data.aux.num_parties(), + num_parties: shared_data.aux.num_parties(), }, &rp, &aux, )) } - Error::R5WrongIdsPsi => { + R5ErrorEnum::WrongIdsPsi => { // TODO (#188): currently unreachable from tests - let r5_nb = message - .normal_broadcast - .deserialize::>(format)?; - let expected_ids = associated_data + let r5_eb = messages.echo_broadcast()?; + let expected_ids = shared_data .aux .as_map() .keys() .collect::>() .without(&guilty_party); - verify_that(r5_nb.psis.keys().collect::>() != expected_ids) + verify_that(r5_eb.psis.keys().collect::>() != expected_ids) } - Error::R5AffGStarFailed { failed_for } => { + R5ErrorEnum::AffGStarFailed { failed_for } => { // TODO (#188): currently unreachable from tests - let r1_ebs = combined_echos - .get_round(1)? - .deserialize_all::>(format)?; - let r2_ebs = combined_echos - .get_round(2)? - .deserialize_all::>(format)?; - let r2_nb = previous_messages - .get_round(2)? - .normal_broadcast - .deserialize::>(format)?; - let r2_eb = previous_messages - .get_round(2)? - .echo_broadcast - .deserialize::>(format)?; - let r5_eb = message - .echo_broadcast - .deserialize::>(format)?; - - let failed_for_aux = &associated_data.aux.as_map().try_get("aux infos", failed_for)?; - let guilty_party_aux = &associated_data.aux.as_map().try_get("aux infos", guilty_party)?; + let r1_ebs = messages.combined_echos::>(1)?; + let r2_ebs = messages.combined_echos::>(2)?; + let r2_nb = messages.previous_normal_broadcast::>(2)?; + let r2_eb = messages.previous_echo_broadcast::>(2)?; + let r5_eb = messages.echo_broadcast()?; + + let failed_for_aux = &shared_data + .aux + .as_map() + .get_or_invalid_evidence("aux infos", failed_for)?; + let guilty_party_aux = &shared_data + .aux + .as_map() + .get_or_invalid_evidence("aux infos", guilty_party)?; let failed_for_pk = failed_for_aux.paillier_pk.clone().into_precomputed(); @@ -734,19 +730,19 @@ impl ProtocolError for InteractiveSigningError let cap_d = r2_nb .cap_ds - .try_get("`D` map", failed_for)? + .get_or_invalid_evidence("`D` map", failed_for)? .to_precomputed(&failed_for_pk); let cap_k = r1_ebs - .try_get("Round 1 echo broadcasts", failed_for)? + .get_or_invalid_evidence("Round 1 echo broadcasts", failed_for)? .cap_k .to_precomputed(&failed_for_pk); let cap_f = r2_ebs - .try_get("Round 2 echo broadcasts", failed_for)? + .get_or_invalid_evidence("Round 2 echo broadcasts", failed_for)? .cap_fs - .try_get("`F` map", guilty_party)? + .get_or_invalid_evidence("`F` map", guilty_party)? .to_precomputed(&guilty_party_pk); - let psi = r5_eb.psis.try_get("`\\{psi}` map", failed_for)?; + let psi = r5_eb.psis.get_or_invalid_evidence("`\\{psi}` map", failed_for)?; verify_that(!psi.verify( AffGStarPublicInputs { @@ -760,29 +756,80 @@ impl ProtocolError for InteractiveSigningError &aux, )) } - Error::R6DecFailed => { - let r1_eb = previous_messages - .get_round(1)? - .echo_broadcast - .deserialize::>(format)?; - let r2_ebs = combined_echos - .get_round(2)? - .deserialize_all::>(format)?; - let r2_nb = previous_messages - .get_round(2)? - .normal_broadcast - .deserialize::>(format)?; - let r2_eb = previous_messages - .get_round(2)? - .echo_broadcast - .deserialize::>(format)?; - let r3_nb = previous_messages - .get_round(3)? - .normal_broadcast - .deserialize::>(format)?; - let r5_eb = message - .echo_broadcast - .deserialize::>(format)?; + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(super) struct R6Error { + error: R6ErrorEnum, + phantom: PhantomData P>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +enum R6ErrorEnum { + DecFailed, + WrongIdsPsi, + AffGStarFailed { + /// The index $\ell$ for which the verification of $\hat{\psi}_{j,\ell}$ failed + /// (where $j$ is the index of the guilty party). + failed_for: Id, + }, +} + +impl ProtocolError for R6Error { + type Round = Round6; + + fn description(&self) -> String { + match self.error { + R6ErrorEnum::DecFailed => "`П^{dec}` proof verification failed.", + R6ErrorEnum::WrongIdsPsi => "wrong IDs in `П^{aff-g*}` proof map.", + R6ErrorEnum::AffGStarFailed { .. } => "`П^{aff-g*}` proof verification failed.", + } + .into() + } + + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { + match self.error { + R6ErrorEnum::DecFailed => RequiredMessages::new( + RequiredMessageParts::echo_broadcast(), + Some( + [ + (1.into(), RequiredMessageParts::echo_broadcast()), + (2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast()), + (3.into(), RequiredMessageParts::normal_broadcast()), + ] + .into(), + ), + Some([2.into()].into()), + ), + R6ErrorEnum::WrongIdsPsi => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), + R6ErrorEnum::AffGStarFailed { .. } => RequiredMessages::new( + RequiredMessageParts::echo_broadcast(), + None, + Some([1.into(), 2.into()].into()), + ), + } + } + + fn verify_evidence( + &self, + _round_id: &RoundId, + guilty_party: &Id, + shared_randomness: &[u8], + shared_data: &<>::Protocol as Protocol>::SharedData, + messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + let epid = Epid::new::(shared_randomness, shared_data); + + match &self.error { + R6ErrorEnum::DecFailed => { + let r1_eb = messages.previous_echo_broadcast::>(1)?; + let r2_ebs = messages.combined_echos::>(2)?; + let r2_nb = messages.previous_normal_broadcast::>(2)?; + let r2_eb = messages.previous_echo_broadcast::>(2)?; + let r3_nb = messages.previous_normal_broadcast::>(3)?; + let r6_eb = messages.echo_broadcast()?; // Calculate `\hat{D}_j` where `j = guilty_party`. // `\hat{D}_j = sum_{l != j}(\hat{D}_{l,j} + \hat{F}_{j,l}) @@ -792,12 +839,15 @@ impl ProtocolError for InteractiveSigningError // r2_ebs[i], i != j: contains \hat{D}_{l,i}, \hat{F}_{l,i} for l != i // => \hat{F}_{j,l} = r2_ebs[l].hat_cap_fs[j] - let public_aux = &associated_data.aux.as_map().try_get("aux infos", guilty_party)?; + let public_aux = &shared_data + .aux + .as_map() + .get_or_invalid_evidence("aux infos", guilty_party)?; let pk = public_aux.paillier_pk.clone().into_precomputed(); let rp = public_aux.rp_params.to_precomputed(); let aux = (&epid, guilty_party); - let ids = associated_data + let ids = shared_data .aux .as_map() .keys() @@ -805,27 +855,34 @@ impl ProtocolError for InteractiveSigningError .without(&guilty_party); let hat_cap_d = sum_non_empty( - ids.iter() - .map(|id| Ok(r2_nb.hat_cap_ds.try_get("`\\hat{D}` map", id)?.to_precomputed(&pk))), - ProtocolValidationError::InvalidEvidence("There must be at least two parties".into()), + ids.iter().map(|id| { + Ok(r2_nb + .hat_cap_ds + .get_or_invalid_evidence("`\\hat{D}` map", id)? + .to_precomputed(&pk)) + }), + EvidenceError::InvalidEvidence("There must be at least two parties".into()), )? + sum_non_empty( ids.iter().map(|id| { Ok(r2_ebs - .try_get("Round 2 echo broadcasts", id)? + .get_or_invalid_evidence("Round 2 echo broadcasts", id)? .hat_cap_fs - .try_get("`\\hat{F}` map", guilty_party)? + .get_or_invalid_evidence("`\\hat{F}` map", guilty_party)? .to_precomputed(&pk)) }), - ProtocolValidationError::InvalidEvidence("There must be at least two parties".into()), + EvidenceError::InvalidEvidence("There must be at least two parties".into()), )?; let cap_k = r1_eb.cap_k.to_precomputed(&pk); let total_cap_gamma = r2_eb.cap_gamma + r2_ebs.values().map(|eb| eb.cap_gamma).sum(); - let cap_x = associated_data.shares.as_map().try_get("`X` map", guilty_party)?; + let cap_x = shared_data + .shares + .as_map() + .get_or_invalid_evidence("`X` map", guilty_party)?; - verify_that(!r5_eb.psi_star.verify( + verify_that(!r6_eb.hat_psi_star.verify( DecPublicInputs { pk0: &pk, cap_k: &cap_k, @@ -833,50 +890,48 @@ impl ProtocolError for InteractiveSigningError cap_d: &hat_cap_d, cap_s: &r3_nb.cap_s, cap_g: &total_cap_gamma, - num_parties: associated_data.aux.num_parties(), + num_parties: shared_data.aux.num_parties(), }, &rp, &aux, )) } - Error::R6WrongIdsPsi => { + R6ErrorEnum::WrongIdsPsi => { // TODO (#188): currently unreachable from tests - let r6_nb = message - .normal_broadcast - .deserialize::>(format)?; - let expected_ids = associated_data + let r6_eb = messages.echo_broadcast()?; + let expected_ids = shared_data .aux .as_map() .keys() .collect::>() .without(&guilty_party); - verify_that(r6_nb.hat_psis.keys().collect::>() != expected_ids) + verify_that(r6_eb.hat_psis.keys().collect::>() != expected_ids) } - Error::R6AffGStarFailed { failed_for } => { + R6ErrorEnum::AffGStarFailed { failed_for } => { // TODO (#188): currently unreachable from tests - let r1_ebs = combined_echos - .get_round(1)? - .deserialize_all::>(format)?; - let r2_ebs = combined_echos - .get_round(2)? - .deserialize_all::>(format)?; - let r2_nb = previous_messages - .get_round(2)? - .normal_broadcast - .deserialize::>(format)?; - let r6_eb = message - .echo_broadcast - .deserialize::>(format)?; - - let failed_for_aux = &associated_data.aux.as_map().try_get("aux infos", failed_for)?; - let guilty_party_aux = &associated_data.aux.as_map().try_get("aux infos", guilty_party)?; + let r1_ebs = messages.combined_echos::>(1)?; + let r2_ebs = messages.combined_echos::>(2)?; + let r2_nb = messages.previous_normal_broadcast::>(2)?; + let r6_eb = messages.echo_broadcast()?; + + let failed_for_aux = &shared_data + .aux + .as_map() + .get_or_invalid_evidence("aux infos", failed_for)?; + let guilty_party_aux = &shared_data + .aux + .as_map() + .get_or_invalid_evidence("aux infos", guilty_party)?; let failed_for_pk = failed_for_aux.paillier_pk.clone().into_precomputed(); let guilty_party_pk = guilty_party_aux.paillier_pk.clone().into_precomputed(); let aux = (&epid, guilty_party); - let cap_x = associated_data.shares.as_map().try_get("shares", failed_for)?; + let cap_x = shared_data + .shares + .as_map() + .get_or_invalid_evidence("shares", failed_for)?; // l = failed_for // j = guilty_party @@ -884,19 +939,21 @@ impl ProtocolError for InteractiveSigningError let hat_cap_d = r2_nb .hat_cap_ds - .try_get("`\\hat{D}` map", failed_for)? + .get_or_invalid_evidence("`\\hat{D}` map", failed_for)? .to_precomputed(&failed_for_pk); let cap_k = r1_ebs - .try_get("Round 1 echo broadcasts", failed_for)? + .get_or_invalid_evidence("Round 1 echo broadcasts", failed_for)? .cap_k .to_precomputed(&failed_for_pk); let hat_cap_f = r2_ebs - .try_get("Round 2 echo broadcasts", failed_for)? + .get_or_invalid_evidence("Round 2 echo broadcasts", failed_for)? .hat_cap_fs - .try_get("`\\hat{F}` map", guilty_party)? + .get_or_invalid_evidence("`\\hat{F}` map", guilty_party)? .to_precomputed(&guilty_party_pk); - let hat_psi = r6_eb.hat_psis.try_get("`\\hat{\\psi}` map", failed_for)?; + let hat_psi = r6_eb + .hat_psis + .get_or_invalid_evidence("`\\hat{\\psi}` map", failed_for)?; verify_that(!hat_psi.verify( AffGStarPublicInputs { @@ -914,6 +971,63 @@ impl ProtocolError for InteractiveSigningError } } +/// Associated data for InteractiveSigning protocol. +#[derive(Debug, Clone)] +pub struct InteractiveSigningSharedData { + /// Public shares of all participating nodes. + pub shares: PublicKeyShares, + /// Auxiliary data of all participating nodes. + pub aux: PublicAuxInfos, + /// The message to be signed. + pub message: PrehashedMessage, +} + +impl InteractiveSigningSharedData { + /// Creates the associated data for evidence verification of InteractiveSigning. + pub fn new( + message: PrehashedMessage, + public_key_shares: PublicKeyShares, + public_aux_infos: PublicAuxInfos, + ) -> Result { + let key_share_keys = public_key_shares.as_map().keys().collect::>(); + let aux_info_keys = public_aux_infos.as_map().keys().collect::>(); + + if key_share_keys != aux_info_keys { + return Err(LocalError::new( + "The key share and the auxiliary info must have information for the same set of parties", + )); + } + + Ok(Self { + shares: public_key_shares, + aux: public_aux_infos, + message, + }) + } +} + +/// The epoch identifier (see Remark 4.1 in the paper). +/// +/// The epoch identifier is tied to the key-refresh epoch and the auxiliary key material of the parties for that epoch. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct Epid(HashOutput); + +impl Epid { + fn new( + shared_randomness: &[u8], + shared_data: &InteractiveSigningSharedData, + ) -> Self { + let digest = Hasher::::new_with_dst(b"EPID"); + let digest = chain_scheme_params::(digest); + let digest = digest + .chain(&shared_randomness) + .chain(&shared_data.shares) + .chain(&shared_data.aux); + + Self(digest.finalize(P::SECURITY_BITS)) + } +} + /// An entry point for the [`InteractiveSigningProtocol`]. #[derive(Debug, Clone)] pub struct InteractiveSigning @@ -967,7 +1081,7 @@ impl EntryPoint for InteractiveSigning fn make_round( self, - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, shared_randomness: &[u8], id: &Id, ) -> Result, LocalError> { @@ -985,7 +1099,7 @@ impl EntryPoint for InteractiveSigning let epid = Epid::new::( shared_randomness, - &InteractiveSigningAssociatedData { + &InteractiveSigningSharedData { shares: key_share.public().clone(), aux: aux_info.public().clone(), message: self.message.clone(), @@ -1027,7 +1141,7 @@ impl EntryPoint for InteractiveSigning cap_b2, }; - Ok(BoxedRound::new_dynamic(Round1 { + Ok(BoxedRound::new(Round1 { context: Context { scalar_message: Scalar::from_reduced_bytes(self.message), epid, @@ -1075,16 +1189,16 @@ where Id: PartyId, { pub fn public_share(&self, i: &Id) -> Result<&Point

, LocalError> { - self.key_share.public_shares().safe_get("public share", i) + self.key_share.public_shares().get_or_local_error("public share", i) } pub fn public_aux(&self, i: &Id) -> Result<&PublicAuxInfoPrecomputed

, LocalError> { - self.aux_info.public_aux.safe_get("public aux", i) + self.aux_info.public_aux.get_or_local_error("public aux", i) } } #[derive(Debug)] -struct Round1 +pub(super) struct Round1 where P: SchemeParams, Id: PartyId, @@ -1107,7 +1221,7 @@ pub(super) struct Round1EchoBroadcast { #[derive(Clone)] #[derive_where::derive_where(Serialize, Deserialize)] -struct Round1DirectMessage { +pub(super) struct Round1DirectMessage { psi0: EncElgProof

, psi1: EncElgProof

, } @@ -1125,6 +1239,15 @@ pub(super) struct Round1Payload { impl Round for Round1 { type Protocol = InteractiveSigningProtocol; + type DirectMessage = Round1DirectMessage

; + type NormalBroadcast = NoMessage; + type EchoBroadcast = Round1EchoBroadcast

; + + type Payload = Round1Payload

; + type Artifact = (); + + type ProtocolError = R1Error; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear(1) } @@ -1133,20 +1256,16 @@ impl Round for Round1 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_echo_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - EchoBroadcast::new(format, self.r1_echo_broadcast.clone()) + fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(self.r1_echo_broadcast.clone()) } fn make_direct_message( &self, - rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, + rng: &mut impl CryptoRngCore, + destination: &Id, - ) -> Result<(DirectMessage, Option), LocalError> { + ) -> Result<(Self::DirectMessage, Self::Artifact), LocalError> { let aux = (&self.context.epid, &self.context.my_id); let pk = self.context.aux_info.secret_aux.paillier_sk.public_key(); @@ -1170,7 +1289,7 @@ impl Round for Round1 { .context .aux_info .public_aux - .safe_get("public aux", destination)? + .get_or_local_error("public aux", destination)? .rp_params, &aux, ); @@ -1193,27 +1312,22 @@ impl Round for Round1 { .context .aux_info .public_aux - .safe_get("public aux", destination)? + .get_or_local_error("public aux", destination)? .rp_params, &aux, ); - Ok(( - DirectMessage::new(format, Round1DirectMessage::

{ psi0, psi1 })?, - None, - )) + Ok((Round1DirectMessage::

{ psi0, psi1 }, ())) } fn receive_message( &self, - format: &BoxedFormat, - from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.normal_broadcast.assert_is_none()?; - let direct_message = message.direct_message.deserialize::>(format)?; - let echo_broadcast = message.echo_broadcast.deserialize::>(format)?; + from: &Id, + message: ProtocolMessage, + ) -> Result> { + let direct_message = message.direct_message; + let echo_broadcast = message.echo_broadcast; let aux = (&self.context.epid, from); @@ -1235,7 +1349,10 @@ impl Round for Round1 { &public_aux.rp_params, &aux, ) { - return Err(ReceiveError::protocol(Error::R1EncElg0Failed.into())); + return Err(ReceiveError::Protocol(R1Error { + error: R1ErrorEnum::EncElg0Failed, + phantom: PhantomData, + })); } if !direct_message.psi1.verify( @@ -1249,26 +1366,29 @@ impl Round for Round1 { &public_aux.rp_params, &aux, ) { - return Err(ReceiveError::protocol(Error::R1EncElg1Failed.into())); + return Err(ReceiveError::Protocol(R1Error { + error: R1ErrorEnum::EncElg1Failed, + phantom: PhantomData, + })); } - Ok(Payload::new(Round1Payload::

{ + Ok(Round1Payload::

{ cap_k, cap_a1: echo_broadcast.cap_a1, cap_a2: echo_broadcast.cap_a2, cap_b1: echo_broadcast.cap_b1, cap_b2: echo_broadcast.cap_b2, cap_y: echo_broadcast.cap_y, - })) + }) } fn finalize( - self: Box, - rng: &mut dyn CryptoRngCore, - payloads: BTreeMap, - _artifacts: BTreeMap, + self, + rng: &mut impl CryptoRngCore, + payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { - let mut payloads = payloads.downcast_all::>()?; + let mut payloads = payloads; let pk = self.context.aux_info.secret_aux.paillier_sk.public_key(); let my_payload = Round1Payload { @@ -1358,12 +1478,12 @@ impl Round for Round1 { for id in self.context.other_ids.iter() { let rp = &self.context.public_aux(id)?.rp_params; - let r1_payload = payloads.safe_get("Round 1 payloads", id)?; + let r1_payload = payloads.get_or_local_error("Round 1 payloads", id)?; let target_pk = &self.context.public_aux(id)?.paillier_pk; - let beta = betas.safe_get("`beta` map", id)?; - let r = rs.safe_get("`r` map", id)?; - let s = ss.safe_get("`s` map", id)?; + let beta = betas.get_or_local_error("`beta` map", id)?; + let r = rs.get_or_local_error("`r` map", id)?; + let s = ss.get_or_local_error("`s` map", id)?; let cap_f = Ciphertext::new_with_randomizer(pk, beta, r); let cap_d = &r1_payload.cap_k * &gamma + Ciphertext::new_with_randomizer(target_pk, &-beta, s); @@ -1387,9 +1507,9 @@ impl Round for Round1 { &aux, ); - let hat_beta = hat_betas.safe_get("`\\hat{beta}` map", id)?; - let hat_r = hat_rs.safe_get("`\\hat{r}` map", id)?; - let hat_s = hat_ss.safe_get("`\\hat{s}` map", id)?; + let hat_beta = hat_betas.get_or_local_error("`\\hat{beta}` map", id)?; + let hat_r = hat_rs.get_or_local_error("`\\hat{r}` map", id)?; + let hat_s = hat_ss.get_or_local_error("`\\hat{s}` map", id)?; let hat_cap_f = Ciphertext::new_with_randomizer(pk, hat_beta, hat_r); let hat_cap_d = &r1_payload.cap_k * &x + Ciphertext::new_with_randomizer(target_pk, &-hat_beta, hat_s); @@ -1442,7 +1562,7 @@ impl Round for Round1 { hat_psis, }; - Ok(FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(next_round))) + Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(next_round))) } } @@ -1485,7 +1605,7 @@ pub(super) struct Round2EchoBroadcast { pub(super) hat_cap_fs: SerializableMap>, } -struct Round2Payload { +pub(super) struct Round2Payload { cap_gamma: Point

, alpha: Secret>, hat_alpha: Secret>, @@ -1498,6 +1618,15 @@ struct Round2Payload { impl Round for Round2 { type Protocol = InteractiveSigningProtocol; + type DirectMessage = NoMessage; + type NormalBroadcast = Round2NormalBroadcast; + type EchoBroadcast = Round2EchoBroadcast; + + type Payload = Round2Payload; + type Artifact = NoArtifact; + + type ProtocolError = R2Error; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear(2) } @@ -1506,51 +1635,32 @@ impl Round for Round2 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_normal_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - NormalBroadcast::new( - format, - Round2NormalBroadcast:: { - cap_ds: self.cap_ds.map_values_ref(|cap_d| cap_d.to_wire()).into(), - hat_cap_ds: self.hat_cap_ds.map_values_ref(|hat_cap_d| hat_cap_d.to_wire()).into(), - psi_elog: self.psi_elog.clone(), - psis: self.psis.clone().into(), - hat_psis: self.hat_psis.clone().into(), - }, - ) + fn make_normal_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round2NormalBroadcast:: { + cap_ds: self.cap_ds.map_values_ref(|cap_d| cap_d.to_wire()).into(), + hat_cap_ds: self.hat_cap_ds.map_values_ref(|hat_cap_d| hat_cap_d.to_wire()).into(), + psi_elog: self.psi_elog.clone(), + psis: self.psis.clone().into(), + hat_psis: self.hat_psis.clone().into(), + }) } - fn make_echo_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - EchoBroadcast::new( - format, - Round2EchoBroadcast:: { - cap_gamma: self.cap_gamma, - cap_fs: self.cap_fs.map_values_ref(|cap_f| cap_f.to_wire()).into(), - hat_cap_fs: self.hat_cap_fs.map_values_ref(|hat_cap_f| hat_cap_f.to_wire()).into(), - }, - ) + fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round2EchoBroadcast:: { + cap_gamma: self.cap_gamma, + cap_fs: self.cap_fs.map_values_ref(|cap_f| cap_f.to_wire()).into(), + hat_cap_fs: self.hat_cap_fs.map_values_ref(|hat_cap_f| hat_cap_f.to_wire()).into(), + }) } fn receive_message( &self, - format: &BoxedFormat, + from: &Id, - message: ProtocolMessage, - ) -> Result> { - let echo_broadcast = message - .echo_broadcast - .deserialize::>(format)?; - let normal_broadcast = message - .normal_broadcast - .deserialize::>(format)?; - message.direct_message.assert_is_none()?; + message: ProtocolMessage, + ) -> Result> { + let echo_broadcast = message.echo_broadcast; + let normal_broadcast = message.normal_broadcast; let aux = (&self.context.epid, from); let from_pk = &self.context.public_aux(from)?.paillier_pk; @@ -1558,27 +1668,42 @@ impl Round for Round2 { let expected_ids = self.context.all_ids.clone().without(from); if normal_broadcast.cap_ds.keys().cloned().collect::>() != expected_ids { - return Err(ReceiveError::protocol(Error::R2WrongIdsD.into())); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::WrongIdsD, + phantom: PhantomData, + })); } if echo_broadcast.cap_fs.keys().cloned().collect::>() != expected_ids { - return Err(ReceiveError::protocol(Error::R2WrongIdsF.into())); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::WrongIdsF, + phantom: PhantomData, + })); } if normal_broadcast.psis.keys().cloned().collect::>() != expected_ids { - return Err(ReceiveError::protocol(Error::R2WrongIdsPsi.into())); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::WrongIdsPsi, + phantom: PhantomData, + })); } - let sender_payload = self.r1_payloads.safe_get("Round 1 payloads", from)?; + let sender_payload = self.r1_payloads.get_or_local_error("Round 1 payloads", from)?; let mut cap_ds = BTreeMap::new(); let mut cap_fs = BTreeMap::new(); for (id, psi) in normal_broadcast.psis.iter() { let rp = &self.context.public_aux(id)?.rp_params; let for_pk = &self.context.public_aux(id)?.paillier_pk; - let for_payload = self.r1_payloads.safe_get("Round 1 payloads", id)?; - let cap_d = normal_broadcast.cap_ds.safe_get("`D` map", id)?.to_precomputed(for_pk); - let cap_f = echo_broadcast.cap_fs.safe_get("`F` map", id)?.to_precomputed(from_pk); + let for_payload = self.r1_payloads.get_or_local_error("Round 1 payloads", id)?; + let cap_d = normal_broadcast + .cap_ds + .get_or_local_error("`D` map", id)? + .to_precomputed(for_pk); + let cap_f = echo_broadcast + .cap_fs + .get_or_local_error("`F` map", id)? + .to_precomputed(from_pk); if !psi.verify( AffGPublicInputs { @@ -1592,9 +1717,10 @@ impl Round for Round2 { rp, &aux, ) { - return Err(ReceiveError::protocol( - Error::R2AffGPsiFailed { failed_for: id.clone() }.into(), - )); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::AffGPsiFailed { failed_for: id.clone() }, + phantom: PhantomData, + })); } cap_ds.insert(id.clone(), cap_d); @@ -1606,14 +1732,14 @@ impl Round for Round2 { for (id, hat_psi) in normal_broadcast.hat_psis.iter() { let rp = &self.context.public_aux(id)?.rp_params; let for_pk = &self.context.public_aux(id)?.paillier_pk; - let for_payload = self.r1_payloads.safe_get("Round 1 payloads", id)?; + let for_payload = self.r1_payloads.get_or_local_error("Round 1 payloads", id)?; let hat_cap_d = normal_broadcast .hat_cap_ds - .safe_get("`D` map", id)? + .get_or_local_error("`D` map", id)? .to_precomputed(for_pk); let hat_cap_f = echo_broadcast .hat_cap_fs - .safe_get("`F` map", id)? + .get_or_local_error("`F` map", id)? .to_precomputed(from_pk); let cap_x = self.context.public_share(from)?; @@ -1630,9 +1756,10 @@ impl Round for Round2 { rp, &aux, ) { - return Err(ReceiveError::protocol( - Error::R2AffGHatPsiFailed { failed_for: id.clone() }.into(), - )); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::AffGHatPsiFailed { failed_for: id.clone() }, + phantom: PhantomData, + })); } hat_cap_ds.insert(id.clone(), hat_cap_d); @@ -1649,20 +1776,23 @@ impl Round for Round2 { }, &aux, ) { - return Err(ReceiveError::protocol(Error::R2ElogFailed.into())); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::ElogFailed, + phantom: PhantomData, + })); } let alpha_uint = cap_ds - .safe_get("`D` map", &self.context.my_id)? + .get_or_local_error("`D` map", &self.context.my_id)? .decrypt(&self.context.aux_info.secret_aux.paillier_sk); let hat_alpha_uint = hat_cap_ds - .safe_get("`\\hat{D}` map", &self.context.my_id)? + .get_or_local_error("`\\hat{D}` map", &self.context.my_id)? .decrypt(&self.context.aux_info.secret_aux.paillier_sk); let alpha = secret_scalar_from_signed::

(&alpha_uint); let hat_alpha = secret_scalar_from_signed::

(&hat_alpha_uint); - Ok(Payload::new(Round2Payload:: { + Ok(Round2Payload:: { cap_gamma: echo_broadcast.cap_gamma, alpha, hat_alpha, @@ -1670,17 +1800,15 @@ impl Round for Round2 { cap_fs, hat_cap_ds, hat_cap_fs, - })) + }) } fn finalize( - self: Box, - rng: &mut dyn CryptoRngCore, - payloads: BTreeMap, - _artifacts: BTreeMap, + self, + rng: &mut impl CryptoRngCore, + payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { - let payloads = payloads.downcast_all::>()?; - let mut cap_gammas = payloads.map_values_ref(|payload| payload.cap_gamma); cap_gammas.insert(self.context.my_id.clone(), self.cap_gamma); @@ -1700,7 +1828,9 @@ impl Round for Round2 { let cap_s = cap_gamma_combined * χ let aux = (&self.context.epid, &self.context.my_id); - let my_r1_payload = self.r1_payloads.safe_get("Round 1 payloads", &self.context.my_id)?; + let my_r1_payload = self + .r1_payloads + .get_or_local_error("Round 1 payloads", &self.context.my_id)?; let psi_prime = ElogProof::new( rng, ElogSecretInputs { @@ -1795,7 +1925,7 @@ impl Round for Round2 { hat_ss: self.hat_ss, }; - Ok(FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(next_round))) + Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(next_round))) } } @@ -1844,6 +1974,15 @@ pub(super) struct Round3Payload { impl Round for Round3 { type Protocol = InteractiveSigningProtocol; + type DirectMessage = NoMessage; + type NormalBroadcast = Round3NormalBroadcast

; + type EchoBroadcast = Round3EchoBroadcast

; + + type Payload = Round3Payload

; + type Artifact = NoArtifact; + + type ProtocolError = R3Error; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear(3).with_children([5, 6].into()) } @@ -1852,36 +1991,25 @@ impl Round for Round3 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_echo_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - EchoBroadcast::new(format, self.r3_echo_broadcast.clone()) + fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(self.r3_echo_broadcast.clone()) } - fn make_normal_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - NormalBroadcast::new(format, self.r3_normal_broadcast.clone()) + fn make_normal_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(self.r3_normal_broadcast.clone()) } fn receive_message( &self, - format: &BoxedFormat, + from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.direct_message.assert_is_none()?; - let echo_broadcast = message.echo_broadcast.deserialize::>(format)?; - let normal_broadcast = message - .normal_broadcast - .deserialize::>(format)?; + message: ProtocolMessage, + ) -> Result> { + let echo_broadcast = message.echo_broadcast; + let normal_broadcast = message.normal_broadcast; let aux = (&self.context.epid, from); - let r1_payload = self.r1_payloads.safe_get("Round 1 payload", from)?; + let r1_payload = self.r1_payloads.get_or_local_error("Round 1 payload", from)?; if !normal_broadcast.psi_prime.verify( ElogPublicInputs { @@ -1893,23 +2021,26 @@ impl Round for Round3 { }, &aux, ) { - return Err(ReceiveError::protocol(Error::R3ElogFailed.into())); + return Err(ReceiveError::Protocol(R3Error { + error: R3ErrorEnum::ElogFailed, + phantom: PhantomData, + })); } - Ok(Payload::new(Round3Payload { + Ok(Round3Payload { delta: echo_broadcast.delta, cap_delta: normal_broadcast.cap_delta, cap_s: normal_broadcast.cap_s, - })) + }) } fn finalize( - self: Box, - _rng: &mut dyn CryptoRngCore, - payloads: BTreeMap, - _artifacts: BTreeMap, + self, + _rng: &mut impl CryptoRngCore, + payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { - let mut payloads = payloads.downcast_all::>()?; + let mut payloads = payloads; let my_payload = Round3Payload { delta: self.r3_echo_broadcast.delta, cap_delta: self.r3_normal_broadcast.cap_delta, @@ -1941,7 +2072,7 @@ impl Round for Round3 { cap_fs: self.cap_fs, }; - return Ok(FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(next_round))); + return Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(next_round))); } if cap_s != cap_x * delta_combined { @@ -1962,7 +2093,7 @@ impl Round for Round3 { hat_cap_fs: self.hat_cap_fs, }; - return Ok(FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(next_round))); + return Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(next_round))); } // Intentionally making delta = 0 would require coordination from all the participants, @@ -1982,7 +2113,7 @@ impl Round for Round3 { let sigma = *(&presigning_data.tilde_k * self.context.scalar_message + &presigning_data.tilde_chi * nonce) .expose_secret(); - Ok(FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(Round4 { + Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(Round4 { context: self.context, presigning_data, sigma, @@ -1991,7 +2122,7 @@ impl Round for Round3 { } #[derive(Debug)] -struct Round4 { +pub(super) struct Round4 { context: Context, presigning_data: PresigningData, sigma: Scalar

, @@ -2003,13 +2134,22 @@ pub(super) struct Round4NormalBroadcast { pub(crate) sigma: Scalar

, } -struct Round4Payload { +pub(super) struct Round4Payload { sigma: Scalar

, } impl Round for Round4 { type Protocol = InteractiveSigningProtocol; + type DirectMessage = NoMessage; + type NormalBroadcast = Round4NormalBroadcast

; + type EchoBroadcast = NoMessage; + + type Payload = Round4Payload

; + type Artifact = NoArtifact; + + type ProtocolError = R4Error; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear_terminating(4).with_siblings([5, 6].into()) } @@ -2018,51 +2158,47 @@ impl Round for Round4 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_normal_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - NormalBroadcast::new(format, Round4NormalBroadcast { sigma: self.sigma }) + fn make_normal_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round4NormalBroadcast { sigma: self.sigma }) } fn receive_message( &self, - format: &BoxedFormat, + from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.echo_broadcast.assert_is_none()?; - message.direct_message.assert_is_none()?; - let normal_broadcast = message - .normal_broadcast - .deserialize::>(format)?; + message: ProtocolMessage, + ) -> Result> { + let normal_broadcast = message.normal_broadcast; let nonce = self.presigning_data.cap_gamma_combined.x_coordinate(); let tilde_cap_delta = self .presigning_data .tilde_cap_deltas - .safe_get("`\\tilde{Delta}` map", from)?; - let tilde_cap_s = self.presigning_data.tilde_cap_ss.safe_get("`\\tilde{S}` map", from)?; + .get_or_local_error("`\\tilde{Delta}` map", from)?; + let tilde_cap_s = self + .presigning_data + .tilde_cap_ss + .get_or_local_error("`\\tilde{S}` map", from)?; if self.presigning_data.cap_gamma_combined * normal_broadcast.sigma != tilde_cap_delta * self.context.scalar_message + tilde_cap_s * nonce { - return Err(ReceiveError::protocol(Error::R4InvalidSignatureShare.into())); + return Err(ReceiveError::Protocol(R4Error { + error: R4ErrorEnum::InvalidSignatureShare, + phantom: PhantomData, + })); } - Ok(Payload::new(Round4Payload { + Ok(Round4Payload { sigma: normal_broadcast.sigma, - })) + }) } fn finalize( - self: Box, - _rng: &mut dyn CryptoRngCore, - payloads: BTreeMap, - _artifacts: BTreeMap, + self, + _rng: &mut impl CryptoRngCore, + payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { - let payloads = payloads.downcast_all::>()?; - let assembled_sigma = payloads.values().map(|payload| payload.sigma).sum::>() + self.sigma; let signature = RecoverableSignature::from_scalars( @@ -2076,7 +2212,7 @@ impl Round for Round4 { return Ok(FinalizeOutcome::Result(signature)); } - Err(LocalError::new("Failed!")) + Err(LocalError::new("The execution should not have reached this round")) } } @@ -2103,6 +2239,15 @@ pub(super) struct Round5EchoBroadcast { impl Round for Round5 { type Protocol = InteractiveSigningProtocol; + type DirectMessage = NoMessage; + type NormalBroadcast = NoMessage; + type EchoBroadcast = Round5EchoBroadcast; + + type Payload = (); + type Artifact = NoArtifact; + + type ProtocolError = R5Error; + fn transition_info(&self) -> TransitionInfo { TransitionInfo { id: 5.into(), @@ -2117,11 +2262,7 @@ impl Round for Round5 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_echo_broadcast( - &self, - rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { + fn make_echo_broadcast(&self, rng: &mut impl CryptoRngCore) -> Result { let my_id = self.context.my_id.clone(); let aux = (&self.context.epid, &my_id); let pk = self.context.aux_info.secret_aux.paillier_sk.public_key(); @@ -2131,15 +2272,15 @@ impl Round for Round5 { let cap_d = sum_non_empty_ref( ids.iter() - .map(|id| self.cap_ds.safe_get("`D` map", &(my_id.clone(), id.clone()))), + .map(|id| self.cap_ds.get_or_local_error("`D` map", &(my_id.clone(), id.clone()))), LocalError::new("There must be at least two parties"), )? + sum_non_empty_ref( ids.iter() - .map(|id| self.cap_fs.safe_get("`F` map", &(id.clone(), my_id.clone()))), + .map(|id| self.cap_fs.get_or_local_error("`F` map", &(id.clone(), my_id.clone()))), LocalError::new("There must be at least two parties"), )?; - let cap_k = self.cap_ks.safe_get("`K` map", &self.context.my_id)?; + let cap_k = self.cap_ks.get_or_local_error("`K` map", &self.context.my_id)?; let gamma = secret_signed_from_scalar::

(&self.context.gamma); let full_ciphertext = cap_k * &gamma + &cap_d; @@ -2159,7 +2300,7 @@ impl Round for Round5 { .ok_or_else(|| LocalError::new("`delta` is not in the expected range"))?; // This is equal to what we would get from reducing `delta_signed` to Scalar. - let delta = self.deltas.safe_get("`delta` map", &self.context.my_id)?; + let delta = self.deltas.get_or_local_error("`delta` map", &self.context.my_id)?; let psi_star = DecProof::new( rng, @@ -2188,17 +2329,21 @@ impl Round for Round5 { rng, AffGStarSecretInputs { x: &gamma, - y: self.betas.safe_get("`beta` map", id)?, - rho: self.ss.safe_get("`s` map", id)?, - mu: self.rs.safe_get("`r` map", id)?, + y: self.betas.get_or_local_error("`beta` map", id)?, + rho: self.ss.get_or_local_error("`s` map", id)?, + mu: self.rs.get_or_local_error("`r` map", id)?, }, AffGStarPublicInputs { pk0: &self.context.public_aux(id)?.paillier_pk, pk1: pk, - cap_c: self.cap_ks.safe_get("`K` map", id)?, - cap_d: self.cap_ds.safe_get("`D` map", &(id.clone(), my_id.clone()))?, - cap_y: self.cap_fs.safe_get("`F` map", &(id.clone(), my_id.clone()))?, - cap_x: self.cap_gammas.safe_get("`Gamma` map", &my_id)?, + cap_c: self.cap_ks.get_or_local_error("`K` map", id)?, + cap_d: self + .cap_ds + .get_or_local_error("`D` map", &(id.clone(), my_id.clone()))?, + cap_y: self + .cap_fs + .get_or_local_error("`F` map", &(id.clone(), my_id.clone()))?, + cap_x: self.cap_gammas.get_or_local_error("`Gamma` map", &my_id)?, }, &aux, ); @@ -2206,20 +2351,16 @@ impl Round for Round5 { psis.insert(id.clone(), psi); } - EchoBroadcast::new(format, Round5EchoBroadcast:: { psi_star, psis }) + Ok(Round5EchoBroadcast:: { psi_star, psis }) } fn receive_message( &self, - format: &BoxedFormat, + from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.normal_broadcast.assert_is_none()?; - message.direct_message.assert_is_none()?; - let echo_broadcast = message - .echo_broadcast - .deserialize::>(format)?; + message: ProtocolMessage, + ) -> Result> { + let echo_broadcast = message.echo_broadcast; let my_id = self.context.my_id.clone(); let aux = (&self.context.epid, from); @@ -2231,33 +2372,39 @@ impl Round for Round5 { let cap_d = sum_non_empty_ref( ids.iter() - .map(|id| self.cap_ds.safe_get("`D` map", &(from.clone(), id.clone()))), + .map(|id| self.cap_ds.get_or_local_error("`D` map", &(from.clone(), id.clone()))), LocalError::new("There must be at least two parties"), )? + sum_non_empty_ref( ids.iter() - .map(|id| self.cap_fs.safe_get("`F` map", &(id.clone(), from.clone()))), + .map(|id| self.cap_fs.get_or_local_error("`F` map", &(id.clone(), from.clone()))), LocalError::new("There must be at least two parties"), )?; if !echo_broadcast.psi_star.verify( DecPublicInputs { pk0: sender_pk, - cap_k: self.cap_ks.safe_get("`K` map", from)?, - cap_x: self.cap_gammas.safe_get("`Gamma` map", from)?, + cap_k: self.cap_ks.get_or_local_error("`K` map", from)?, + cap_x: self.cap_gammas.get_or_local_error("`Gamma` map", from)?, cap_d: &cap_d, - cap_s: &self.deltas.safe_get("`delta` map", from)?.mul_by_generator(), + cap_s: &self.deltas.get_or_local_error("`delta` map", from)?.mul_by_generator(), cap_g: &Point::generator(), num_parties: self.context.all_ids.len(), }, sender_rp, &aux, ) { - return Err(ReceiveError::protocol(Error::R5DecFailed.into())); + return Err(ReceiveError::Protocol(R5Error { + error: R5ErrorEnum::DecFailed, + phantom: PhantomData, + })); } let expected_ids = self.context.all_ids.clone().without(from); if echo_broadcast.psis.keys().cloned().collect::>() != expected_ids { - return Err(ReceiveError::protocol(Error::R5WrongIdsPsi.into())); + return Err(ReceiveError::Protocol(R5Error { + error: R5ErrorEnum::WrongIdsPsi, + phantom: PhantomData, + })); } for (id, psi) in echo_broadcast.psis.iter() { @@ -2270,27 +2417,28 @@ impl Round for Round5 { AffGStarPublicInputs { pk0: pk, pk1: sender_pk, - cap_c: self.cap_ks.safe_get("`K` map", id)?, - cap_d: self.cap_ds.safe_get("`D` map", &(id.clone(), from.clone()))?, - cap_y: self.cap_fs.safe_get("`F` map", &(id.clone(), from.clone()))?, - cap_x: self.cap_gammas.safe_get("`Gamma` map", from)?, + cap_c: self.cap_ks.get_or_local_error("`K` map", id)?, + cap_d: self.cap_ds.get_or_local_error("`D` map", &(id.clone(), from.clone()))?, + cap_y: self.cap_fs.get_or_local_error("`F` map", &(id.clone(), from.clone()))?, + cap_x: self.cap_gammas.get_or_local_error("`Gamma` map", from)?, }, &aux, ) { - return Err(ReceiveError::protocol( - Error::R5AffGStarFailed { failed_for: id.clone() }.into(), - )); + return Err(ReceiveError::Protocol(R5Error { + error: R5ErrorEnum::AffGStarFailed { failed_for: id.clone() }, + phantom: PhantomData, + })); } } - Ok(Payload::empty()) + Ok(()) } fn finalize( - self: Box, - _rng: &mut dyn CryptoRngCore, - _payloads: BTreeMap, - _artifacts: BTreeMap, + self, + _rng: &mut impl CryptoRngCore, + _payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { Err(LocalError::new( "One of the messages should have been missing or invalid", @@ -2321,6 +2469,15 @@ pub(super) struct Round6EchoBroadcast { impl Round for Round6 { type Protocol = InteractiveSigningProtocol; + type DirectMessage = NoMessage; + type NormalBroadcast = NoMessage; + type EchoBroadcast = Round6EchoBroadcast; + + type Payload = (); + type Artifact = NoArtifact; + + type ProtocolError = R6Error; + fn transition_info(&self) -> TransitionInfo { TransitionInfo { id: 6.into(), @@ -2335,11 +2492,7 @@ impl Round for Round6 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_echo_broadcast( - &self, - rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { + fn make_echo_broadcast(&self, rng: &mut impl CryptoRngCore) -> Result { let my_id = self.context.my_id.clone(); let aux = (&self.context.epid, &my_id); let pk = self.context.aux_info.secret_aux.paillier_sk.public_key(); @@ -2348,16 +2501,20 @@ impl Round for Round6 { let ids = self.context.other_ids.clone(); let hat_cap_d = sum_non_empty_ref( - ids.iter() - .map(|id| self.hat_cap_ds.safe_get("`\\hat{D}` map", &(my_id.clone(), id.clone()))), + ids.iter().map(|id| { + self.hat_cap_ds + .get_or_local_error("`\\hat{D}` map", &(my_id.clone(), id.clone())) + }), LocalError::new("There must be at least two parties"), )? + sum_non_empty_ref( - ids.iter() - .map(|id| self.hat_cap_fs.safe_get("`\\hat{F}` map", &(id.clone(), my_id.clone()))), + ids.iter().map(|id| { + self.hat_cap_fs + .get_or_local_error("`\\hat{F}` map", &(id.clone(), my_id.clone())) + }), LocalError::new("There must be at least two parties"), )?; - let cap_k = self.cap_ks.safe_get("`K` map", &self.context.my_id)?; + let cap_k = self.cap_ks.get_or_local_error("`K` map", &self.context.my_id)?; let cap_xs = self.context.key_share.public_shares().clone(); @@ -2389,9 +2546,9 @@ impl Round for Round6 { DecPublicInputs { pk0: pk, cap_k, - cap_x: cap_xs.safe_get("`X` map", &self.context.my_id)?, + cap_x: cap_xs.get_or_local_error("`X` map", &self.context.my_id)?, cap_d: &hat_cap_d, - cap_s: self.cap_ss.safe_get("`S` map", &self.context.my_id)?, + cap_s: self.cap_ss.get_or_local_error("`S` map", &self.context.my_id)?, cap_g: &self.cap_gamma_combined, num_parties, }, @@ -2405,64 +2562,59 @@ impl Round for Round6 { rng, AffGStarSecretInputs { x: &x, - y: self.hat_betas.safe_get("`\\hat{beta}` map", id)?, - rho: self.hat_ss.safe_get("`\\hat{s}` map", id)?, - mu: self.hat_rs.safe_get("`\\hat{r}` map", id)?, + y: self.hat_betas.get_or_local_error("`\\hat{beta}` map", id)?, + rho: self.hat_ss.get_or_local_error("`\\hat{s}` map", id)?, + mu: self.hat_rs.get_or_local_error("`\\hat{r}` map", id)?, }, AffGStarPublicInputs { pk0: &self.context.public_aux(id)?.paillier_pk, pk1: pk, - cap_c: self.cap_ks.safe_get("``K` map", id)?, + cap_c: self.cap_ks.get_or_local_error("``K` map", id)?, cap_d: self .hat_cap_ds - .safe_get("`\\hat{D}` map", &(id.clone(), my_id.clone()))?, + .get_or_local_error("`\\hat{D}` map", &(id.clone(), my_id.clone()))?, cap_y: self .hat_cap_fs - .safe_get("`\\hat{F}` map", &(id.clone(), my_id.clone()))?, - cap_x: cap_xs.safe_get("`X` map", &my_id)?, + .get_or_local_error("`\\hat{F}` map", &(id.clone(), my_id.clone()))?, + cap_x: cap_xs.get_or_local_error("`X` map", &my_id)?, }, &aux, ); - assert!(hat_psi.verify( - AffGStarPublicInputs { - pk0: &self.context.public_aux(id)?.paillier_pk, - pk1: pk, - cap_c: self.cap_ks.safe_get("``K` map", id)?, - cap_d: self - .hat_cap_ds - .safe_get("`\\hat{D}` map", &(id.clone(), my_id.clone()))?, - cap_y: self - .hat_cap_fs - .safe_get("`\\hat{F}` map", &(id.clone(), my_id.clone()))?, - cap_x: cap_xs.safe_get("`X` map", &my_id)?, - }, - &aux, - )); + assert!( + hat_psi.verify( + AffGStarPublicInputs { + pk0: &self.context.public_aux(id)?.paillier_pk, + pk1: pk, + cap_c: self.cap_ks.get_or_local_error("``K` map", id)?, + cap_d: self + .hat_cap_ds + .get_or_local_error("`\\hat{D}` map", &(id.clone(), my_id.clone()))?, + cap_y: self + .hat_cap_fs + .get_or_local_error("`\\hat{F}` map", &(id.clone(), my_id.clone()))?, + cap_x: cap_xs.get_or_local_error("`X` map", &my_id)?, + }, + &aux, + ) + ); hat_psis.insert(id.clone(), hat_psi); } - EchoBroadcast::new( - format, - Round6EchoBroadcast:: { - hat_psi_star, - hat_psis: hat_psis.into(), - }, - ) + Ok(Round6EchoBroadcast:: { + hat_psi_star, + hat_psis: hat_psis.into(), + }) } fn receive_message( &self, - format: &BoxedFormat, + from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.normal_broadcast.assert_is_none()?; - message.direct_message.assert_is_none()?; - let echo_broadcast = message - .echo_broadcast - .deserialize::>(format)?; + message: ProtocolMessage, + ) -> Result> { + let echo_broadcast = message.echo_broadcast; let my_id = self.context.my_id.clone(); let aux = (&self.context.epid, from); @@ -2473,12 +2625,16 @@ impl Round for Round6 { let ids = self.context.all_ids.clone().without(from); let hat_cap_d = sum_non_empty_ref( - ids.iter() - .map(|id| self.hat_cap_ds.safe_get("`\\hat{D}` map", &(from.clone(), id.clone()))), + ids.iter().map(|id| { + self.hat_cap_ds + .get_or_local_error("`\\hat{D}` map", &(from.clone(), id.clone())) + }), LocalError::new("There must be at least two parties"), )? + sum_non_empty_ref( - ids.iter() - .map(|id| self.hat_cap_fs.safe_get("`\\hat{F}` map", &(id.clone(), from.clone()))), + ids.iter().map(|id| { + self.hat_cap_fs + .get_or_local_error("`\\hat{F}` map", &(id.clone(), from.clone())) + }), LocalError::new("There must be at least two parties"), )?; @@ -2487,22 +2643,28 @@ impl Round for Round6 { if !echo_broadcast.hat_psi_star.verify( DecPublicInputs { pk0: sender_pk, - cap_k: self.cap_ks.safe_get("`K` map", from)?, - cap_x: cap_xs.safe_get("`X` map", from)?, + cap_k: self.cap_ks.get_or_local_error("`K` map", from)?, + cap_x: cap_xs.get_or_local_error("`X` map", from)?, cap_d: &hat_cap_d, - cap_s: self.cap_ss.safe_get("`S` map", from)?, + cap_s: self.cap_ss.get_or_local_error("`S` map", from)?, cap_g: &self.cap_gamma_combined, num_parties: self.context.all_ids.len(), }, sender_rp, &aux, ) { - return Err(ReceiveError::protocol(Error::R6DecFailed.into())); + return Err(ReceiveError::Protocol(R6Error { + error: R6ErrorEnum::DecFailed, + phantom: PhantomData, + })); } let expected_ids = self.context.all_ids.clone().without(from); if echo_broadcast.hat_psis.keys().cloned().collect::>() != expected_ids { - return Err(ReceiveError::protocol(Error::R6WrongIdsPsi.into())); + return Err(ReceiveError::Protocol(R6Error { + error: R6ErrorEnum::WrongIdsPsi, + phantom: PhantomData, + })); } for (id, hat_psi) in echo_broadcast.hat_psis.iter() { @@ -2515,27 +2677,32 @@ impl Round for Round6 { AffGStarPublicInputs { pk0: pk, pk1: sender_pk, - cap_c: self.cap_ks.safe_get("`K` map", id)?, - cap_d: self.hat_cap_ds.safe_get("`D` map", &(id.clone(), from.clone()))?, - cap_y: self.hat_cap_fs.safe_get("`F` map", &(id.clone(), from.clone()))?, - cap_x: cap_xs.safe_get("`X` map", from)?, + cap_c: self.cap_ks.get_or_local_error("`K` map", id)?, + cap_d: self + .hat_cap_ds + .get_or_local_error("`D` map", &(id.clone(), from.clone()))?, + cap_y: self + .hat_cap_fs + .get_or_local_error("`F` map", &(id.clone(), from.clone()))?, + cap_x: cap_xs.get_or_local_error("`X` map", from)?, }, &aux, ) { - return Err(ReceiveError::protocol( - Error::R6AffGStarFailed { failed_for: id.clone() }.into(), - )); + return Err(ReceiveError::Protocol(R6Error { + error: R6ErrorEnum::AffGStarFailed { failed_for: id.clone() }, + phantom: PhantomData, + })); } } - Ok(Payload::empty()) + Ok(()) } fn finalize( - self: Box, - _rng: &mut dyn CryptoRngCore, - _payloads: BTreeMap, - _artifacts: BTreeMap, + self, + _rng: &mut impl CryptoRngCore, + _payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { Err(LocalError::new( "One of the messages should have been missing or invalid", @@ -2547,19 +2714,19 @@ impl Round for Round6 { mod tests { use alloc::collections::BTreeSet; - use ecdsa::{signature::hazmat::PrehashVerifier, VerifyingKey}; + use ecdsa::{VerifyingKey, signature::hazmat::PrehashVerifier}; use elliptic_curve::FieldBytes; use manul::{ - dev::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, + dev::{BinaryFormat, TestSessionParams, TestSigner, TestVerifier, run_sync}, signature::Keypair, }; use rand_core::{OsRng, RngCore}; use super::InteractiveSigning; use crate::{ + SchemeParams, dev::TestParams, entities::{AuxInfo, KeyShare}, - SchemeParams, }; type Curve = ::Curve; diff --git a/src/protocols/key_init.rs b/src/protocols/key_init.rs index f494d575..e4900b32 100644 --- a/src/protocols/key_init.rs +++ b/src/protocols/key_init.rs @@ -3,19 +3,18 @@ //! auxiliary parameters need to be generated as well (during the KeyRefresh protocol). use alloc::{ - boxed::Box, collections::{BTreeMap, BTreeSet}, + string::String, }; -use core::{ - fmt::{self, Debug, Display}, - marker::PhantomData, -}; +use core::{fmt::Debug, marker::PhantomData}; -use manul::protocol::{ - Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EntryPoint, FinalizeOutcome, - LocalError, MessageValidationError, NormalBroadcast, PartyId, Payload, Protocol, ProtocolError, ProtocolMessage, - ProtocolMessagePart, ProtocolValidationError, ReceiveError, RequiredMessageParts, RequiredMessages, Round, RoundId, - TransitionInfo, +use manul::{ + protocol::{ + BoxedRound, CommunicationInfo, EntryPoint, EvidenceError, EvidenceMessages, FinalizeOutcome, LocalError, + NoArtifact, NoMessage, NoProtocolErrors, PartyId, Protocol, ProtocolError, ProtocolMessage, ReceiveError, + RequiredMessageParts, RequiredMessages, Round, RoundId, RoundInfo, TransitionInfo, + }, + utils::{GetOrLocalError, MapValues, MapValuesRef, Without, verify_that}, }; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; @@ -25,10 +24,9 @@ use crate::{ entities::{KeyShare, Sid}, params::SchemeParams, tools::{ + Secret, bitvec::BitVec, hashing::{Chain, HashOutput, Hasher}, - protocol_shortcuts::{verify_that, DeserializeAll, DowncastMap, GetRound, MapValues, SafeGet, Without}, - Secret, }, zk::{SchCommitment, SchProof, SchSecret}, }; @@ -39,125 +37,70 @@ pub struct KeyInitProtocol(PhantomData<(P, Id)>); impl Protocol for KeyInitProtocol { type Result = KeyShare; - type ProtocolError = KeyInitError

; - - fn verify_direct_message_is_invalid( - _format: &BoxedFormat, - round_id: &RoundId, - message: &DirectMessage, - ) -> Result<(), MessageValidationError> { - match round_id { - r if r == &1 => message.verify_is_some(), - r if r == &2 => message.verify_is_some(), - r if r == &3 => message.verify_is_some(), - _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), - } - } - - fn verify_echo_broadcast_is_invalid( - format: &BoxedFormat, - round_id: &RoundId, - message: &EchoBroadcast, - ) -> Result<(), MessageValidationError> { + type SharedData = KeyInitSharedData; + fn round_info(round_id: &RoundId) -> Option> { match round_id { - r if r == &1 => message.verify_is_not::(format), - r if r == &2 => message.verify_is_not::(format), - r if r == &3 => message.verify_is_some(), - _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), - } - } - - fn verify_normal_broadcast_is_invalid( - format: &BoxedFormat, - round_id: &RoundId, - message: &NormalBroadcast, - ) -> Result<(), MessageValidationError> { - match round_id { - r if r == &1 => message.verify_is_some(), - r if r == &2 => message.verify_is_not::>(format), - r if r == &3 => message.verify_is_not::>(format), - _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), + _ if round_id == 1 => Some(RoundInfo::new::>()), + _ if round_id == 2 => Some(RoundInfo::new::>()), + _ if round_id == 3 => Some(RoundInfo::new::>()), + _ => None, } } } -/// Possible verifiable errors of the KeyGen protocol. #[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct KeyInitError

{ - error: Error, - phantom: PhantomData

, -} - -impl Display for KeyInitError

{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", self.error) - } +pub(super) struct R2Error

{ + error: R2ErrorEnum, + phantom: PhantomData P>, } -impl From for KeyInitError

{ - fn from(source: Error) -> Self { +impl

From for R2Error

{ + fn from(error: R2ErrorEnum) -> Self { Self { - error: source, + error, phantom: PhantomData, } } } -#[derive(displaydoc::Display, Debug, Clone, Copy, Serialize, Deserialize)] -enum Error { - /// Round 2: the previously sent hash does not match the public data. - R2HashMismatch, - /// Round 3: failed to verify `П^{{sch}}`. - R3InvalidSchProof, +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +enum R2ErrorEnum { + HashMismatch, } -/// Associated data for KeyInit protocol. -#[derive(Debug, Clone)] -pub struct KeyInitAssociatedData { - /// IDs of all participating nodes. - pub ids: BTreeSet, -} +impl ProtocolError for R2Error

{ + type Round = Round2; -impl ProtocolError for KeyInitError

{ - type AssociatedData = KeyInitAssociatedData; + fn description(&self) -> String { + match self.error { + R2ErrorEnum::HashMismatch => "The previously sent hash does not match the public data.".into(), + } + } - fn required_messages(&self) -> RequiredMessages { + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { match self.error { - Error::R2HashMismatch => RequiredMessages::new( + R2ErrorEnum::HashMismatch => RequiredMessages::new( RequiredMessageParts::echo_broadcast().and_normal_broadcast(), Some([(1.into(), RequiredMessageParts::echo_broadcast())].into()), None, ), - Error::R3InvalidSchProof => RequiredMessages::new( - RequiredMessageParts::normal_broadcast(), - Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), - Some([2.into()].into()), - ), } } - fn verify_messages_constitute_error( + fn verify_evidence( &self, - format: &BoxedFormat, + _round_id: &RoundId, guilty_party: &Id, shared_randomness: &[u8], - associated_data: &Self::AssociatedData, - message: ProtocolMessage, - previous_messages: BTreeMap, - combined_echos: BTreeMap>, - ) -> Result<(), ProtocolValidationError> { - let sid = Sid::new::(shared_randomness, &associated_data.ids); - + shared_data: &<>::Protocol as Protocol>::SharedData, + messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + let sid = Sid::new::(shared_randomness, &shared_data.ids); match self.error { - Error::R2HashMismatch => { - let r1_eb = previous_messages - .get_round(1)? - .echo_broadcast - .deserialize::(format)?; - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; - let r2_eb = message.echo_broadcast.deserialize::(format)?; + R2ErrorEnum::HashMismatch => { + let r1_eb = messages.previous_echo_broadcast::>(1)?; + let r2_nb = messages.normal_broadcast()?; + let r2_eb = messages.echo_broadcast()?; let data = PublicData { cap_x: r2_nb.cap_x, cap_a: r2_nb.cap_a, @@ -166,21 +109,64 @@ impl ProtocolError for KeyInitError

{ }; verify_that(data.hash(&sid, guilty_party) != r1_eb.cap_v) } - Error::R3InvalidSchProof => { - let r2_ebs = combined_echos - .get_round(2)? - .deserialize_all::(format)?; - let r2_nb = previous_messages - .get_round(2)? - .normal_broadcast - .deserialize::>(format)?; - let r2_eb = previous_messages - .get_round(2)? - .echo_broadcast - .deserialize::(format)?; - let r3_nb = message - .normal_broadcast - .deserialize::>(format)?; + } + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub(super) struct R3Error

{ + error: R3ErrorEnum, + phantom: PhantomData P>, +} + +impl

From for R3Error

{ + fn from(error: R3ErrorEnum) -> Self { + Self { + error, + phantom: PhantomData, + } + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +enum R3ErrorEnum { + InvalidSchProof, +} + +impl ProtocolError for R3Error

{ + type Round = Round3; + + fn description(&self) -> String { + match self.error { + R3ErrorEnum::InvalidSchProof => "Failed to verify `П^{sch}`.".into(), + } + } + + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { + match self.error { + R3ErrorEnum::InvalidSchProof => RequiredMessages::new( + RequiredMessageParts::normal_broadcast(), + Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), + Some([2.into()].into()), + ), + } + } + + fn verify_evidence( + &self, + _round_id: &RoundId, + guilty_party: &Id, + shared_randomness: &[u8], + shared_data: &<>::Protocol as Protocol>::SharedData, + messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + let sid = Sid::new::(shared_randomness, &shared_data.ids); + match self.error { + R3ErrorEnum::InvalidSchProof => { + let r2_ebs = messages.combined_echos::>(2)?; + let r2_nb = messages.previous_normal_broadcast::>(2)?; + let r2_eb = messages.previous_echo_broadcast::>(2)?; + let r3_nb = messages.normal_broadcast()?; let mut rho = r2_eb.rho; for message in r2_ebs.values() { @@ -194,6 +180,13 @@ impl ProtocolError for KeyInitError

{ } } +/// Associated data for KeyInit protocol. +#[derive(Debug, Clone)] +pub struct KeyInitSharedData { + /// IDs of all participating nodes. + pub ids: BTreeSet, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub(super) struct PublicData { cap_x: Point

, @@ -245,7 +238,7 @@ impl EntryPoint for KeyInit { fn make_round( self, - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, shared_randomness: &[u8], id: &Id, ) -> Result, LocalError> { @@ -278,7 +271,7 @@ impl EntryPoint for KeyInit { sid, }; - Ok(BoxedRound::new_dynamic(Round1 { context })) + Ok(BoxedRound::new(Round1 { context })) } } @@ -293,16 +286,16 @@ pub(super) struct Context { } #[derive(Debug)] -struct Round1 { +pub(super) struct Round1 { context: Context, } #[derive(Debug, Clone, Serialize, Deserialize)] -struct Round1EchoBroadcast { +pub(super) struct Round1EchoBroadcast { cap_v: HashOutput, } -struct Round1Payload { +pub(super) struct Round1Payload { cap_v: HashOutput, } @@ -313,6 +306,15 @@ where { type Protocol = KeyInitProtocol; + type DirectMessage = NoMessage; + type NormalBroadcast = NoMessage; + type EchoBroadcast = Round1EchoBroadcast; + + type Payload = Round1Payload; + type Artifact = NoArtifact; + + type ProtocolError = NoProtocolErrors; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear(1) } @@ -321,53 +323,44 @@ where CommunicationInfo::regular(&self.context.other_ids) } - fn make_echo_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { + fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { let cap_v = self.context.public_data.hash(&self.context.sid, &self.context.my_id); - EchoBroadcast::new(format, Round1EchoBroadcast { cap_v }) + Ok(Round1EchoBroadcast { cap_v }) } fn receive_message( &self, - format: &BoxedFormat, _from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.normal_broadcast.assert_is_none()?; - message.direct_message.assert_is_none()?; - let echo_broadcast = message.echo_broadcast.deserialize::(format)?; - Ok(Payload::new(Round1Payload { - cap_v: echo_broadcast.cap_v, - })) + message: ProtocolMessage, + ) -> Result> { + Ok(Round1Payload { + cap_v: message.echo_broadcast.cap_v, + }) } fn finalize( - self: Box, - _rng: &mut dyn CryptoRngCore, - payloads: BTreeMap, - _artifacts: BTreeMap, + self, + _rng: &mut impl CryptoRngCore, + payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { - let payloads = payloads.downcast_all::()?; let cap_vs = payloads.map_values(|payload| payload.cap_v); let next_round = Round2 { context: self.context, cap_vs, }; - Ok(FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(next_round))) + Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(next_round))) } } #[derive(Debug)] -struct Round2 { +pub(super) struct Round2 { context: Context, cap_vs: BTreeMap, } #[derive(Clone, Serialize, Deserialize)] -struct Round2EchoBroadcast { +pub(super) struct Round2EchoBroadcast { rho: BitVec, } @@ -379,7 +372,7 @@ pub(super) struct Round2NormalBroadcast { pub(super) u: BitVec, } -struct Round2Payload { +pub(super) struct Round2Payload { cap_x: Point

, cap_a: SchCommitment

, rho: BitVec, @@ -388,6 +381,15 @@ struct Round2Payload { impl Round for Round2 { type Protocol = KeyInitProtocol; + type DirectMessage = NoMessage; + type NormalBroadcast = Round2NormalBroadcast

; + type EchoBroadcast = Round2EchoBroadcast; + + type Payload = Round2Payload

; + type Artifact = NoArtifact; + + type ProtocolError = R2Error

; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear(2) } @@ -396,73 +398,50 @@ impl Round for Round2 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_echo_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - EchoBroadcast::new( - format, - Round2EchoBroadcast { - rho: self.context.public_data.rho.clone(), - }, - ) + fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round2EchoBroadcast { + rho: self.context.public_data.rho.clone(), + }) } - fn make_normal_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - NormalBroadcast::new( - format, - Round2NormalBroadcast { - cap_x: self.context.public_data.cap_x, - cap_a: self.context.public_data.cap_a.clone(), - u: self.context.public_data.u.clone(), - }, - ) + fn make_normal_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round2NormalBroadcast { + cap_x: self.context.public_data.cap_x, + cap_a: self.context.public_data.cap_a.clone(), + u: self.context.public_data.u.clone(), + }) } fn receive_message( &self, - format: &BoxedFormat, from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.direct_message.assert_is_none()?; - let normal_broadcast = message - .normal_broadcast - .deserialize::>(format)?; - let echo_broadcast = message.echo_broadcast.deserialize::(format)?; - - let cap_v = self.cap_vs.safe_get("vector `V`", from)?; + message: ProtocolMessage, + ) -> Result> { + let cap_v = self.cap_vs.get_or_local_error("vector `V`", from)?; let data = PublicData { - cap_x: normal_broadcast.cap_x, - cap_a: normal_broadcast.cap_a, - u: normal_broadcast.u, - rho: echo_broadcast.rho, + cap_x: message.normal_broadcast.cap_x, + cap_a: message.normal_broadcast.cap_a, + u: message.normal_broadcast.u, + rho: message.echo_broadcast.rho, }; if &data.hash(&self.context.sid, from) != cap_v { - return Err(ReceiveError::protocol(Error::R2HashMismatch.into())); + return Err(ReceiveError::Protocol(R2ErrorEnum::HashMismatch.into())); } - Ok(Payload::new(Round2Payload { + Ok(Round2Payload { cap_x: data.cap_x, rho: data.rho, cap_a: data.cap_a, - })) + }) } fn finalize( - self: Box, - _rng: &mut dyn CryptoRngCore, - payloads: BTreeMap, - _artifacts: BTreeMap, + self, + _rng: &mut impl CryptoRngCore, + payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { - let payloads = payloads.downcast_all::>()?; - let mut rho_combined = self.context.public_data.rho.clone(); for payload in payloads.values() { rho_combined ^= &payload.rho; @@ -478,7 +457,7 @@ impl Round for Round2 { rho_combined, }; - Ok(FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(next_round))) + Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(next_round))) } } @@ -499,6 +478,15 @@ pub(super) struct Round3NormalBroadcast { impl Round for Round3 { type Protocol = KeyInitProtocol; + type DirectMessage = NoMessage; + type NormalBroadcast = Round3NormalBroadcast

; + type EchoBroadcast = NoMessage; + + type Payload = (); + type Artifact = NoArtifact; + + type ProtocolError = R3Error

; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear_terminating(3) } @@ -507,11 +495,7 @@ impl Round for Round3 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_normal_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { + fn make_normal_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { let aux = (&self.context.sid, &self.context.my_id, &self.rho_combined); let psi = SchProof::new( &self.context.tau, @@ -520,36 +504,29 @@ impl Round for Round3 { &self.context.public_data.cap_x, &aux, ); - NormalBroadcast::new(format, Round3NormalBroadcast { psi }) + Ok(Round3NormalBroadcast { psi }) } fn receive_message( &self, - format: &BoxedFormat, from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.echo_broadcast.assert_is_none()?; - message.direct_message.assert_is_none()?; - let normal_broadcast = message - .normal_broadcast - .deserialize::>(format)?; - - let cap_a = self.cap_as.safe_get("`A` map", from)?; - let cap_x = self.cap_xs.safe_get("`X` map", from)?; + message: ProtocolMessage, + ) -> Result> { + let cap_a = self.cap_as.get_or_local_error("`A` map", from)?; + let cap_x = self.cap_xs.get_or_local_error("`X` map", from)?; let aux = (&self.context.sid, from, &self.rho_combined); - if !normal_broadcast.psi.verify(cap_a, cap_x, &aux) { - return Err(ReceiveError::protocol(Error::R3InvalidSchProof.into())); + if !message.normal_broadcast.psi.verify(cap_a, cap_x, &aux) { + return Err(ReceiveError::Protocol(R3ErrorEnum::InvalidSchProof.into())); } - Ok(Payload::empty()) + Ok(()) } fn finalize( - self: Box, - _rng: &mut dyn CryptoRngCore, - _payloads: BTreeMap, - _artifacts: BTreeMap, + self, + _rng: &mut impl CryptoRngCore, + _payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { let my_id = self.context.my_id.clone(); let mut public_shares = self.cap_xs; @@ -569,13 +546,14 @@ mod tests { use alloc::collections::BTreeSet; use manul::{ - dev::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, + dev::{BinaryFormat, TestSessionParams, TestSigner, TestVerifier, run_sync}, signature::Keypair, + utils::MapValuesRef, }; use rand_core::OsRng; use super::KeyInit; - use crate::{dev::TestParams, tools::protocol_shortcuts::MapValues}; + use crate::dev::TestParams; #[test] fn execute_keygen() { diff --git a/src/protocols/key_refresh.rs b/src/protocols/key_refresh.rs index adc6ba7c..3b70f314 100644 --- a/src/protocols/key_refresh.rs +++ b/src/protocols/key_refresh.rs @@ -3,29 +3,25 @@ //! for ZK proofs (e.g. Paillier keys). use alloc::{ - boxed::Box, collections::{BTreeMap, BTreeSet}, + string::String, }; -use core::{ - fmt::{self, Debug, Display}, - marker::PhantomData, -}; +use core::{fmt::Debug, marker::PhantomData}; use crypto_bigint::BitOps; use manul::{ protocol::{ - Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EntryPoint, - FinalizeOutcome, LocalError, MessageValidationError, NormalBroadcast, PartyId, Payload, Protocol, - ProtocolError, ProtocolMessage, ProtocolMessagePart, ProtocolValidationError, ReceiveError, - RequiredMessageParts, RequiredMessages, Round, RoundId, TransitionInfo, + BoxedRound, CommunicationInfo, EntryPoint, EvidenceError, EvidenceMessages, FinalizeOutcome, LocalError, + NoArtifact, NoMessage, NoProtocolErrors, PartyId, Protocol, ProtocolError, ProtocolMessage, ReceiveError, + RequiredMessageParts, RequiredMessages, Round, RoundId, RoundInfo, TransitionInfo, }, - utils::SerializableMap, + utils::{GetOrInvalidEvidence, GetOrLocalError, MapValues, MapValuesRef, SerializableMap, Without, verify_that}, }; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; use crate::{ - curve::{secret_split, Point, Scalar}, + curve::{Point, Scalar, secret_split}, entities::{AuxInfo, KeyShareChange, PublicAuxInfo, PublicAuxInfos, SecretAuxInfo, Sid}, paillier::{ PaillierParams, PublicKeyPaillier, PublicKeyPaillierWire, RPParams, RPParamsWire, RPSecret, SecretKeyPaillier, @@ -33,10 +29,9 @@ use crate::{ }, params::SchemeParams, tools::{ + Secret, bitvec::BitVec, hashing::{Chain, HashOutput, Hasher}, - protocol_shortcuts::{verify_that, DeserializeAll, DowncastMap, GetRound, MapValues, SafeGet, Without}, - Secret, }, zk::{FacProof, ModProof, PrmProof, SchCommitment, SchProof, SchSecret}, }; @@ -52,232 +47,91 @@ where Id: PartyId, { type Result = (KeyShareChange, AuxInfo); - type ProtocolError = KeyRefreshError; - - fn verify_direct_message_is_invalid( - format: &BoxedFormat, - round_id: &RoundId, - message: &DirectMessage, - ) -> Result<(), MessageValidationError> { - match round_id { - r if r == &1 => message.verify_is_some(), - r if r == &2 => message.verify_is_some(), - r if r == &3 => message.verify_is_not::>(format), - _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), - } - } - - fn verify_echo_broadcast_is_invalid( - format: &BoxedFormat, - round_id: &RoundId, - message: &EchoBroadcast, - ) -> Result<(), MessageValidationError> { - match round_id { - r if r == &1 => message.verify_is_not::(format), - r if r == &2 => message.verify_is_not::>(format), - r if r == &3 => message.verify_is_not::>(format), - _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), - } - } - - fn verify_normal_broadcast_is_invalid( - format: &BoxedFormat, - round_id: &RoundId, - message: &NormalBroadcast, - ) -> Result<(), MessageValidationError> { + type SharedData = KeyRefreshSharedData; + fn round_info(round_id: &RoundId) -> Option> { match round_id { - r if r == &1 => message.verify_is_some(), - r if r == &2 => message.verify_is_not::>(format), - r if r == &3 => message.verify_is_not::>(format), - _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), + _ if round_id == 1 => Some(RoundInfo::new::>()), + _ if round_id == 2 => Some(RoundInfo::new::>()), + _ if round_id == 3 => Some(RoundInfo::new::>()), + _ => None, } } } -/// Provable KeyRefresh faults. -#[derive(Debug, Clone)] -#[derive_where::derive_where(Serialize, Deserialize)] -pub struct KeyRefreshError -where - Id: PartyId, - P: SchemeParams, -{ - error: Error, +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(super) struct R2Error

{ + error: R2ErrorEnum, + phantom: PhantomData P>, } -impl From> for KeyRefreshError -where - Id: PartyId, - P: SchemeParams, -{ - fn from(source: Error) -> Self { - Self { error: source } - } +#[derive(Debug, Clone, Serialize, Deserialize)] +enum R2ErrorEnum { + HashMismatch, + WrongIdsX, + WrongIdsY, + WrongIdsA, + PaillierModulusTooSmall, + RPModulusTooSmall, + NonZeroSumOfChanges, + PrmFailed, } -impl Display for KeyRefreshError -where - P: SchemeParams, - Id: PartyId, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - f, - "{}", - match self.error { - Error::R2HashMismatch => "Round 2: the previously sent hash does not match the public data.", - Error::R2WrongIdsX => "Round 2: wrong IDs in public shares map.", - Error::R2WrongIdsY => "Round 2: wrong IDs in Elgamal keys map.", - Error::R2WrongIdsA => "Round 2: wrong IDs in Schnorr commitments map.", - Error::R2PaillierModulusTooSmall => "Round 2: Paillier modulus is too small.", - Error::R2RPModulusTooSmall => "Round 2: ring-Pedersent modulus is too small.", - Error::R2NonZeroSumOfChanges => "Round 2: sum of share changes is not zero.", - Error::R2PrmFailed => "Round 2: `П^{prm}` verification failed.", - Error::R3ShareChangeMismatch { .. } => - "Round 3: secret share change does not match the public commitment.", - Error::R3ModFailed => "Round 3: `П^{mod}` verification failed.", - Error::R3FacFailed { .. } => "Round 3: `П^{fac}` verification failed.", - Error::R3WrongIdsHatPsi => "Round 3: Wrong IDs in Schnorr proofs map.", - Error::R3SchFailed { .. } => "Round 3: `П^{sch}` verification failed.", - } - ) - } -} +impl ProtocolError for R2Error

{ + type Round = Round2; -/// KeyRefresh error -#[derive(Debug, Clone)] -#[derive_where::derive_where(Serialize, Deserialize)] -enum Error -where - Id: PartyId, - P: SchemeParams, -{ - R2HashMismatch, - R2WrongIdsX, - R2WrongIdsY, - R2WrongIdsA, - R2PaillierModulusTooSmall, - R2RPModulusTooSmall, - R2NonZeroSumOfChanges, - R2PrmFailed, - R3ShareChangeMismatch { - /// The index $i$ of the node that produced the evidence. - reported_by: Id, - /// $y_{i,j}$, where where $j$ is the index of the guilty party. - y: Scalar

, - }, - R3ModFailed, - R3FacFailed { - /// The index $i$ of the node that produced the evidence. - reported_by: Id, - }, - R3WrongIdsHatPsi, - R3SchFailed { - /// The index $k$ for which the verification of $\hat{\psi}_{j,k}$ failed - /// (where $j$ is the index of the guilty party). - failed_for: Id, - }, -} - -/// Reconstruct `rid` from echoed messages -fn reconstruct_rid( - format: &BoxedFormat, - previous_messages: &BTreeMap, - combined_echos: &BTreeMap>, -) -> Result { - let r2_ebs = combined_echos - .get_round(2)? - .deserialize_all::>(format)?; - let r2_eb = previous_messages - .get_round(2)? - .echo_broadcast - .deserialize::>(format)?; - let mut rid_combined = r2_eb.rid; - for message in r2_ebs.values() { - rid_combined ^= &message.rid; + fn description(&self) -> String { + match self.error { + R2ErrorEnum::HashMismatch => "The previously sent hash does not match the public data.".into(), + R2ErrorEnum::WrongIdsX => "Wrong IDs in public shares map.".into(), + R2ErrorEnum::WrongIdsY => "Wrong IDs in Elgamal keys map.".into(), + R2ErrorEnum::WrongIdsA => "Wrong IDs in Schnorr commitments map.".into(), + R2ErrorEnum::PaillierModulusTooSmall => "Paillier modulus is too small.".into(), + R2ErrorEnum::RPModulusTooSmall => "Ring-Pedersen modulus is too small.".into(), + R2ErrorEnum::NonZeroSumOfChanges => "Sum of share changes is not zero.".into(), + R2ErrorEnum::PrmFailed => "`П^{prm}` verification failed.".into(), + } } - Ok(rid_combined) -} -/// Associated data for KeyRefresh protocol. -#[derive(Debug, Clone)] -pub struct KeyRefreshAssociatedData { - /// IDs of all participating nodes. - pub ids: BTreeSet, -} - -impl ProtocolError for KeyRefreshError { - type AssociatedData = KeyRefreshAssociatedData; - - fn required_messages(&self) -> RequiredMessages { + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { match self.error { - Error::R2HashMismatch => RequiredMessages::new( + R2ErrorEnum::HashMismatch => RequiredMessages::new( RequiredMessageParts::normal_broadcast().and_echo_broadcast(), Some([(1.into(), RequiredMessageParts::echo_broadcast())].into()), None, ), - Error::R2WrongIdsX => RequiredMessages::new(RequiredMessageParts::normal_broadcast(), None, None), - Error::R2WrongIdsY => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), - Error::R2WrongIdsA => RequiredMessages::new(RequiredMessageParts::normal_broadcast(), None, None), - Error::R2PaillierModulusTooSmall => { + R2ErrorEnum::WrongIdsX => RequiredMessages::new(RequiredMessageParts::normal_broadcast(), None, None), + R2ErrorEnum::WrongIdsY => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), + R2ErrorEnum::WrongIdsA => RequiredMessages::new(RequiredMessageParts::normal_broadcast(), None, None), + R2ErrorEnum::PaillierModulusTooSmall => { + RequiredMessages::new(RequiredMessageParts::normal_broadcast(), None, None) + } + R2ErrorEnum::RPModulusTooSmall => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), + R2ErrorEnum::NonZeroSumOfChanges => { RequiredMessages::new(RequiredMessageParts::normal_broadcast(), None, None) } - Error::R2RPModulusTooSmall => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), - Error::R2NonZeroSumOfChanges => RequiredMessages::new(RequiredMessageParts::normal_broadcast(), None, None), - Error::R2PrmFailed => RequiredMessages::new( + R2ErrorEnum::PrmFailed => RequiredMessages::new( RequiredMessageParts::echo_broadcast().and_normal_broadcast(), None, None, ), - Error::R3ShareChangeMismatch { .. } => RequiredMessages::new( - RequiredMessageParts::direct_message(), - Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), - Some([2.into()].into()), - ), - Error::R3ModFailed => RequiredMessages::new( - RequiredMessageParts::normal_broadcast(), - Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), - Some([2.into()].into()), - ), - Error::R3FacFailed { .. } => RequiredMessages::new( - RequiredMessageParts::direct_message(), - Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), - Some([2.into()].into()), - ), - Error::R3WrongIdsHatPsi => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), - Error::R3SchFailed { .. } => RequiredMessages::new( - RequiredMessageParts::echo_broadcast(), - Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), - Some([2.into()].into()), - ), } } - fn verify_messages_constitute_error( + fn verify_evidence( &self, - format: &BoxedFormat, + _round_id: &RoundId, guilty_party: &Id, shared_randomness: &[u8], - associated_data: &Self::AssociatedData, - message: ProtocolMessage, - previous_messages: BTreeMap, - combined_echos: BTreeMap>, - ) -> Result<(), ProtocolValidationError> { - let sid = Sid::new::(shared_randomness, &associated_data.ids); + shared_data: &<>::Protocol as Protocol>::SharedData, + messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + let sid = Sid::new::(shared_randomness, &shared_data.ids); match &self.error { - Error::R2HashMismatch => { - let r1_eb = previous_messages - .get_round(1)? - .echo_broadcast - .deserialize::(format)?; - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; - let r2_eb = message - .echo_broadcast - .deserialize::>(format)?; - + R2ErrorEnum::HashMismatch => { + let r1_eb = messages.previous_echo_broadcast::>(1)?; + let r2_nb = messages.normal_broadcast()?; + let r2_eb = messages.echo_broadcast()?; let data = PublicData { cap_xs: r2_nb.cap_xs.into(), cap_ys: r2_eb.cap_ys.into(), @@ -290,79 +144,138 @@ impl ProtocolError for KeyRefreshError }; verify_that(data.hash(&sid, guilty_party) != r1_eb.cap_v) } - Error::R2WrongIdsX => { - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; - verify_that(r2_nb.cap_xs.keys().cloned().collect::>() != associated_data.ids) + R2ErrorEnum::WrongIdsX => { + let r2_nb = messages.normal_broadcast()?; + verify_that(r2_nb.cap_xs.keys().cloned().collect::>() != shared_data.ids) } - Error::R2WrongIdsY => { - let r2_eb = message - .echo_broadcast - .deserialize::>(format)?; - verify_that(r2_eb.cap_ys.keys().cloned().collect::>() != associated_data.ids) + R2ErrorEnum::WrongIdsY => { + let r2_eb = messages.echo_broadcast()?; + verify_that(r2_eb.cap_ys.keys().cloned().collect::>() != shared_data.ids) } - Error::R2WrongIdsA => { - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; - verify_that(r2_nb.cap_as.keys().cloned().collect::>() != associated_data.ids) + R2ErrorEnum::WrongIdsA => { + let r2_nb = messages.normal_broadcast()?; + verify_that(r2_nb.cap_as.keys().cloned().collect::>() != shared_data.ids) } - Error::R2PaillierModulusTooSmall => { - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; + R2ErrorEnum::PaillierModulusTooSmall => { + let r2_nb = messages.normal_broadcast()?; verify_that( r2_nb.paillier_pk.modulus().bits_vartime() < ::MODULUS_BITS - 2, ) } - Error::R2RPModulusTooSmall => { - let r2_eb = message - .echo_broadcast - .deserialize::>(format)?; + R2ErrorEnum::RPModulusTooSmall => { + let r2_eb = messages.echo_broadcast()?; verify_that( r2_eb.rp_params.modulus().bits_vartime() < ::MODULUS_BITS - 2, ) } - Error::R2NonZeroSumOfChanges => { - let r2_nb = message - .normal_broadcast - .deserialize::>(format)?; + R2ErrorEnum::NonZeroSumOfChanges => { + let r2_nb = messages.normal_broadcast()?; verify_that(r2_nb.cap_xs.values().sum::>() != Point::identity()) } - Error::R2PrmFailed => { - let r2_eb = message - .echo_broadcast - .deserialize::>(format)?; - let r2_bc = message - .normal_broadcast - .deserialize::>(format)?; + R2ErrorEnum::PrmFailed => { + let r2_eb = messages.echo_broadcast()?; + let r2_nb = messages.normal_broadcast()?; let aux = (&sid, guilty_party); let rp_params = r2_eb.rp_params.to_precomputed(); - verify_that(!r2_bc.psi.verify(&rp_params, &aux)) + verify_that(!r2_nb.psi.verify(&rp_params, &aux)) } - Error::R3ShareChangeMismatch { reported_by, y } => { + } + } +} + +#[derive(Debug, Clone)] +#[derive_where::derive_where(Serialize, Deserialize)] +pub(super) enum R3Error { + ShareChangeMismatch { + /// The index $i$ of the node that produced the evidence. + reported_by: Id, + /// $y_{i,j}$, where where $j$ is the index of the guilty party. + y: Scalar

, + }, + ModFailed, + FacFailed { + /// The index $i$ of the node that produced the evidence. + reported_by: Id, + }, + WrongIdsHatPsi, + SchFailed { + /// The index $k$ for which the verification of $\hat{\psi}_{j,k}$ failed + /// (where $j$ is the index of the guilty party). + failed_for: Id, + }, +} + +impl ProtocolError for R3Error { + type Round = Round3; + + fn description(&self) -> String { + match self { + Self::ShareChangeMismatch { .. } => "Secret share change does not match the public commitment.".into(), + Self::ModFailed => "`П^{mod}` verification failed.".into(), + Self::FacFailed { .. } => "`П^{fac}` verification failed.".into(), + Self::WrongIdsHatPsi => "Wrong IDs in Schnorr proofs map.".into(), + Self::SchFailed { .. } => "`П^{sch}` verification failed.".into(), + } + } + + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { + match self { + Self::ShareChangeMismatch { .. } => RequiredMessages::new( + RequiredMessageParts::direct_message(), + Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), + Some([2.into()].into()), + ), + Self::ModFailed => RequiredMessages::new( + RequiredMessageParts::normal_broadcast(), + Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), + Some([2.into()].into()), + ), + Self::FacFailed { .. } => RequiredMessages::new( + RequiredMessageParts::direct_message(), + Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), + Some([2.into()].into()), + ), + Self::WrongIdsHatPsi => RequiredMessages::new(RequiredMessageParts::echo_broadcast(), None, None), + Self::SchFailed { .. } => RequiredMessages::new( + RequiredMessageParts::echo_broadcast(), + Some([(2.into(), RequiredMessageParts::echo_broadcast().and_normal_broadcast())].into()), + Some([2.into()].into()), + ), + } + } + + fn verify_evidence( + &self, + _round_id: &RoundId, + guilty_party: &Id, + shared_randomness: &[u8], + shared_data: &<>::Protocol as Protocol>::SharedData, + messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + let sid = Sid::new::(shared_randomness, &shared_data.ids); + + match self { + Self::ShareChangeMismatch { reported_by, y } => { // Check that `y` attached to the evidence is correct // (that is, can be verified against something signed by `guilty_party`). // It is `y_{i,j}` where `i == reported_by` and `j == guilty_party` - let r2_eb_i = combined_echos - .get_round(2)? - .try_get("combined echos for Round 2", reported_by)? - .deserialize::>(format)?; - let cap_y_ij = r2_eb_i.cap_ys.try_get("public Elgamal values", guilty_party)?; + let r2_eb_i = messages + .combined_echos::>(2)? + .get_or_invalid_evidence("Round 2 echoed messsages", reported_by)? + .clone(); + let cap_y_ij = r2_eb_i + .cap_ys + .get_or_invalid_evidence("public Elgamal values", guilty_party)?; if &y.mul_by_generator() != cap_y_ij { - return Err(ProtocolValidationError::InvalidEvidence( - "The provided `y` is invalid".into(), - )); + return Err(EvidenceError::InvalidEvidence("The provided `y` is invalid".into())); } - let rid = reconstruct_rid::(format, &previous_messages, &combined_echos)?; + let rid = reconstruct_rid::(&messages)?; - let r2_eb = previous_messages - .get_round(2)? - .echo_broadcast - .deserialize::>(format)?; - let cap_y_ji = r2_eb.cap_ys.try_get("public Elgamal values", reported_by)?; + let r2_eb = messages.previous_echo_broadcast::>(2)?; + let cap_y_ji = r2_eb + .cap_ys + .get_or_invalid_evidence("public Elgamal values", reported_by)?; let mut reader = Hasher::::new_with_dst(b"KeyRefresh Round3") .chain(&sid) .chain(&rid) @@ -371,73 +284,81 @@ impl ProtocolError for KeyRefreshError .finalize_to_reader(); let rho = Scalar::from_xof_reader(&mut reader); - let r2_bc = previous_messages - .get_round(2)? - .normal_broadcast - .deserialize::>(format)?; - let r3_dm = message.direct_message.deserialize::>(format)?; + let r2_nb = messages.previous_normal_broadcast::>(2)?; + let r3_dm = messages.direct_message()?; let x = r3_dm.cap_c - rho; - let cap_x_ji = r2_bc.cap_xs.try_get("public key share changes", reported_by)?; + let cap_x_ji = r2_nb + .cap_xs + .get_or_invalid_evidence("public key share changes", reported_by)?; verify_that(&x.mul_by_generator() != cap_x_ji) } - Error::R3ModFailed => { - let rid = reconstruct_rid::(format, &previous_messages, &combined_echos)?; + Self::ModFailed => { + let rid = reconstruct_rid::(&messages)?; let aux = (&sid, guilty_party, &rid); - let r2_bc = previous_messages - .get_round(2)? - .normal_broadcast - .deserialize::>(format)?; - let r3_bc = message - .normal_broadcast - .deserialize::>(format)?; - let paillier_pk = r2_bc.paillier_pk.into_precomputed(); - verify_that(!r3_bc.psi_prime.verify(&paillier_pk, &aux)) + let r2_nb = messages.previous_normal_broadcast::>(2)?; + let r3_nb = messages.normal_broadcast()?; + let paillier_pk = r2_nb.paillier_pk.into_precomputed(); + verify_that(!r3_nb.psi_prime.verify(&paillier_pk, &aux)) } - Error::R3FacFailed { reported_by } => { - let rid = reconstruct_rid::(format, &previous_messages, &combined_echos)?; + Self::FacFailed { reported_by } => { + let rid = reconstruct_rid::(&messages)?; let aux = (&sid, guilty_party, &rid); - let r2_eb = combined_echos - .get_round(2)? - .try_get("combined echos for Round 2", reported_by)? - .deserialize::>(format)?; - let r2_bc = previous_messages - .get_round(2)? - .normal_broadcast - .deserialize::>(format)?; - let r3_dm = message.direct_message.deserialize::>(format)?; - let paillier_pk = r2_bc.paillier_pk.into_precomputed(); + let r2_eb = messages + .combined_echos::>(2)? + .get_or_invalid_evidence("combined echos for Round 2", reported_by)? + .clone(); + let r2_nb = messages.previous_normal_broadcast::>(2)?; + let r3_dm = messages.direct_message()?; + let paillier_pk = r2_nb.paillier_pk.into_precomputed(); let rp_params = r2_eb.rp_params.to_precomputed(); verify_that(!r3_dm.psi.verify(&paillier_pk, &rp_params, &aux)) } - Error::R3WrongIdsHatPsi => { - let r3_eb = message - .echo_broadcast - .deserialize::>(format)?; - verify_that(r3_eb.hat_psis.keys().cloned().collect::>() != associated_data.ids) + Self::WrongIdsHatPsi => { + let r3_eb = messages.echo_broadcast()?; + verify_that(r3_eb.hat_psis.keys().cloned().collect::>() != shared_data.ids) } - Error::R3SchFailed { failed_for } => { - let rid = reconstruct_rid::(format, &previous_messages, &combined_echos)?; + Self::SchFailed { failed_for } => { + let rid = reconstruct_rid::(&messages)?; let aux = (&sid, guilty_party, &rid); - let r2_bc = previous_messages - .get_round(2)? - .normal_broadcast - .deserialize::>(format)?; - let r3_eb = message - .echo_broadcast - .deserialize::>(format)?; - - let cap_a = r2_bc.cap_as.try_get("Schnorr commitments", failed_for)?; - let cap_x = r2_bc.cap_xs.try_get("public share changes", failed_for)?; - let hat_psi = r3_eb.hat_psis.try_get("Schnorr proofs", failed_for)?; + let r2_bc = messages.previous_normal_broadcast::>(2)?; + let r3_eb = messages.echo_broadcast()?; + + let cap_a = r2_bc + .cap_as + .get_or_invalid_evidence("Schnorr commitments", failed_for)?; + let cap_x = r2_bc + .cap_xs + .get_or_invalid_evidence("public share changes", failed_for)?; + let hat_psi = r3_eb.hat_psis.get_or_invalid_evidence("Schnorr proofs", failed_for)?; verify_that(!hat_psi.verify(cap_a, cap_x, &aux)) } } } } +/// Reconstruct `rid` from echoed messages +fn reconstruct_rid( + messages: &EvidenceMessages<'_, Id, Round3>, +) -> Result { + let r2_ebs = messages.combined_echos::>(2)?; + let r2_eb = messages.previous_echo_broadcast::>(2)?; + let mut rid_combined = r2_eb.rid; + for message in r2_ebs.values() { + rid_combined ^= &message.rid; + } + Ok(rid_combined) +} + +/// Associated data for KeyRefresh protocol. +#[derive(Debug, Clone)] +pub struct KeyRefreshSharedData { + /// IDs of all participating nodes. + pub ids: BTreeSet, +} + #[derive(Debug, Clone)] pub(super) struct PublicData { pub(super) cap_xs: BTreeMap>, // $X_{i,j}$ where $i$ is this party's index @@ -494,7 +415,7 @@ impl EntryPoint for KeyRefresh { fn make_round( self, - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, shared_randomness: &[u8], id: &Id, ) -> Result, LocalError> { @@ -575,7 +496,7 @@ impl EntryPoint for KeyRefresh { let round = Round1 { context, public_data }; - Ok(BoxedRound::new_dynamic(round)) + Ok(BoxedRound::new(round)) } } @@ -603,13 +524,22 @@ pub(super) struct Round1EchoBroadcast { pub(super) cap_v: HashOutput, } -struct Round1Payload { +pub(super) struct Round1Payload { cap_v: HashOutput, } impl Round for Round1 { type Protocol = KeyRefreshProtocol; + type DirectMessage = NoMessage; + type NormalBroadcast = NoMessage; + type EchoBroadcast = Round1EchoBroadcast; + + type Payload = Round1Payload; + type Artifact = NoArtifact; + + type ProtocolError = NoProtocolErrors; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear(1) } @@ -618,51 +548,40 @@ impl Round for Round1 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_echo_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - let message = Round1EchoBroadcast { + fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round1EchoBroadcast { cap_v: self.public_data.hash(&self.context.sid, &self.context.my_id), - }; - EchoBroadcast::new(format, message) + }) } fn receive_message( &self, - format: &BoxedFormat, _from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.normal_broadcast.assert_is_none()?; - message.direct_message.assert_is_none()?; - let echo_broadcast = message.echo_broadcast.deserialize::(format)?; - let payload = Round1Payload { - cap_v: echo_broadcast.cap_v, - }; - Ok(Payload::new(payload)) + message: ProtocolMessage, + ) -> Result> { + Ok(Round1Payload { + cap_v: message.echo_broadcast.cap_v, + }) } fn finalize( - self: Box, - _rng: &mut dyn CryptoRngCore, - payloads: BTreeMap, - _artifacts: BTreeMap, + self, + _rng: &mut impl CryptoRngCore, + payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { - let payloads = payloads.downcast_all::()?; let cap_vs = payloads.map_values(|payload| payload.cap_v); let next_round = Round2 { context: self.context, public_data: self.public_data, cap_vs, }; - Ok(FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(next_round))) + Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(next_round))) } } #[derive(Debug)] -struct Round2 { +pub(super) struct Round2 { context: Context, public_data: PublicData, cap_vs: BTreeMap, @@ -687,7 +606,7 @@ pub(super) struct Round2EchoBroadcast { } #[derive(Debug)] -struct Round2Payload { +pub(super) struct Round2Payload { cap_xs: BTreeMap>, // $X_{i,j}$ where $i$ is this party's index cap_as: BTreeMap>, // $A_{i,j}$ where $i$ is this party's index cap_ys: BTreeMap>, // $Y_{i,j}$ where $i$ is this party's index @@ -699,6 +618,15 @@ struct Round2Payload { impl Round for Round2 { type Protocol = KeyRefreshProtocol; + type DirectMessage = NoMessage; + type NormalBroadcast = Round2NormalBroadcast; + type EchoBroadcast = Round2EchoBroadcast; + + type Payload = Round2Payload; + type Artifact = NoArtifact; + + type ProtocolError = R2Error

; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear(2) } @@ -707,47 +635,31 @@ impl Round for Round2 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_normal_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - let message = Round2NormalBroadcast { + fn make_normal_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round2NormalBroadcast { cap_xs: self.public_data.cap_xs.clone().into(), cap_as: self.public_data.cap_as.clone().into(), paillier_pk: self.public_data.paillier_pk.clone().into_wire(), psi: self.public_data.psi.clone(), u: self.public_data.u.clone(), - }; - NormalBroadcast::new(format, message) + }) } - fn make_echo_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - let message = Round2EchoBroadcast:: { + fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round2EchoBroadcast:: { cap_ys: self.public_data.cap_ys.clone().into(), rid: self.public_data.rid.clone(), rp_params: self.public_data.rp_params.to_wire(), - }; - EchoBroadcast::new(format, message) + }) } fn receive_message( &self, - format: &BoxedFormat, from: &Id, - message: ProtocolMessage, - ) -> Result> { - message.direct_message.assert_is_none()?; - let echo_broadcast = message - .echo_broadcast - .deserialize::>(format)?; - let normal_broadcast = message - .normal_broadcast - .deserialize::>(format)?; + message: ProtocolMessage, + ) -> Result> { + let echo_broadcast = message.echo_broadcast; + let normal_broadcast = message.normal_broadcast; let data = PublicData { cap_xs: normal_broadcast.cap_xs.into(), @@ -760,61 +672,81 @@ impl Round for Round2 { u: normal_broadcast.u, }; - let cap_v = self.cap_vs.safe_get("other nodes' `V`", from)?; + let cap_v = self.cap_vs.get_or_local_error("other nodes' `V`", from)?; if &data.hash(&self.context.sid, from) != cap_v { - return Err(ReceiveError::protocol(Error::R2HashMismatch.into())); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::HashMismatch, + phantom: PhantomData, + })); } if data.cap_xs.keys().cloned().collect::>() != self.context.all_ids { - return Err(ReceiveError::protocol(Error::R2WrongIdsX.into())); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::WrongIdsX, + phantom: PhantomData, + })); } if data.cap_ys.keys().cloned().collect::>() != self.context.all_ids { - return Err(ReceiveError::protocol(Error::R2WrongIdsY.into())); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::WrongIdsY, + phantom: PhantomData, + })); } if data.cap_as.keys().cloned().collect::>() != self.context.all_ids { - return Err(ReceiveError::protocol(Error::R2WrongIdsA.into())); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::WrongIdsA, + phantom: PhantomData, + })); } if data.paillier_pk.modulus().bits_vartime() < ::MODULUS_BITS - 2 { - return Err(ReceiveError::protocol(Error::R2PaillierModulusTooSmall.into())); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::PaillierModulusTooSmall, + phantom: PhantomData, + })); } if data.rp_params.modulus().bits_vartime() < ::MODULUS_BITS - 2 { - return Err(ReceiveError::protocol(Error::R2RPModulusTooSmall.into())); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::RPModulusTooSmall, + phantom: PhantomData, + })); } if data.cap_xs.values().sum::>() != Point::identity() { - return Err(ReceiveError::protocol(Error::R2NonZeroSumOfChanges.into())); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::NonZeroSumOfChanges, + phantom: PhantomData, + })); } let aux = (&self.context.sid, &from); if !data.psi.verify(&data.rp_params, &aux) { - return Err(ReceiveError::protocol(Error::R2PrmFailed.into())); + return Err(ReceiveError::Protocol(R2Error { + error: R2ErrorEnum::PrmFailed, + phantom: PhantomData, + })); } - let payload = Round2Payload:: { + Ok(Round2Payload { cap_xs: data.cap_xs, cap_as: data.cap_as, cap_ys: data.cap_ys, paillier_pk: data.paillier_pk, rp_params: data.rp_params, rid: data.rid, - }; - - Ok(Payload::new(payload)) + }) } fn finalize( - self: Box, - rng: &mut dyn CryptoRngCore, - payloads: BTreeMap, - _artifacts: BTreeMap, + self, + rng: &mut impl CryptoRngCore, + payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { - let mut payloads = payloads.downcast_all::>()?; - let mut rid_combined = self.public_data.rid.clone(); for payload in payloads.values() { rid_combined ^= &payload.rid; @@ -826,15 +758,16 @@ impl Round for Round2 { let mut hat_psis = BTreeMap::new(); for id in self.context.all_ids.iter() { - let x = self.context.xs.safe_get("secret share changes", id)?; - let tau = self.context.taus.safe_get("Schnorr secrets", id)?; - let cap_a = self.public_data.cap_as.safe_get("Schnorr commitments", id)?; - let cap_x = self.public_data.cap_xs.safe_get("public share changes", id)?; + let x = self.context.xs.get_or_local_error("secret share changes", id)?; + let tau = self.context.taus.get_or_local_error("Schnorr secrets", id)?; + let cap_a = self.public_data.cap_as.get_or_local_error("Schnorr commitments", id)?; + let cap_x = self.public_data.cap_xs.get_or_local_error("public share changes", id)?; let hat_psi = SchProof::new(tau, x, cap_a, cap_x, &aux); hat_psis.insert(id.clone(), hat_psi); } // Add in the payload with this node's info, for the sake of uniformity + let mut payloads = payloads; let my_r2_payload = Round2Payload:: { cap_xs: self.public_data.cap_xs, cap_as: self.public_data.cap_as, @@ -853,12 +786,12 @@ impl Round for Round2 { hat_psis, }; - Ok(FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(next_round))) + Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(next_round))) } } #[derive(Debug)] -struct Round3 { +pub(super) struct Round3 { context: Context, rid_combined: BitVec, r2_payloads: BTreeMap>, @@ -885,13 +818,22 @@ pub(super) struct Round3DirectMessage { pub(super) cap_c: Scalar

, } -struct Round3Payload { +pub(super) struct Round3Payload { x: Secret>, // $x_j^i$, a secret share change received from the party $j$ } impl Round for Round3 { type Protocol = KeyRefreshProtocol; + type DirectMessage = Round3DirectMessage

; + type NormalBroadcast = Round3NormalBroadcast

; + type EchoBroadcast = Round3EchoBroadcast; + + type Payload = Round3Payload

; + type Artifact = (); + + type ProtocolError = R3Error; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear_terminating(3) } @@ -900,43 +842,32 @@ impl Round for Round3 { CommunicationInfo::regular(&self.context.other_ids) } - fn make_echo_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - let message = Round3EchoBroadcast { + fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round3EchoBroadcast { hat_psis: self.hat_psis.clone().into(), - }; - EchoBroadcast::new(format, message) + }) } - fn make_normal_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { - let message = Round3NormalBroadcast { + fn make_normal_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + Ok(Round3NormalBroadcast { psi_prime: self.psi_prime.clone(), - }; - NormalBroadcast::new(format, message) + }) } fn make_direct_message( &self, - rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, + rng: &mut impl CryptoRngCore, destination: &Id, - ) -> Result<(DirectMessage, Option), LocalError> { + ) -> Result<(Self::DirectMessage, Self::Artifact), LocalError> { let my_id = &self.context.my_id; let aux = (&self.context.sid, my_id, &self.rid_combined); - let r2_payload = self.r2_payloads.safe_get("Round 2 payloads", destination)?; + let r2_payload = self.r2_payloads.get_or_local_error("Round 2 payloads", destination)?; let psi = FacProof::

::new(rng, &self.context.paillier_sk, &r2_payload.rp_params, &aux); - let cap_y = r2_payload.cap_ys.safe_get("Elgamal public keys", my_id)?; - let y = self.context.ys.safe_get("Elgamal secrets", destination)?; + let cap_y = r2_payload.cap_ys.get_or_local_error("Elgamal public keys", my_id)?; + let y = self.context.ys.get_or_local_error("Elgamal secrets", destination)?; let mut reader = Hasher::::new_with_dst(b"KeyRefresh Round3") .chain(&self.context.sid) .chain(&self.rid_combined) @@ -944,33 +875,29 @@ impl Round for Round3 { .chain(&(cap_y * y)) .finalize_to_reader(); let rho = Scalar::from_xof_reader(&mut reader); - let x = self.context.xs.safe_get("secret share changes", destination)?; + let x = self + .context + .xs + .get_or_local_error("secret share changes", destination)?; let cap_c = *(x + &rho).expose_secret(); - let message = Round3DirectMessage { psi, cap_c }; - let dm = DirectMessage::new(format, message)?; - Ok((dm, None)) + Ok((Round3DirectMessage { psi, cap_c }, ())) } fn receive_message( &self, - format: &BoxedFormat, from: &Id, - message: ProtocolMessage, - ) -> Result> { - let echo_broadcast = message - .echo_broadcast - .deserialize::>(format)?; - let normal_broadcast = message - .normal_broadcast - .deserialize::>(format)?; - let direct_message = message.direct_message.deserialize::>(format)?; + message: ProtocolMessage, + ) -> Result> { + let echo_broadcast = message.echo_broadcast; + let normal_broadcast = message.normal_broadcast; + let direct_message = message.direct_message; let my_id = &self.context.my_id; - let r2_payload = self.r2_payloads.safe_get("Round 2 payloads", from)?; - let cap_y = r2_payload.cap_ys.safe_get("Elgamal public keys", my_id)?; - let y = self.context.ys.safe_get("Elgamal secrets", from)?; + let r2_payload = self.r2_payloads.get_or_local_error("Round 2 payloads", from)?; + let cap_y = r2_payload.cap_ys.get_or_local_error("Elgamal public keys", my_id)?; + let y = self.context.ys.get_or_local_error("Elgamal secrets", from)?; let mut reader = Hasher::::new_with_dst(b"KeyRefresh Round3") .chain(&self.context.sid) .chain(&self.rid_combined) @@ -980,66 +907,56 @@ impl Round for Round3 { let rho = Scalar::from_xof_reader(&mut reader); let x = Secret::init_with(|| direct_message.cap_c - rho); - let my_cap_x = r2_payload.cap_xs.safe_get("public share changes", my_id)?; + let my_cap_x = r2_payload.cap_xs.get_or_local_error("public share changes", my_id)?; if &x.mul_by_generator() != my_cap_x { - return Err(ReceiveError::protocol( - Error::R3ShareChangeMismatch { - reported_by: my_id.clone(), - y: *y.expose_secret(), - } - .into(), - )); + return Err(ReceiveError::Protocol(R3Error::ShareChangeMismatch { + reported_by: my_id.clone(), + y: *y.expose_secret(), + })); } let aux = (&self.context.sid, from, &self.rid_combined); if !normal_broadcast.psi_prime.verify(&r2_payload.paillier_pk, &aux) { - return Err(ReceiveError::protocol(Error::R3ModFailed.into())); + return Err(ReceiveError::Protocol(R3Error::ModFailed)); } if !direct_message .psi .verify(&r2_payload.paillier_pk, &self.context.rp_params, &aux) { - return Err(ReceiveError::protocol( - Error::R3FacFailed { - reported_by: my_id.clone(), - } - .into(), - )); + return Err(ReceiveError::Protocol(R3Error::FacFailed { + reported_by: my_id.clone(), + })); } if echo_broadcast.hat_psis.keys().cloned().collect::>() != self.context.all_ids { - return Err(ReceiveError::protocol(Error::R3WrongIdsHatPsi.into())); + return Err(ReceiveError::Protocol(R3Error::WrongIdsHatPsi)); } for (id, hat_psi) in echo_broadcast.hat_psis.iter() { - let cap_a = r2_payload.cap_as.safe_get("Schnorr commitments", id)?; - let cap_x = r2_payload.cap_xs.safe_get("Public share changes", id)?; + let cap_a = r2_payload.cap_as.get_or_local_error("Schnorr commitments", id)?; + let cap_x = r2_payload.cap_xs.get_or_local_error("Public share changes", id)?; if !hat_psi.verify(cap_a, cap_x, &aux) { - return Err(ReceiveError::protocol( - Error::R3SchFailed { failed_for: id.clone() }.into(), - )); + return Err(ReceiveError::Protocol(R3Error::SchFailed { failed_for: id.clone() })); } } - Ok(Payload::new(Round3Payload { x })) + Ok(Round3Payload { x }) } fn finalize( - self: Box, - _rng: &mut dyn CryptoRngCore, - payloads: BTreeMap, - _artifacts: BTreeMap, + self, + _rng: &mut impl CryptoRngCore, + payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { - let payloads = payloads.downcast_all::>()?; - let my_id = &self.context.my_id; // Share changes from other nodes let xs = payloads.map_values(|payload| payload.x); // Share change generated by this node - let my_x = self.context.xs.safe_get("secret share changes", my_id)?; + let my_x = self.context.xs.get_or_local_error("secret share changes", my_id)?; // The combined secret share change let x_star = xs.into_values().sum::>>() + my_x; @@ -1050,7 +967,7 @@ impl Round for Round3 { for id_k in self.context.all_ids.iter() { let mut result = Point::identity(); for payload in self.r2_payloads.values() { - let cap_x = payload.cap_xs.safe_get("public share changes", id_k)?; + let cap_x = payload.cap_xs.get_or_local_error("public share changes", id_k)?; result = result + *cap_x; } cap_x_star.insert(id_k.clone(), result); @@ -1087,13 +1004,14 @@ mod tests { use alloc::collections::BTreeSet; use manul::{ - dev::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, + dev::{BinaryFormat, TestSessionParams, TestSigner, TestVerifier, run_sync}, signature::Keypair, + utils::MapValues, }; use rand_core::OsRng; use super::KeyRefresh; - use crate::{curve::Scalar, dev::TestParams, tools::protocol_shortcuts::MapValues}; + use crate::{curve::Scalar, dev::TestParams}; #[test] fn execute_key_refresh() { diff --git a/src/protocols/key_resharing.rs b/src/protocols/key_resharing.rs index 2a557591..d1b2ec1d 100644 --- a/src/protocols/key_resharing.rs +++ b/src/protocols/key_resharing.rs @@ -9,19 +9,20 @@ //! since the CGGMP paper itself does not contain any threshold functionality. use alloc::{ - boxed::Box, collections::{BTreeMap, BTreeSet}, - format, + string::String, vec::Vec, }; use core::{fmt::Debug, marker::PhantomData}; use ecdsa::VerifyingKey; -use manul::protocol::{ - Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EchoRoundParticipation, - EntryPoint, FinalizeOutcome, LocalError, MessageValidationError, NormalBroadcast, PartyId, Payload, Protocol, - ProtocolError, ProtocolMessage, ProtocolMessagePart, ProtocolValidationError, ReceiveError, RequiredMessages, - Round, RoundId, TransitionInfo, +use manul::{ + protocol::{ + BoxedRound, CommunicationInfo, EchoRoundParticipation, EntryPoint, EvidenceError, EvidenceMessages, + FinalizeOutcome, LocalError, NoMessage, PartyId, Protocol, ProtocolError, ProtocolMessage, ReceiveError, + RequiredMessageParts, RequiredMessages, Round, RoundId, RoundInfo, TransitionInfo, + }, + utils::{GetOrLocalError, Without}, }; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; @@ -31,72 +32,68 @@ use crate::{ entities::ThresholdKeyShare, params::SchemeParams, tools::{ - protocol_shortcuts::{DowncastMap, Without}, - sss::{interpolation_coeff, shamir_join_points, shamir_join_scalars, Polynomial, PublicPolynomial, ShareId}, Secret, + sss::{Polynomial, PublicPolynomial, ShareId, interpolation_coeff, shamir_join_points, shamir_join_scalars}, }, }; /// A protocol for modifying the set of owners of a shared secret key. #[derive(Debug)] -pub struct KeyResharingProtocol(PhantomData<(P, I)>); - -impl Protocol for KeyResharingProtocol { - type Result = Option>; - type ProtocolError = KeyResharingError; - - fn verify_direct_message_is_invalid( - _format: &BoxedFormat, - _round_id: &RoundId, - _message: &DirectMessage, - ) -> Result<(), MessageValidationError> { - unimplemented!() - } - - fn verify_echo_broadcast_is_invalid( - _format: &BoxedFormat, - _round_id: &RoundId, - _message: &EchoBroadcast, - ) -> Result<(), MessageValidationError> { - unimplemented!() +pub struct KeyResharingProtocol(PhantomData<(P, Id)>); + +impl Protocol for KeyResharingProtocol { + type Result = Option>; + type SharedData = (); + fn round_info(round_id: &RoundId) -> Option> { + match round_id { + _ if round_id == 1 => Some(RoundInfo::new::>()), + _ => None, + } } +} - fn verify_normal_broadcast_is_invalid( - _format: &BoxedFormat, - _round_id: &RoundId, - _message: &NormalBroadcast, - ) -> Result<(), MessageValidationError> { - unimplemented!() - } +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub(super) struct R1Error

{ + error: R1ErrorEnum, + phantom: PhantomData P>, } -/// Provable faults of KeyResharing -#[derive(displaydoc::Display, Debug, Clone, Copy, Serialize, Deserialize)] -pub enum KeyResharingError { - /// Unexpected sender of a message (not one of the old holders) - UnexpectedSender, - /// Mismatch of the subshare +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +enum R1ErrorEnum { SubshareMismatch, } -impl ProtocolError for KeyResharingError { - type AssociatedData = (); +impl ProtocolError for R1Error

{ + type Round = Round1; - fn required_messages(&self) -> RequiredMessages { - unimplemented!() + fn description(&self) -> String { + match self.error { + R1ErrorEnum::SubshareMismatch => "Mismatch of the subshare", + } + .into() } - fn verify_messages_constitute_error( + fn required_messages(&self, _round_id: &RoundId) -> RequiredMessages { + match self.error { + R1ErrorEnum::SubshareMismatch => { + RequiredMessages::new(RequiredMessageParts::echo_broadcast().and_direct_message(), None, None) + } + } + } + + fn verify_evidence( &self, - _format: &BoxedFormat, - _guilty_party: &I, + _round_id: &RoundId, + _guilty_party: &Id, _shared_randomness: &[u8], - _associated_data: &Self::AssociatedData, - _message: ProtocolMessage, - _previous_messages: BTreeMap, - _combined_echos: BTreeMap>, - ) -> Result<(), ProtocolValidationError> { - unimplemented!() + _shared_data: &<>::Protocol as Protocol>::SharedData, + _messages: EvidenceMessages<'_, Id, Self::Round>, + ) -> Result<(), EvidenceError> { + match &self.error { + R1ErrorEnum::SubshareMismatch => { + todo!() + } + } } } @@ -165,7 +162,7 @@ where fn make_round( self, - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, _shared_randomness: &[u8], id: &I, ) -> Result, LocalError> { @@ -225,7 +222,7 @@ where let new_holder = self.new_holder.map(|new_holder| NewHolderData { inputs: new_holder }); - Ok(BoxedRound::new_dynamic(Round1 { + Ok(BoxedRound::new(Round1 { old_holder, new_holder, new_share_ids, @@ -252,7 +249,7 @@ struct NewHolderData { } #[derive(Debug)] -struct Round1 { +pub(super) struct Round1 { old_holder: Option>, new_holder: Option>, new_share_ids: BTreeMap>, @@ -266,17 +263,18 @@ struct Round1 { #[derive(Debug, Clone)] #[derive_where::derive_where(Serialize, Deserialize)] -struct Round1BroadcastMessage { +pub(super) struct Round1BroadcastMessage { public_polynomial: PublicPolynomial

, old_share_id: ShareId

, } #[derive(Debug, Clone)] #[derive_where::derive_where(Serialize, Deserialize)] -struct Round1DirectMessage { +pub(super) struct Round1DirectMessage { subshare: Secret>, } -struct Round1Payload { + +pub(super) struct Round1Payload { subshare: Secret>, public_polynomial: PublicPolynomial

, old_share_id: ShareId

, @@ -285,6 +283,15 @@ struct Round1Payload { impl Round for Round1 { type Protocol = KeyResharingProtocol; + type DirectMessage = Round1DirectMessage

; + type NormalBroadcast = NoMessage; + type EchoBroadcast = Round1BroadcastMessage

; + + type Payload = Round1Payload

; + type Artifact = (); + + type ProtocolError = R1Error

; + fn transition_info(&self) -> TransitionInfo { TransitionInfo::new_linear_terminating(1) } @@ -297,85 +304,73 @@ impl Round for Round1 { } } - fn make_echo_broadcast( - &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, - ) -> Result { + fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { if let Some(old_holder) = self.old_holder.as_ref() { - EchoBroadcast::new( - format, - Round1BroadcastMessage { - public_polynomial: old_holder.public_polynomial.clone(), - old_share_id: old_holder.share_id, - }, - ) + Ok(Round1BroadcastMessage { + public_polynomial: old_holder.public_polynomial.clone(), + old_share_id: old_holder.share_id, + }) } else { - Ok(EchoBroadcast::none()) + Err(LocalError::new("This node does not send messages")) } } fn make_direct_message( &self, - _rng: &mut dyn CryptoRngCore, - format: &BoxedFormat, + _rng: &mut impl CryptoRngCore, destination: &I, - ) -> Result<(DirectMessage, Option), LocalError> { + ) -> Result<(Self::DirectMessage, Self::Artifact), LocalError> { if let Some(old_holder) = self.old_holder.as_ref() { - let their_share_id = self.new_share_ids.get(destination).ok_or(LocalError::new(format!( - "destination={destination:?} is missing from the new_share_ids", - )))?; + let their_share_id = self.new_share_ids.get_or_local_error("new holders", destination)?; let subshare: Secret> = old_holder.polynomial.evaluate(their_share_id); - let dm = DirectMessage::new(format, Round1DirectMessage { subshare })?; - Ok((dm, None)) + Ok((Round1DirectMessage { subshare }, ())) } else { - Ok((DirectMessage::none(), None)) + Err(LocalError::new("This node does not send messages")) } } fn receive_message( &self, - format: &BoxedFormat, from: &I, - message: ProtocolMessage, - ) -> Result> { - message.normal_broadcast.assert_is_none()?; - let echo_broadcast = message - .echo_broadcast - .deserialize::>(format)?; - let direct_message = message.direct_message.deserialize::>(format)?; + message: ProtocolMessage, + ) -> Result> { + let echo_broadcast = message.echo_broadcast; + let direct_message = message.direct_message; if let Some(new_holder) = self.new_holder.as_ref() { if new_holder.inputs.old_holders.contains(from) { - let my_share_id = self.new_share_ids.get(&self.my_id).ok_or(LocalError::new(format!( - "my_id={:?} is missing from the new_share_ids", - &self.my_id - )))?; + let my_share_id = self.new_share_ids.get_or_local_error("new holders", &self.my_id)?; let public_subshare_from_poly = echo_broadcast.public_polynomial.evaluate(my_share_id); let public_subshare_from_private = Secret::mul_by_generator(&direct_message.subshare); // Check that the public polynomial sent in the broadcast corresponds to the secret share // sent in the direct message. if public_subshare_from_poly != public_subshare_from_private { - return Err(ReceiveError::protocol(KeyResharingError::SubshareMismatch)); + return Err(ReceiveError::Protocol(R1Error { + error: R1ErrorEnum::SubshareMismatch, + phantom: PhantomData, + })); } - return Ok(Payload::new(Round1Payload { + return Ok(Round1Payload { subshare: direct_message.subshare, public_polynomial: echo_broadcast.public_polynomial, old_share_id: echo_broadcast.old_share_id, - })); + }); } } - Err(ReceiveError::protocol(KeyResharingError::UnexpectedSender)) + // If this node is an old holder, it has reported that it does not expect any messages + // (in `communication_info()`). + // So if `manul` works correctly, the execution should not reach this point. + Err(LocalError::new("This node is an old holder and does not receive any messages").into()) } fn finalize( - self: Box, - _rng: &mut dyn CryptoRngCore, - payloads: BTreeMap, - _artifacts: BTreeMap, + self, + _rng: &mut impl CryptoRngCore, + payloads: BTreeMap, + _artifacts: BTreeMap, ) -> Result, LocalError> { // If this party is not a new holder, exit. let new_holder = match self.new_holder.as_ref() { @@ -383,12 +378,9 @@ impl Round for Round1 { None => return Ok(FinalizeOutcome::Result(None)), }; - let mut payloads = payloads.downcast_all::>()?; + let mut payloads = payloads; - let share_id = self - .new_share_ids - .get(&self.my_id) - .ok_or_else(|| LocalError::new(format!("my_id={:?} is missing from new_share_ids", &self.my_id)))?; + let share_id = self.new_share_ids.get_or_local_error("new holders", &self.my_id)?; // If this node is both an old and a new holder, // add a simulated payload to the mapping, as if it sent a message to itself. @@ -434,9 +426,7 @@ impl Round for Round1 { .old_holders .iter() .map(|id| { - let payload = payloads - .get(id) - .ok_or_else(|| LocalError::new("id={id:?} is missing from the payloads"))?; + let payload = payloads.get_or_local_error("payloads", id)?; Ok((payload.old_share_id, payload.subshare.clone())) }) .collect::, _>>()?; @@ -471,7 +461,7 @@ mod tests { use alloc::collections::{BTreeMap, BTreeSet}; use manul::{ - dev::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, + dev::{BinaryFormat, TestSessionParams, TestSigner, TestVerifier, run_sync}, signature::Keypair, }; use rand_core::OsRng; diff --git a/src/protocols/misbehavior_tests/aux_gen.rs b/src/protocols/misbehavior_tests/aux_gen.rs index c1821b08..1fd5431a 100644 --- a/src/protocols/misbehavior_tests/aux_gen.rs +++ b/src/protocols/misbehavior_tests/aux_gen.rs @@ -1,29 +1,25 @@ use alloc::collections::BTreeSet; use manul::{ - combinators::misbehave::Misbehaving, - dev::{BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, - protocol::{ - Artifact, BoxedFormat, BoxedRound, DirectMessage, EchoBroadcast, EntryPoint, LocalError, NormalBroadcast, - ProtocolMessagePart, + dev::{ + BinaryFormat, RoundExtension, TestSessionParams, TestSigner, TestVerifier, check_evidence_with_extension, + check_evidence_with_extensions, }, + protocol::{LocalError, Round}, signature::Keypair, }; use rand_chacha::ChaCha8Rng; use rand_core::{CryptoRngCore, OsRng, SeedableRng}; use super::super::aux_gen::{ - AuxGen, AuxGenAssociatedData, Round1, Round1EchoBroadcast, Round2EchoBroadcast, Round2NormalBroadcast, + AuxGen, AuxGenSharedData, Round1, Round1EchoBroadcast, Round2, Round2EchoBroadcast, Round2NormalBroadcast, Round3, Round3DirectMessage, Round3NormalBroadcast, }; use crate::{ dev::TestParams, paillier::{PaillierParams, PublicKeyPaillierWire, RPParams, RPParamsWire, RPSecret, SecretKeyPaillierWire}, params::SchemeParams, - tools::{ - hashing::Hasher, - protocol_shortcuts_dev::{check_evidence_with_behavior, check_invalid_message_evidence, CheckPart}, - }, + tools::hashing::Hasher, zk::{FacProof, ModProof, PrmProof}, }; @@ -32,7 +28,7 @@ type P = TestParams; type SP = TestSessionParams; #[allow(clippy::type_complexity)] -fn make_entry_points() -> (AuxGenAssociatedData, Vec<(TestSigner, AuxGen)>) { +fn make_entry_points() -> (Vec<(TestSigner, AuxGen)>, AuxGenSharedData) { let signers = (0..3).map(TestSigner::new).collect::>(); let all_ids = signers.iter().map(TestSigner::verifying_key).collect::>(); @@ -40,406 +36,251 @@ fn make_entry_points() -> (AuxGenAssociatedData, Vec<(TestSigner, AuxGen(expected_description: &str) -> Result<(), LocalError> -where - M: Misbehaving>, -{ - let (associated_data, entry_points) = make_entry_points().clone(); - check_evidence_with_behavior::(&mut OsRng, entry_points, &(), &associated_data, expected_description) + (entry_points, AuxGenSharedData { ids: all_ids }) } #[test] -fn invalid_messages_r1_echo() { - let (associated_data, entry_points) = make_entry_points(); +fn r2_hash_mismatch() -> Result<(), LocalError> { + #[derive(Debug, Clone)] + struct Override; - check_invalid_message_evidence::( + impl RoundExtension for Override { + type Round = Round1; + + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + _round: &Self::Round, + ) -> Result { + Ok(Round1EchoBroadcast { + cap_v: Hasher::<

::Digest>::new_with_dst(b"bad hash").finalize(P::SECURITY_BITS), + }) + } + } + + check_evidence_with_extension::( &mut OsRng, - entry_points.clone(), - 1, - CheckPart::EchoBroadcast, - &associated_data, - true, + make_entry_points(), + Override, + "Protocol error (Round 2): The previously sent hash does not match the public data.", ) - .unwrap(); } #[test] -fn invalid_messages_r2_echo() { - let (associated_data, entry_points) = make_entry_points(); - - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 2, - CheckPart::EchoBroadcast, - &associated_data, - true, - ) - .unwrap(); -} +fn r2_paillier_modulus_too_small() -> Result<(), LocalError> { + fn make_small_modulus_pk() -> PublicKeyPaillierWire

{ + let mut rng = ChaCha8Rng::seed_from_u64(123); + let paillier_sk = SecretKeyPaillierWire::

::random_small(&mut rng); + paillier_sk.public_key() + } -#[test_log::test] -fn invalid_messages_r3_echo() { - let (associated_data, entry_points) = make_entry_points(); + #[derive(Debug, Clone)] + struct R1Override; + + impl RoundExtension for R1Override { + type Round = Round1; + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result { + let mut data = round.public_data.clone(); + data.paillier_pk = make_small_modulus_pk::<

::Paillier>().into_precomputed(); + Ok(Round1EchoBroadcast { + cap_v: data.hash(&round.context.sid, &round.context.my_id), + }) + } + } - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 3, - CheckPart::EchoBroadcast, - &associated_data, - false, - ) - .unwrap(); -} + #[derive(Debug, Clone)] + struct R2Override; -#[test] -fn invalid_messages_r1_normal() { - let (associated_data, entry_points) = make_entry_points(); + impl RoundExtension for R2Override { + type Round = Round2; - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 1, - CheckPart::NormalBroadcast, - &associated_data, - false, - ) - .unwrap(); -} -#[test] -fn invalid_messages_r2_normal() { - let (associated_data, entry_points) = make_entry_points(); + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; + message.paillier_pk = make_small_modulus_pk::<

::Paillier>(); + Ok(message) + } + } - check_invalid_message_evidence::( + check_evidence_with_extensions::( &mut OsRng, - entry_points.clone(), - 2, - CheckPart::NormalBroadcast, - &associated_data, - true, + make_entry_points(), + |entry_point| entry_point.with_extension(R1Override).with_extension(R2Override), + "Protocol error (Round 2): Paillier modulus is too small.", ) - .unwrap(); } #[test] -fn invalid_messages_r3_normal() { - let (associated_data, entry_points) = make_entry_points(); +fn r2_rp_modulus_too_small() -> Result<(), LocalError> { + fn make_small_modulus_rp_params() -> RPParamsWire

{ + let mut rng = ChaCha8Rng::seed_from_u64(123); + RPParams::random_small(&mut rng).to_wire() + } - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 3, - CheckPart::NormalBroadcast, - &associated_data, - true, - ) - .unwrap(); -} -#[test] -fn invalid_messages_r1_dm() { - let (associated_data, entry_points) = make_entry_points(); + #[derive(Debug, Clone)] + struct R1Override; + + impl RoundExtension for R1Override { + type Round = Round1; + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result { + let mut data = round.public_data.clone(); + data.rp_params = make_small_modulus_rp_params::<

::Paillier>().to_precomputed(); + + Ok(Round1EchoBroadcast { + cap_v: data.hash(&round.context.sid, &round.context.my_id), + }) + } + } - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 1, - CheckPart::DirectMessage, - &associated_data, - false, - ) - .unwrap(); -} -#[test] -fn invalid_messages_r2_dm() { - let (associated_data, entry_points) = make_entry_points(); + #[derive(Debug, Clone)] + struct R2Override; - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 2, - CheckPart::DirectMessage, - &associated_data, - false, - ) - .unwrap(); -} -#[test] -fn invalid_messages_r3_dm() { - let (associated_data, entry_points) = make_entry_points(); + impl RoundExtension for R2Override { + type Round = Round2; - check_invalid_message_evidence::( + fn make_echo_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_echo_broadcast(rng)?; + message.rp_params = make_small_modulus_rp_params::<

::Paillier>(); + Ok(message) + } + } + + check_evidence_with_extensions::( &mut OsRng, - entry_points.clone(), - 3, - CheckPart::DirectMessage, - &associated_data, - true, + make_entry_points(), + |entry_point| entry_point.with_extension(R1Override).with_extension(R2Override), + "Protocol error (Round 2): Ring-Pedersen modulus is too small.", ) - .unwrap(); } #[test] -fn r2_hash_mismatch() { - struct Override; - - impl Misbehaving for Override { - type EntryPoint = AuxGen; - - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - // Send a wrong hash in the Round 1 message - let message = Round1EchoBroadcast { - cap_v: Hasher::<

::Digest>::new_with_dst(b"bad hash").finalize(P::SECURITY_BITS), - }; - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) +fn r2_prm_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] + struct R1Override; + + impl RoundExtension for R1Override { + type Round = Round1; + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result { + let mut data = round.public_data.clone(); + + let mut rng = ChaCha8Rng::seed_from_u64(123); + let secret = RPSecret::random(&mut rng); + let rp_params = RPParams::random_with_secret(&mut rng, &secret); + data.psi = PrmProof::new(&mut rng, &secret, &rp_params, &1u8); + + Ok(Round1EchoBroadcast { + cap_v: data.hash(&round.context.sid, &round.context.my_id), + }) } } - check_evidence::("Protocol error: Round 2: the previously sent hash does not match the public data.") - .unwrap(); -} + #[derive(Debug, Clone)] + struct R2Override; -#[test] -fn r2_paillier_modulus_too_small() { - fn make_small_modulus_pk() -> PublicKeyPaillierWire

{ - let mut rng = ChaCha8Rng::seed_from_u64(123); - let paillier_sk = SecretKeyPaillierWire::

::random_small(&mut rng); - paillier_sk.public_key() - } + impl RoundExtension for R2Override { + type Round = Round2; - struct Override; + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; - impl Misbehaving for Override { - type EntryPoint = AuxGen; - - fn modify_normal_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - message.paillier_pk = make_small_modulus_pk::<

::Paillier>(); - let normal_broadcast = NormalBroadcast::new(format, message)?; - return Ok(normal_broadcast); - } - - Ok(normal_broadcast) - } + let mut rng = ChaCha8Rng::seed_from_u64(123); + let secret = RPSecret::random(&mut rng); + let rp_params = RPParams::random_with_secret(&mut rng, &secret); + message.psi = PrmProof::new(&mut rng, &secret, &rp_params, &1u8); - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - let round1 = round.downcast_ref::>()?; - let mut data = round1.public_data.clone(); - data.paillier_pk = make_small_modulus_pk::<

::Paillier>().into_precomputed(); - let message = Round1EchoBroadcast { - cap_v: data.hash(&round1.context.sid, &round1.context.my_id), - }; - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) + Ok(message) } } - check_evidence::("Protocol error: Round 2: Paillier modulus is too small.").unwrap(); + check_evidence_with_extensions::( + &mut OsRng, + make_entry_points(), + |entry_point| entry_point.with_extension(R1Override).with_extension(R2Override), + "Protocol error (Round 2): `П^{prm}` verification failed.", + ) } #[test] -fn r2_rp_modulus_too_small() { - fn make_small_modulus_rp_params() -> RPParamsWire

{ - let mut rng = ChaCha8Rng::seed_from_u64(123); - RPParams::random_small(&mut rng).to_wire() - } - +fn r3_mod_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = AuxGen; - - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - // Technically we only need to modify `X`, but we need to substitute the hash in Round 1 too, - // so that in Round 2 the hash check could pass and the execution reaches the IDs check. - let round1 = round.downcast_ref::>()?; - - let mut data = round1.public_data.clone(); - data.rp_params = make_small_modulus_rp_params::<

::Paillier>().to_precomputed(); - - let message = Round1EchoBroadcast { - cap_v: data.hash(&round1.context.sid, &round1.context.my_id), - }; - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - if round.id() == 2 { - let mut message = echo_broadcast.deserialize::>(format).unwrap(); - message.rp_params = make_small_modulus_rp_params::<

::Paillier>(); - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) - } - } - - check_evidence::("Protocol error: Round 2: ring-Pedersent modulus is too small.").unwrap(); -} + impl RoundExtension for Override { + type Round = Round3; -#[test] -fn r2_prm_failed() { - struct Override; + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; - impl Misbehaving for Override { - type EntryPoint = AuxGen; - - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - let round1 = round.downcast_ref::>()?; - let mut data = round1.public_data.clone(); - - let mut rng = ChaCha8Rng::seed_from_u64(123); - let secret = RPSecret::random(&mut rng); - let rp_params = RPParams::random_with_secret(&mut rng, &secret); - data.psi = PrmProof::new(&mut rng, &secret, &rp_params, &1u8); - - let message = Round1EchoBroadcast { - cap_v: data.hash(&round1.context.sid, &round1.context.my_id), - }; - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) - } + let sk = SecretKeyPaillierWire::random(rng).into_precomputed(); + message.psi_prime = ModProof::new(rng, &sk, &1u8); - fn modify_normal_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - - let mut rng = ChaCha8Rng::seed_from_u64(123); - let secret = RPSecret::random(&mut rng); - let rp_params = RPParams::random_with_secret(&mut rng, &secret); - message.psi = PrmProof::new(&mut rng, &secret, &rp_params, &1u8); - - let normal_broadcast = NormalBroadcast::new(format, message)?; - return Ok(normal_broadcast); - } - - Ok(normal_broadcast) + Ok(message) } } - check_evidence::("Protocol error: Round 2: `П^{prm}` verification failed.").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 3): `П^{mod}` verification failed.", + ) } #[test] -fn r3_mod_failed() { +fn r3_fac_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = AuxGen; - - fn modify_normal_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 3 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - - let sk = SecretKeyPaillierWire::random(rng).into_precomputed(); - message.psi_prime = ModProof::new(rng, &sk, &1u8); - - let normal_broadcast = NormalBroadcast::new(format, message)?; - return Ok(normal_broadcast); - } - - Ok(normal_broadcast) - } - } + impl RoundExtension for Override { + type Round = Round3; - check_evidence::("Protocol error: Round 3: `П^{mod}` verification failed.").unwrap(); -} + fn make_direct_message( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + destination: &Id, + ) -> Result<(Round3DirectMessage

, ()), LocalError> { + let (mut message, artifact) = round.make_direct_message(rng, destination)?; -#[test] -fn r3_fac_failed() { - struct Override; + let sk = SecretKeyPaillierWire::random(&mut OsRng).into_precomputed(); + let rp_params = RPParams::random(rng); + message.psi = FacProof::new(rng, &sk, &rp_params, &1u8); - impl Misbehaving for Override { - type EntryPoint = AuxGen; - - fn modify_direct_message( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - _destination: &Id, - direct_message: DirectMessage, - artifact: Option, - ) -> Result<(DirectMessage, Option), LocalError> { - if round.id() == 3 { - let mut message = direct_message.deserialize::>(format).unwrap(); - let sk = SecretKeyPaillierWire::random(&mut OsRng).into_precomputed(); - let rp_params = RPParams::random(rng); - message.psi = FacProof::new(rng, &sk, &rp_params, &1u8); - let direct_message = DirectMessage::new(format, message)?; - return Ok((direct_message, artifact)); - } - - Ok((direct_message, artifact)) + Ok((message, artifact)) } } - check_evidence::("Protocol error: Round 3: `П^{fac}` verification failed.").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 3): `П^{fac}` verification failed.", + ) } diff --git a/src/protocols/misbehavior_tests/interactive_signing.rs b/src/protocols/misbehavior_tests/interactive_signing.rs index fd411834..cb9b18bd 100644 --- a/src/protocols/misbehavior_tests/interactive_signing.rs +++ b/src/protocols/misbehavior_tests/interactive_signing.rs @@ -2,30 +2,26 @@ use alloc::collections::{BTreeMap, BTreeSet}; use elliptic_curve::FieldBytes; use manul::{ - combinators::misbehave::{FinalizeOverride, Misbehaving}, - dev::{BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, - protocol::{ - Artifact, BoxedFormat, BoxedRound, DirectMessage, EchoBroadcast, EntryPoint, FinalizeOutcome, LocalError, - NormalBroadcast, Payload, ProtocolMessagePart, + dev::{ + BinaryFormat, RoundExtension, TestSessionParams, TestSigner, TestVerifier, check_evidence_with_extension, + check_evidence_with_extensions, }, + protocol::{BoxedRound, FinalizeOutcome, LocalError, Round}, signature::Keypair, + utils::{MapValues, MapValuesRef}, }; use rand_core::{CryptoRngCore, OsRng, RngCore}; use super::super::interactive_signing::{ - InteractiveSigning, InteractiveSigningAssociatedData, InteractiveSigningProtocol, Round1EchoBroadcast, Round2, - Round2EchoBroadcast, Round2NormalBroadcast, Round3, Round3EchoBroadcast, Round3NormalBroadcast, Round3Payload, - Round4NormalBroadcast, Round5, Round6, + InteractiveSigning, InteractiveSigningSharedData, Round1, Round1EchoBroadcast, Round2, Round2EchoBroadcast, + Round2NormalBroadcast, Round3, Round3EchoBroadcast, Round3NormalBroadcast, Round4, Round4NormalBroadcast, Round5, + Round6, }; use crate::{ curve::{Point, RecoverableSignature, Scalar}, dev::TestParams, entities::{AuxInfo, KeyShare}, params::SchemeParams, - tools::{ - protocol_shortcuts::{DowncastMap, MapValues}, - protocol_shortcuts_dev::{check_evidence_with_behavior, check_invalid_message_evidence, CheckPart}, - }, zk::{ElogProof, ElogPublicInputs, ElogSecretInputs}, }; @@ -36,8 +32,8 @@ type Curve = ::Curve; #[allow(clippy::type_complexity)] fn make_entry_points() -> ( - InteractiveSigningAssociatedData, Vec<(TestSigner, InteractiveSigning)>, + InteractiveSigningSharedData, ) { let signers = (0..3).map(TestSigner::new).collect::>(); let all_ids = signers.iter().map(TestSigner::verifying_key).collect::>(); @@ -59,824 +55,468 @@ fn make_entry_points() -> ( .collect(); let id = all_ids.first().unwrap(); - let associated_data = InteractiveSigningAssociatedData { + let shared_data = InteractiveSigningSharedData { shares: key_shares[id].public().clone(), aux: aux_infos[id].public().clone(), message, }; - (associated_data, entry_points) -} - -fn check_evidence(expected_description: &str) -> Result<(), LocalError> -where - M: Misbehaving>, -{ - let (associated_data, entry_points) = make_entry_points(); - check_evidence_with_behavior::(&mut OsRng, entry_points, &(), &associated_data, expected_description) -} - -mod invalid_messages { - use super::*; - - #[test] - fn echo() { - // Note that this test checks the happy path only. The error rounds need to be triggered explicitly, - // so they are checked in a separate test. - - let (associated_data, entry_points) = make_entry_points(); - - for (round_id, expecting_messages) in [(1, true), (2, true), (3, true), (4, false)] { - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - round_id, - CheckPart::EchoBroadcast, - &associated_data, - expecting_messages, - ) - .unwrap(); - } - } - - #[test] - fn normal() { - // Note that this test checks the happy path only. The error rounds need to be triggered explicitly, - // so they are checked in a separate test. - - let (associated_data, entry_points) = make_entry_points(); - - for (round_id, expecting_messages) in [(1, false), (2, true), (3, true), (4, true)] { - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - round_id, - CheckPart::NormalBroadcast, - &associated_data, - expecting_messages, - ) - .unwrap(); - } - } - - #[test] - fn direct_message() { - // Note that this test checks the happy path only. The error rounds need to be triggered explicitly, - // so they are checked in a separate test. - - let (associated_data, entry_points) = make_entry_points(); - - for (round_id, expecting_messages) in [(1, true), (2, false), (3, false), (4, false)] { - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - round_id, - CheckPart::DirectMessage, - &associated_data, - expecting_messages, - ) - .unwrap(); - } - } + (entry_points, shared_data) } #[test] -fn r1_enc_elg_0_failed() { +fn r1_enc_elg_0_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_echo_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - let mut message = echo_broadcast.deserialize::>(format).unwrap(); - message.cap_a1 = Scalar::random(rng).mul_by_generator(); - return EchoBroadcast::new(format, message); - } - - Ok(echo_broadcast) + impl RoundExtension for Override { + type Round = Round1; + + fn make_echo_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_echo_broadcast(rng)?; + message.cap_a1 = Scalar::random(rng).mul_by_generator(); + Ok(message) } } - check_evidence::("Protocol error: Round 1: failed to verify `\\psi^0` (`П^{enc-elg}` proof).").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 1): failed to verify `\\psi^0` (`П^{enc-elg}` proof).", + ) } #[test] -fn r1_enc_elg_1_failed() { +fn r1_enc_elg_1_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_echo_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - let mut message = echo_broadcast.deserialize::>(format).unwrap(); - message.cap_b1 = Scalar::random(rng).mul_by_generator(); - return EchoBroadcast::new(format, message); - } - - Ok(echo_broadcast) + impl RoundExtension for Override { + type Round = Round1; + + fn make_echo_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_echo_broadcast(rng)?; + message.cap_b1 = Scalar::random(rng).mul_by_generator(); + Ok(message) } } - check_evidence::("Protocol error: Round 1: failed to verify `\\psi^1` (`П^{enc-elg}` proof).").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 1): failed to verify `\\psi^1` (`П^{enc-elg}` proof).", + ) } #[test] -fn r2_wrong_ids_d() { +fn r2_wrong_ids_d() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_normal_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - message.cap_ds.pop_first(); - return NormalBroadcast::new(format, message); - } - - Ok(normal_broadcast) + impl RoundExtension for Override { + type Round = Round2; + + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; + message.cap_ds.pop_first(); + Ok(message) } } - check_evidence::("Protocol error: Round 2: wrong IDs in `D` map.").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 2): wrong IDs in `D` map.", + ) } #[test] -fn r2_wrong_ids_f() { +fn r2_wrong_ids_f() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = echo_broadcast - .deserialize::>(format) - .unwrap(); - message.cap_fs.pop_first(); - return EchoBroadcast::new(format, message); - } - - Ok(echo_broadcast) + impl RoundExtension for Override { + type Round = Round2; + + fn make_echo_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_echo_broadcast(rng)?; + message.cap_fs.pop_first(); + Ok(message) } } - check_evidence::("Protocol error: Round 2: wrong IDs in `F` map.").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 2): wrong IDs in `F` map.", + ) } #[test] -fn r2_wrong_ids_psi() { +fn r2_wrong_ids_psi() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_normal_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - message.psis.pop_first(); - return NormalBroadcast::new(format, message); - } - - Ok(normal_broadcast) + impl RoundExtension for Override { + type Round = Round2; + + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; + message.psis.pop_first(); + Ok(message) } } - check_evidence::("Protocol error: Round 2: wrong IDs in `\\psi` map (`П^{aff-g}` proofs for `D`).") - .unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 2): wrong IDs in `\\psi` map (`П^{aff-g}` proofs for `D`).", + ) } #[test] -fn r2_aff_g_psi_failed() { +fn r2_aff_g_psi_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_echo_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = echo_broadcast - .deserialize::>(format) - .unwrap(); - message.cap_gamma = Scalar::random(rng).mul_by_generator(); - return EchoBroadcast::new(format, message); - } - - Ok(echo_broadcast) + impl RoundExtension for Override { + type Round = Round2; + + fn make_echo_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_echo_broadcast(rng)?; + message.cap_gamma = Scalar::random(rng).mul_by_generator(); + Ok(message) } } - check_evidence::("Protocol error: Round 2: failed to verify `\\psi` (`П^{aff-g}` proof for `D`).") - .unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 2): failed to verify `\\psi` (`П^{aff-g}` proof for `D`).", + ) } #[test] -fn r2_aff_g_hat_psi_failed() { +fn r2_aff_g_hat_psi_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_normal_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - message.hat_cap_ds = message.cap_ds.clone(); - return NormalBroadcast::new(format, message); - } - - Ok(normal_broadcast) + impl RoundExtension for Override { + type Round = Round2; + + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; + message.hat_cap_ds = message.cap_ds.clone(); + Ok(message) } } - check_evidence::( - "Protocol error: Round 2: failed to verify `\\hat{psi}` (`П^{aff-g}` proof for `\\hat{D}`).", + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 2): failed to verify `\\hat{psi}` (`П^{aff-g}` proof for `\\hat{D}`).", ) - .unwrap(); } #[test] -fn r2_elog_failed() { +fn r2_elog_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_normal_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 2 { - let round2 = round.downcast_ref::>()?; - let aux = (&round2.context.epid, &round2.context.my_id); - - // An invalid `y` - let y = Scalar::random(rng); - let cap_y = y.mul_by_generator(); - let cap_b1 = round2.context.b.mul_by_generator(); - let cap_b2 = cap_y * &round2.context.b + round2.context.gamma.mul_by_generator(); - - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - - // We can't replace any dependent values in the messages because - // it will trigger errors in earlier proofs. - // So we're replacing the elog proof itself, with one incorrect value (`y`). - - message.psi_elog = ElogProof::new( - rng, - ElogSecretInputs { - y: &round2.context.gamma, - lambda: &round2.context.b, - }, - // Note that the parameter order in the protocol description - // and in the ZK proof description do not match. - ElogPublicInputs { - cap_l: &cap_b1, - cap_m: &cap_b2, - cap_x: &cap_y, - cap_y: &round2.cap_gamma, - h: &Point::generator(), - }, - &aux, - ); - return NormalBroadcast::new(format, message); - } - - Ok(normal_broadcast) + impl RoundExtension for Override { + type Round = Round2; + + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; + + let aux = (&round.context.epid, &round.context.my_id); + + // An invalid `y` + let y = Scalar::random(rng); + let cap_y = y.mul_by_generator(); + let cap_b1 = round.context.b.mul_by_generator(); + let cap_b2 = cap_y * &round.context.b + round.context.gamma.mul_by_generator(); + + // We can't replace any dependent values in the messages because + // it will trigger errors in earlier proofs. + // So we're replacing the elog proof itself, with one incorrect value (`y`). + + message.psi_elog = ElogProof::new( + rng, + ElogSecretInputs { + y: &round.context.gamma, + lambda: &round.context.b, + }, + // Note that the parameter order in the protocol description + // and in the ZK proof description do not match. + ElogPublicInputs { + cap_l: &cap_b1, + cap_m: &cap_b2, + cap_x: &cap_y, + cap_y: &round.cap_gamma, + h: &Point::generator(), + }, + &aux, + ); + + Ok(message) } } - check_evidence::("Protocol error: Round 2: failed to verify `П^{elog}` proof.").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 2): failed to verify `П^{elog}` proof.", + ) } #[test] -fn r3_elog_failed() { +fn r3_elog_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_normal_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 3 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - message.cap_delta = Scalar::random(rng).mul_by_generator(); - return NormalBroadcast::new(format, message); - } - - Ok(normal_broadcast) + impl RoundExtension for Override { + type Round = Round3; + + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; + message.cap_delta = Scalar::random(rng).mul_by_generator(); + Ok(message) } } - check_evidence::("Protocol error: Round 3: failed to verify `П^{elog}` proof.").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 3): failed to verify `П^{elog}` proof.", + ) } #[test] -fn r4_invalid_signature_share() { +fn r4_invalid_signature_share() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_normal_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 4 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - message.sigma = Scalar::random(rng); - return NormalBroadcast::new(format, message); - } - - Ok(normal_broadcast) + impl RoundExtension for Override { + type Round = Round4; + + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; + message.sigma = Scalar::random(rng); + Ok(message) } } - check_evidence::("Protocol error: Round 4: signature share verification failed.").unwrap(); -} - -fn force_round5_on_malicious_node( - rng: &mut dyn CryptoRngCore, - round: BoxedRound>, - payloads: BTreeMap, - artifacts: BTreeMap, -) -> Result>, LocalError> { - if round.id() == 3 { - // Manually start the error round in the malicious node - - let round3 = round.downcast::>()?; - let payloads = payloads.downcast_all::>()?; - - let mut deltas = payloads.map_values(|payload| payload.delta); - deltas.insert(round3.context.my_id, round3.r3_echo_broadcast.delta); - - let mut cap_ks = round3.r1_payloads.map_values_ref(|payload| payload.cap_k.clone()); - cap_ks.insert(round3.context.my_id, round3.cap_k); - - let outcome = FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(Round5 { - context: round3.context, - deltas, - betas: round3.betas, - ss: round3.ss, - rs: round3.rs, - cap_gammas: round3.cap_gammas, - cap_ks, - cap_ds: round3.cap_ds, - cap_fs: round3.cap_fs, - })); - - return Ok(FinalizeOverride::Override(outcome)); - } - - if round.id() == 5 { - // Return a bogus signature in the malicious node, - // so that it finishes successfully. - // Since it's the only malicious node, all the messages it receives will contain - // valid correctness proofs, which will normally lead to a finalization error. - return Ok(FinalizeOverride::Override(FinalizeOutcome::Result( - RecoverableSignature::random(rng).ok_or_else(|| LocalError::new("Failed to create signature"))?, - ))); - } - - Ok(FinalizeOverride::UseDefault { - round, - payloads, - artifacts, - }) + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 4): signature share verification failed.", + ) } #[test] -fn r5_dec_failed() { - struct Override; - - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_echo_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 3 { - // Trigger the error round in lawful nodes - let mut message = echo_broadcast.deserialize::>(format).unwrap(); - message.delta = Scalar::random(rng); - return EchoBroadcast::new(format, message); - } - - Ok(echo_broadcast) +fn r5_dec_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] + struct R3Override; + + impl RoundExtension for R3Override { + type Round = Round3; + + fn make_echo_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_echo_broadcast(rng)?; + message.delta = Scalar::random(rng); + Ok(message) } - fn override_finalize( - rng: &mut dyn CryptoRngCore, - round: BoxedRound>::Protocol>, - _behavior: &(), - payloads: BTreeMap, - artifacts: BTreeMap, - ) -> Result>::Protocol>, LocalError> { - force_round5_on_malicious_node(rng, round, payloads, artifacts) + fn finalize( + &self, + _rng: &mut impl CryptoRngCore, + round: Self::Round, + payloads: BTreeMap>::Payload>, + _artifacts: BTreeMap>::Artifact>, + ) -> Result>::Protocol>, LocalError> { + // Manually start the error round in the malicious node + + let mut deltas = payloads.map_values(|payload| payload.delta); + deltas.insert(round.context.my_id, round.r3_echo_broadcast.delta); + + let mut cap_ks = round.r1_payloads.map_values_ref(|payload| payload.cap_k.clone()); + cap_ks.insert(round.context.my_id, round.cap_k); + + Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(Round5 { + context: round.context, + deltas, + betas: round.betas, + ss: round.ss, + rs: round.rs, + cap_gammas: round.cap_gammas, + cap_ks, + cap_ds: round.cap_ds, + cap_fs: round.cap_fs, + }))) } } - check_evidence::("Protocol error: Round 5: `П^{dec}` proof verification failed.").unwrap(); -} - -mod invalid_r5_messages { - use super::*; - struct Override; - - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_echo_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - behavior: &CheckPart, - format: &BoxedFormat, - - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 3 { - // Trigger the error round in lawful nodes - let mut message = echo_broadcast.deserialize::>(format).unwrap(); - message.delta = Scalar::random(rng); - return EchoBroadcast::new(format, message); - } - - // Actual test: supply an invalid message on the malicious node - if round.id() == 5 && behavior == &CheckPart::EchoBroadcast { - return EchoBroadcast::new::<[u8; 0]>(format, []); - } - - Ok(echo_broadcast) - } - - fn modify_normal_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - behavior: &CheckPart, - format: &BoxedFormat, - normal_broadcast: NormalBroadcast, - ) -> Result { - // Actual test: supply an invalid message on the malicious node - if round.id() == 5 && behavior == &CheckPart::NormalBroadcast { - return NormalBroadcast::new::<[u8; 0]>(format, []); - } - - Ok(normal_broadcast) - } - - fn modify_direct_message( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - behavior: &CheckPart, - format: &BoxedFormat, - _destination: &Id, - direct_message: DirectMessage, - artifact: Option, - ) -> Result<(DirectMessage, Option), LocalError> { - // Actual test: supply an invalid message on the malicious node - if round.id() == 5 && behavior == &CheckPart::DirectMessage { - let dm = DirectMessage::new::<[u8; 0]>(format, [])?; - return Ok((dm, artifact)); - } - - Ok((direct_message, artifact)) - } - - fn override_finalize( - rng: &mut dyn CryptoRngCore, - round: BoxedRound>::Protocol>, - _behavior: &CheckPart, - payloads: BTreeMap, - artifacts: BTreeMap, - ) -> Result>::Protocol>, LocalError> { - force_round5_on_malicious_node(rng, round, payloads, artifacts) + #[derive(Debug, Clone)] + struct R5Override; + + impl RoundExtension for R5Override { + type Round = Round5; + + fn finalize( + &self, + rng: &mut impl CryptoRngCore, + _round: Self::Round, + _payloads: BTreeMap>::Payload>, + _artifacts: BTreeMap>::Artifact>, + ) -> Result>::Protocol>, LocalError> { + // Return a bogus signature in the malicious node, + // so that it finishes successfully. + // Since it's the only malicious node, all the messages it receives will contain + // valid correctness proofs, which will normally lead to a finalization error. + Ok(FinalizeOutcome::Result( + RecoverableSignature::random(rng).ok_or_else(|| LocalError::new("Failed to create signature"))?, + )) } } - #[test] - fn echo() { - let (associated_data, entry_points) = make_entry_points(); - - check_evidence_with_behavior::( - &mut OsRng, - entry_points.clone(), - &CheckPart::EchoBroadcast, - &associated_data, - "Echo broadcast error: Deserialization error", - ) - .unwrap(); - } - - #[test] - fn normal() { - let (associated_data, entry_points) = make_entry_points(); - - check_evidence_with_behavior::( - &mut OsRng, - entry_points.clone(), - &CheckPart::NormalBroadcast, - &associated_data, - "Normal broadcast error: The payload was expected to be `None`, but contains a message", - ) - .unwrap(); - } - - #[test] - fn direct_message() { - let (associated_data, entry_points) = make_entry_points(); - - check_evidence_with_behavior::( - &mut OsRng, - entry_points.clone(), - &CheckPart::DirectMessage, - &associated_data, - "Direct message error: The payload was expected to be `None`, but contains a message", - ) - .unwrap(); - } -} - -fn force_round6_on_malicious_node( - rng: &mut dyn CryptoRngCore, - round: BoxedRound>, - payloads: BTreeMap, - artifacts: BTreeMap, -) -> Result>, LocalError> { - if round.id() == 3 { - // Manually start the error round in the malicious node - - let round3 = round.downcast::>()?; - let payloads = payloads.downcast_all::>()?; - - let mut cap_ks = round3.r1_payloads.map_values_ref(|payload| payload.cap_k.clone()); - cap_ks.insert(round3.context.my_id, round3.cap_k); - - let mut cap_ss = payloads.map_values_ref(|payload| payload.cap_s); - cap_ss.insert(round3.context.my_id, round3.r3_normal_broadcast.cap_s); - - let outcome = FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(Round6 { - context: round3.context, - cap_gamma_combined: round3.cap_gamma_combined, - hat_betas: round3.hat_betas, - hat_ss: round3.hat_ss, - hat_rs: round3.hat_rs, - cap_ks, - cap_ss, - hat_cap_ds: round3.hat_cap_ds, - hat_cap_fs: round3.hat_cap_fs, - })); - - return Ok(FinalizeOverride::Override(outcome)); - } - - if round.id() == 6 { - // Return a bogus signature in the malicious node, - // so that it finishes successfully. - // Since it's the only malicious node, all the messages it receives will contain - // valid correctness proofs, which will normally lead to a finalization error. - return Ok(FinalizeOverride::Override(FinalizeOutcome::Result( - RecoverableSignature::random(rng).ok_or_else(|| LocalError::new("Failed to create signature"))?, - ))); - } - - Ok(FinalizeOverride::UseDefault { - round, - payloads, - artifacts, - }) + check_evidence_with_extensions::( + &mut OsRng, + make_entry_points(), + |entry_point| entry_point.with_extension(R3Override).with_extension(R5Override), + "Protocol error (Round 5): `П^{dec}` proof verification failed.", + ) } #[test] -fn r6_dec_failed() { - struct Override; - - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_normal_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - normal_broadcast: NormalBroadcast, - ) -> Result { - // Actual test: supply an invalid message on the malicious node - if round.id() == 3 { - // Trigger the error round in lawful nodes - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - message.cap_s = Scalar::random(rng).mul_by_generator(); - return NormalBroadcast::new(format, message); - } - - Ok(normal_broadcast) +fn r6_dec_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] + struct R3Override; + + impl RoundExtension for R3Override { + type Round = Round3; + + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; + // Trigger the error round in lawful nodes + message.cap_s = Scalar::random(rng).mul_by_generator(); + Ok(message) } - fn override_finalize( - rng: &mut dyn CryptoRngCore, - round: BoxedRound>::Protocol>, - _behavior: &(), - payloads: BTreeMap, - artifacts: BTreeMap, - ) -> Result>::Protocol>, LocalError> { - force_round6_on_malicious_node(rng, round, payloads, artifacts) + fn finalize( + &self, + _rng: &mut impl CryptoRngCore, + round: Self::Round, + payloads: BTreeMap>::Payload>, + _artifacts: BTreeMap>::Artifact>, + ) -> Result>::Protocol>, LocalError> { + // Manually start the error round in the malicious node + + let mut cap_ks = round.r1_payloads.map_values_ref(|payload| payload.cap_k.clone()); + cap_ks.insert(round.context.my_id, round.cap_k); + + let mut cap_ss = payloads.map_values_ref(|payload| payload.cap_s); + cap_ss.insert(round.context.my_id, round.r3_normal_broadcast.cap_s); + + Ok(FinalizeOutcome::AnotherRound(BoxedRound::new(Round6 { + context: round.context, + cap_gamma_combined: round.cap_gamma_combined, + hat_betas: round.hat_betas, + hat_ss: round.hat_ss, + hat_rs: round.hat_rs, + cap_ks, + cap_ss, + hat_cap_ds: round.hat_cap_ds, + hat_cap_fs: round.hat_cap_fs, + }))) } } - check_evidence::("Protocol error: Round 6: `П^{dec}` proof verification failed.").unwrap(); -} - -mod invalid_r6_messages { - use super::*; - struct Override; - - impl Misbehaving for Override { - type EntryPoint = InteractiveSigning; - - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - behavior: &CheckPart, - format: &BoxedFormat, - echo_broadcast: EchoBroadcast, - ) -> Result { - // Actual test: supply an invalid message on the malicious node - if round.id() == 6 && behavior == &CheckPart::EchoBroadcast { - return EchoBroadcast::new::<[u8; 0]>(format, []); - } - - Ok(echo_broadcast) - } - - fn modify_normal_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - behavior: &CheckPart, - format: &BoxedFormat, - - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 3 { - // Trigger the error round in lawful nodes - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - message.cap_s = Scalar::random(rng).mul_by_generator(); - return NormalBroadcast::new(format, message); - } - - // Actual test: supply an invalid message on the malicious node - if round.id() == 6 && behavior == &CheckPart::NormalBroadcast { - return NormalBroadcast::new::<[u8; 0]>(format, []); - } - - Ok(normal_broadcast) - } - - fn modify_direct_message( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - behavior: &CheckPart, - format: &BoxedFormat, - _destination: &Id, - direct_message: DirectMessage, - artifact: Option, - ) -> Result<(DirectMessage, Option), LocalError> { - // Actual test: supply an invalid message on the malicious node - if round.id() == 6 && behavior == &CheckPart::DirectMessage { - let dm = DirectMessage::new::<[u8; 0]>(format, [])?; - return Ok((dm, artifact)); - } - - Ok((direct_message, artifact)) + #[derive(Debug, Clone)] + struct R6Override; + + impl RoundExtension for R6Override { + type Round = Round6; + + fn finalize( + &self, + rng: &mut impl CryptoRngCore, + _round: Self::Round, + _payloads: BTreeMap>::Payload>, + _artifacts: BTreeMap>::Artifact>, + ) -> Result>::Protocol>, LocalError> { + // Return a bogus signature in the malicious node, + // so that it finishes successfully. + // Since it's the only malicious node, all the messages it receives will contain + // valid correctness proofs, which will normally lead to a finalization error. + Ok(FinalizeOutcome::Result( + RecoverableSignature::random(rng).ok_or_else(|| LocalError::new("Failed to create signature"))?, + )) } - - fn override_finalize( - rng: &mut dyn CryptoRngCore, - round: BoxedRound>::Protocol>, - _behavior: &CheckPart, - payloads: BTreeMap, - artifacts: BTreeMap, - ) -> Result>::Protocol>, LocalError> { - force_round6_on_malicious_node(rng, round, payloads, artifacts) - } - } - - #[test] - fn echo() { - let (associated_data, entry_points) = make_entry_points(); - - check_evidence_with_behavior::( - &mut OsRng, - entry_points.clone(), - &CheckPart::EchoBroadcast, - &associated_data, - "Echo broadcast error: Deserialization error", - ) - .unwrap(); } - #[test] - fn normal() { - let (associated_data, entry_points) = make_entry_points(); - - check_evidence_with_behavior::( - &mut OsRng, - entry_points.clone(), - &CheckPart::NormalBroadcast, - &associated_data, - "Normal broadcast error: The payload was expected to be `None`, but contains a message", - ) - .unwrap(); - } - - #[test] - fn direct_message() { - let (associated_data, entry_points) = make_entry_points(); - - check_evidence_with_behavior::( - &mut OsRng, - entry_points.clone(), - &CheckPart::DirectMessage, - &associated_data, - "Direct message error: The payload was expected to be `None`, but contains a message", - ) - .unwrap(); - } + check_evidence_with_extensions::( + &mut OsRng, + make_entry_points(), + |entry_point| entry_point.with_extension(R3Override).with_extension(R6Override), + "Protocol error (Round 6): `П^{dec}` proof verification failed.", + ) } diff --git a/src/protocols/misbehavior_tests/key_init.rs b/src/protocols/misbehavior_tests/key_init.rs index 7e88743c..3afa4a1e 100644 --- a/src/protocols/misbehavior_tests/key_init.rs +++ b/src/protocols/misbehavior_tests/key_init.rs @@ -1,22 +1,19 @@ use alloc::collections::BTreeSet; use manul::{ - combinators::misbehave::Misbehaving, - dev::{BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, - protocol::{BoxedFormat, BoxedRound, EntryPoint, LocalError, NormalBroadcast, ProtocolMessagePart}, + dev::{BinaryFormat, RoundExtension, TestSessionParams, TestSigner, TestVerifier, check_evidence_with_extension}, + protocol::{LocalError, Round}, signature::Keypair, }; use rand_core::{CryptoRngCore, OsRng}; -use super::super::key_init::{KeyInit, KeyInitAssociatedData, Round2NormalBroadcast, Round3, Round3NormalBroadcast}; +use super::super::key_init::{ + KeyInit, KeyInitSharedData, Round2, Round2NormalBroadcast, Round3, Round3NormalBroadcast, +}; use crate::{ curve::Scalar, dev::TestParams, - tools::{ - bitvec::BitVec, - protocol_shortcuts_dev::{check_evidence_with_behavior, check_invalid_message_evidence, CheckPart}, - Secret, - }, + tools::{Secret, bitvec::BitVec}, zk::SchProof, }; @@ -25,7 +22,7 @@ type P = TestParams; type SP = TestSessionParams; #[allow(clippy::type_complexity)] -fn make_entry_points() -> (KeyInitAssociatedData, Vec<(TestSigner, KeyInit)>) { +fn make_entry_points() -> (Vec<(TestSigner, KeyInit)>, KeyInitSharedData) { let signers = (0..3).map(TestSigner::new).collect::>(); let all_ids = signers .iter() @@ -37,181 +34,75 @@ fn make_entry_points() -> (KeyInitAssociatedData, Vec<(TestSigner, KeyInit

(expected_description: &str) -> Result<(), LocalError> -where - M: Misbehaving>, -{ - let (associated_data, entry_points) = make_entry_points(); - check_evidence_with_behavior::( - &mut OsRng, - entry_points.clone(), - &(), - &associated_data, - expected_description, - ) -} - -#[test] -fn invalid_messages() { - let (associated_data, entry_points) = make_entry_points(); - - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 1, - CheckPart::EchoBroadcast, - &associated_data, - true, - ) - .unwrap(); - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 2, - CheckPart::EchoBroadcast, - &associated_data, - true, - ) - .unwrap(); - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 3, - CheckPart::EchoBroadcast, - &associated_data, - false, - ) - .unwrap(); - - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 1, - CheckPart::NormalBroadcast, - &associated_data, - false, - ) - .unwrap(); - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 2, - CheckPart::NormalBroadcast, - &associated_data, - true, - ) - .unwrap(); - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 3, - CheckPart::NormalBroadcast, - &associated_data, - true, - ) - .unwrap(); - - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 1, - CheckPart::DirectMessage, - &associated_data, - false, - ) - .unwrap(); - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 2, - CheckPart::DirectMessage, - &associated_data, - false, - ) - .unwrap(); - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 3, - CheckPart::DirectMessage, - &associated_data, - false, - ) - .unwrap(); + (entry_points, KeyInitSharedData { ids: all_ids }) } #[test] -fn r2_hash_mismatch() { +fn r2_hash_mismatch() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = KeyInit; - - fn modify_normal_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); + impl RoundExtension for Override { + type Round = Round2; - // Replace `u` with something other than we committed to when hashing it in Round 1. - message.u = BitVec::random(rng, message.u.bits().len()); + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; - return NormalBroadcast::new(format, message); - } + // Replace `u` with something other than we committed to when hashing it in Round 1. + message.u = BitVec::random(rng, message.u.bits().len()); - Ok(normal_broadcast) + Ok(message) } } - check_evidence::("Protocol error: Round 2: the previously sent hash does not match the public data.") - .unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 2): The previously sent hash does not match the public data.", + ) } #[test] fn r3_invalid_sch_proof() { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = KeyInit; - - fn modify_normal_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 3 { - let round3 = round.downcast_ref::>()?; - let context = &round3.context; - let aux = (&context.sid, &context.my_id, &round3.rho_combined); - - // Make a proof for a random secret. This won't pass verification. - let x = Secret::init_with(|| Scalar::random(rng)); - let psi = SchProof::new( - &context.tau, - &x, - &context.public_data.cap_a, - &x.mul_by_generator(), - &aux, - ); - - let message = Round3NormalBroadcast { psi }; - return NormalBroadcast::new(format, message); - } - - Ok(normal_broadcast) + impl RoundExtension for Override { + type Round = Round3; + + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let context = &round.context; + let aux = (&context.sid, &context.my_id, &round.rho_combined); + + // Make a proof for a random secret. This won't pass verification. + let x = Secret::init_with(|| Scalar::random(rng)); + let psi = SchProof::new( + &context.tau, + &x, + &context.public_data.cap_a, + &x.mul_by_generator(), + &aux, + ); + + let message = Round3NormalBroadcast { psi }; + Ok(message) } } - check_evidence::("Protocol error: Round 3: failed to verify `П^{sch}`").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 3): Failed to verify `П^{sch}`.", + ) + .unwrap(); } diff --git a/src/protocols/misbehavior_tests/key_refresh.rs b/src/protocols/misbehavior_tests/key_refresh.rs index 4759a9ee..4ba53a62 100644 --- a/src/protocols/misbehavior_tests/key_refresh.rs +++ b/src/protocols/misbehavior_tests/key_refresh.rs @@ -1,31 +1,26 @@ use alloc::collections::BTreeSet; use manul::{ - combinators::misbehave::Misbehaving, - dev::{BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, - protocol::{ - Artifact, BoxedFormat, BoxedRound, DirectMessage, EchoBroadcast, EntryPoint, LocalError, NormalBroadcast, - ProtocolMessagePart, + dev::{ + BinaryFormat, RoundExtension, TestSessionParams, TestSigner, TestVerifier, check_evidence_with_extension, + check_evidence_with_extensions, }, + protocol::{LocalError, Round}, signature::Keypair, }; use rand_chacha::ChaCha8Rng; use rand_core::{CryptoRngCore, OsRng, SeedableRng}; use super::super::key_refresh::{ - KeyRefresh, KeyRefreshAssociatedData, Round1, Round1EchoBroadcast, Round2EchoBroadcast, Round2NormalBroadcast, - Round3DirectMessage, Round3EchoBroadcast, Round3NormalBroadcast, + KeyRefresh, KeyRefreshSharedData, Round1, Round1EchoBroadcast, Round2, Round2EchoBroadcast, Round2NormalBroadcast, + Round3, Round3DirectMessage, Round3EchoBroadcast, Round3NormalBroadcast, }; use crate::{ curve::Scalar, dev::TestParams, paillier::{PaillierParams, PublicKeyPaillierWire, RPParams, RPParamsWire, RPSecret, SecretKeyPaillierWire}, params::SchemeParams, - tools::{ - hashing::Hasher, - protocol_shortcuts_dev::{check_evidence_with_behavior, check_invalid_message_evidence, CheckPart}, - Secret, - }, + tools::{Secret, hashing::Hasher}, zk::{FacProof, ModProof, PrmProof, SchCommitment, SchProof, SchSecret}, }; @@ -34,7 +29,7 @@ type P = TestParams; type SP = TestSessionParams; #[allow(clippy::type_complexity)] -fn make_entry_points() -> (KeyRefreshAssociatedData, Vec<(TestSigner, KeyRefresh)>) { +fn make_entry_points() -> (Vec<(TestSigner, KeyRefresh)>, KeyRefreshSharedData) { let signers = (0..3).map(TestSigner::new).collect::>(); let all_ids = signers.iter().map(TestSigner::verifying_key).collect::>(); @@ -42,697 +37,531 @@ fn make_entry_points() -> (KeyRefreshAssociatedData, Vec<(TestSigner, KeyRef .into_iter() .map(|signer| (signer, KeyRefresh::new(all_ids.clone()).unwrap())) .collect(); - (KeyRefreshAssociatedData { ids: all_ids }, entry_points) -} - -fn check_evidence(expected_description: &str) -> Result<(), LocalError> -where - M: Misbehaving>, -{ - let (associated_data, entry_points) = make_entry_points().clone(); - check_evidence_with_behavior::(&mut OsRng, entry_points, &(), &associated_data, expected_description) + (entry_points, KeyRefreshSharedData { ids: all_ids }) } #[test] -fn invalid_messages() { - let (associated_data, entry_points) = make_entry_points(); - - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 1, - CheckPart::EchoBroadcast, - &associated_data, - true, - ) - .unwrap(); - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 2, - CheckPart::EchoBroadcast, - &associated_data, - true, - ) - .unwrap(); - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 3, - CheckPart::EchoBroadcast, - &associated_data, - true, - ) - .unwrap(); +fn r2_hash_mismatch() -> Result<(), LocalError> { + #[derive(Debug, Clone)] + struct Override; - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 1, - CheckPart::NormalBroadcast, - &associated_data, - false, - ) - .unwrap(); - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 2, - CheckPart::NormalBroadcast, - &associated_data, - true, - ) - .unwrap(); - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 3, - CheckPart::NormalBroadcast, - &associated_data, - true, - ) - .unwrap(); + impl RoundExtension for Override { + type Round = Round1; + + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + _round: &Self::Round, + ) -> Result { + Ok(Round1EchoBroadcast { + cap_v: Hasher::<

::Digest>::new_with_dst(b"bad hash").finalize(P::SECURITY_BITS), + }) + } + } - check_invalid_message_evidence::( + check_evidence_with_extension::( &mut OsRng, - entry_points.clone(), - 1, - CheckPart::DirectMessage, - &associated_data, - false, + make_entry_points(), + Override, + "Protocol error (Round 2): The previously sent hash does not match the public data.", ) - .unwrap(); - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 2, - CheckPart::DirectMessage, - &associated_data, - false, - ) - .unwrap(); - check_invalid_message_evidence::( - &mut OsRng, - entry_points.clone(), - 3, - CheckPart::DirectMessage, - &associated_data, - true, - ) - .unwrap(); } #[test] -fn r2_hash_mismatch() { - struct Override; - - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - // Send a wrong hash in the Round 1 message - let message = Round1EchoBroadcast { - cap_v: Hasher::<

::Digest>::new_with_dst(b"bad hash").finalize(P::SECURITY_BITS), - }; - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) +fn r2_wrong_ids_x() -> Result<(), LocalError> { + // Technically we only need to modify `X`, but we need to substitute the hash in Round 1 too, + // so that in Round 2 the hash check could pass and the execution reaches the IDs check. + #[derive(Debug, Clone)] + struct R1Override; + + impl RoundExtension for R1Override { + type Round = Round1; + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result { + let mut data = round.public_data.clone(); + data.cap_xs.pop_first(); + Ok(Round1EchoBroadcast { + cap_v: data.hash(&round.context.sid, &round.context.my_id), + }) } } - check_evidence::("Protocol error: Round 2: the previously sent hash does not match the public data.") - .unwrap(); -} - -#[test] -fn r2_wrong_ids_x() { - struct Override; + #[derive(Debug, Clone)] + struct R2Override; - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_normal_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - message.cap_xs.pop_first(); - let normal_broadcast = NormalBroadcast::new(format, message)?; - return Ok(normal_broadcast); - } - - Ok(normal_broadcast) - } + impl RoundExtension for R2Override { + type Round = Round2; - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - // Technically we only need to modify `X`, but we need to substitute the hash in Round 1 too, - // so that in Round 2 the hash check could pass and the execution reaches the IDs check. - let round1 = round.downcast_ref::>()?; - - let mut data = round1.public_data.clone(); - data.cap_xs.pop_first(); - - let message = Round1EchoBroadcast { - cap_v: data.hash(&round1.context.sid, &round1.context.my_id), - }; - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; + message.cap_xs.pop_first(); + Ok(message) } } - check_evidence::("Protocol error: Round 2: wrong IDs in public shares map.").unwrap(); + check_evidence_with_extensions::( + &mut OsRng, + make_entry_points(), + |entry_point| entry_point.with_extension(R1Override).with_extension(R2Override), + "Protocol error (Round 2): Wrong IDs in public shares map.", + ) } #[test] -fn r2_wrong_ids_y() { - struct Override; - - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - // Technically we only need to modify `X`, but we need to substitute the hash in Round 1 too, - // so that in Round 2 the hash check could pass and the execution reaches the IDs check. - let round1 = round.downcast_ref::>()?; - - let mut data = round1.public_data.clone(); - data.cap_ys.pop_first(); - - let message = Round1EchoBroadcast { - cap_v: data.hash(&round1.context.sid, &round1.context.my_id), - }; - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - if round.id() == 2 { - let mut message = echo_broadcast - .deserialize::>(format) - .unwrap(); - message.cap_ys.pop_first(); - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) +fn r2_wrong_ids_y() -> Result<(), LocalError> { + // Technically we only need to modify `Y`, but we need to substitute the hash in Round 1 too, + // so that in Round 2 the hash check could pass and the execution reaches the IDs check. + #[derive(Debug, Clone)] + struct R1Override; + + impl RoundExtension for R1Override { + type Round = Round1; + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result { + let mut data = round.public_data.clone(); + data.cap_ys.pop_first(); + Ok(Round1EchoBroadcast { + cap_v: data.hash(&round.context.sid, &round.context.my_id), + }) } } - check_evidence::("Protocol error: Round 2: wrong IDs in Elgamal keys map.").unwrap(); -} - -#[test] -fn r2_wrong_ids_a() { - struct Override; - - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_normal_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, + #[derive(Debug, Clone)] + struct R2Override; - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); + impl RoundExtension for R2Override { + type Round = Round2; - message.cap_as.pop_first(); - let normal_broadcast = NormalBroadcast::new(format, message)?; - return Ok(normal_broadcast); - } - - Ok(normal_broadcast) + fn make_echo_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_echo_broadcast(rng)?; + message.cap_ys.pop_first(); + Ok(message) } + } - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - // Technically we only need to modify `A`, but we need to substitute the hash in Round 1 too, - // so that in Round 2 the hash check could pass and the execution reaches the IDs check. - let round1 = round.downcast_ref::>()?; + check_evidence_with_extensions::( + &mut OsRng, + make_entry_points(), + |entry_point| entry_point.with_extension(R1Override).with_extension(R2Override), + "Protocol error (Round 2): Wrong IDs in Elgamal keys map.", + ) +} - let mut data = round1.public_data.clone(); +#[test] +fn r2_wrong_ids_a() -> Result<(), LocalError> { + // Technically we only need to modify `A`, but we need to substitute the hash in Round 1 too, + // so that in Round 2 the hash check could pass and the execution reaches the IDs check. + #[derive(Debug, Clone)] + struct R1Override; + + impl RoundExtension for R1Override { + type Round = Round1; + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result { + let mut data = round.public_data.clone(); + data.cap_as.pop_first(); + Ok(Round1EchoBroadcast { + cap_v: data.hash(&round.context.sid, &round.context.my_id), + }) + } + } - data.cap_as.pop_first(); + #[derive(Debug, Clone)] + struct R2Override; - let message = Round1EchoBroadcast { - cap_v: data.hash(&round1.context.sid, &round1.context.my_id), - }; - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } + impl RoundExtension for R2Override { + type Round = Round2; - Ok(echo_broadcast) + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; + message.cap_as.pop_first(); + Ok(message) } } - check_evidence::("Protocol error: Round 2: wrong IDs in Schnorr commitments map.").unwrap(); + check_evidence_with_extensions::( + &mut OsRng, + make_entry_points(), + |entry_point| entry_point.with_extension(R1Override).with_extension(R2Override), + "Protocol error (Round 2): Wrong IDs in Schnorr commitments map.", + ) } #[test] -fn r2_paillier_modulus_too_small() { +fn r2_paillier_modulus_too_small() -> Result<(), LocalError> { fn make_small_modulus_pk() -> PublicKeyPaillierWire

{ let mut rng = ChaCha8Rng::seed_from_u64(123); let paillier_sk = SecretKeyPaillierWire::

::random_small(&mut rng); paillier_sk.public_key() } - struct Override; - - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_normal_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - message.paillier_pk = make_small_modulus_pk::<

::Paillier>(); - let normal_broadcast = NormalBroadcast::new(format, message)?; - return Ok(normal_broadcast); - } - - Ok(normal_broadcast) + #[derive(Debug, Clone)] + struct R1Override; + + impl RoundExtension for R1Override { + type Round = Round1; + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result { + let mut data = round.public_data.clone(); + data.paillier_pk = make_small_modulus_pk::<

::Paillier>().into_precomputed(); + Ok(Round1EchoBroadcast { + cap_v: data.hash(&round.context.sid, &round.context.my_id), + }) } + } + + #[derive(Debug, Clone)] + struct R2Override; + + impl RoundExtension for R2Override { + type Round = Round2; - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - let round1 = round.downcast_ref::>()?; - let mut data = round1.public_data.clone(); - data.paillier_pk = make_small_modulus_pk::<

::Paillier>().into_precomputed(); - let message = Round1EchoBroadcast { - cap_v: data.hash(&round1.context.sid, &round1.context.my_id), - }; - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; + message.paillier_pk = make_small_modulus_pk::<

::Paillier>(); + Ok(message) } } - check_evidence::("Protocol error: Round 2: Paillier modulus is too small.").unwrap(); + check_evidence_with_extensions::( + &mut OsRng, + make_entry_points(), + |entry_point| entry_point.with_extension(R1Override).with_extension(R2Override), + "Protocol error (Round 2): Paillier modulus is too small.", + ) } #[test] -fn r2_rp_modulus_too_small() { +fn r2_rp_modulus_too_small() -> Result<(), LocalError> { fn make_small_modulus_rp_params() -> RPParamsWire

{ let mut rng = ChaCha8Rng::seed_from_u64(123); RPParams::random_small(&mut rng).to_wire() } - struct Override; - - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - // Technically we only need to modify `X`, but we need to substitute the hash in Round 1 too, - // so that in Round 2 the hash check could pass and the execution reaches the IDs check. - let round1 = round.downcast_ref::>()?; - - let mut data = round1.public_data.clone(); - data.rp_params = make_small_modulus_rp_params::<

::Paillier>().to_precomputed(); - - let message = Round1EchoBroadcast { - cap_v: data.hash(&round1.context.sid, &round1.context.my_id), - }; - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - if round.id() == 2 { - let mut message = echo_broadcast - .deserialize::>(format) - .unwrap(); - message.rp_params = make_small_modulus_rp_params::<

::Paillier>(); - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) + #[derive(Debug, Clone)] + struct R1Override; + + impl RoundExtension for R1Override { + type Round = Round1; + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result { + let mut data = round.public_data.clone(); + data.rp_params = make_small_modulus_rp_params::<

::Paillier>().to_precomputed(); + + Ok(Round1EchoBroadcast { + cap_v: data.hash(&round.context.sid, &round.context.my_id), + }) } } - check_evidence::("Protocol error: Round 2: ring-Pedersent modulus is too small.").unwrap(); -} + #[derive(Debug, Clone)] + struct R2Override; -#[test] -fn r2_non_zero_sum_of_changes() { - struct Override; + impl RoundExtension for R2Override { + type Round = Round2; - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - let round1 = round.downcast_ref::>()?; - let mut data = round1.public_data.clone(); - - let (id, _point) = data.cap_xs.pop_first().unwrap(); - let mut rng = ChaCha8Rng::seed_from_u64(123); - data.cap_xs.insert(id, Scalar::random(&mut rng).mul_by_generator()); - - let message = Round1EchoBroadcast { - cap_v: data.hash(&round1.context.sid, &round1.context.my_id), - }; - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) + fn make_echo_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_echo_broadcast(rng)?; + message.rp_params = make_small_modulus_rp_params::<

::Paillier>(); + Ok(message) } + } - fn modify_normal_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - - let (id, _point) = message.cap_xs.pop_first().unwrap(); - let mut rng = ChaCha8Rng::seed_from_u64(123); - message.cap_xs.insert(id, Scalar::random(&mut rng).mul_by_generator()); + check_evidence_with_extensions::( + &mut OsRng, + make_entry_points(), + |entry_point| entry_point.with_extension(R1Override).with_extension(R2Override), + "Protocol error (Round 2): Ring-Pedersen modulus is too small.", + ) +} - let normal_broadcast = NormalBroadcast::new(format, message)?; - return Ok(normal_broadcast); - } +#[test] +fn r2_non_zero_sum_of_changes() -> Result<(), LocalError> { + #[derive(Debug, Clone)] + struct R1Override; + + impl RoundExtension for R1Override { + type Round = Round1; + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result { + let mut data = round.public_data.clone(); + + let (id, _point) = data.cap_xs.pop_first().unwrap(); + let mut rng = ChaCha8Rng::seed_from_u64(123); + data.cap_xs.insert(id, Scalar::random(&mut rng).mul_by_generator()); + + Ok(Round1EchoBroadcast { + cap_v: data.hash(&round.context.sid, &round.context.my_id), + }) + } + } - Ok(normal_broadcast) + #[derive(Debug, Clone)] + struct R2Override; + + impl RoundExtension for R2Override { + type Round = Round2; + + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; + let (id, _point) = message.cap_xs.pop_first().unwrap(); + let mut rng = ChaCha8Rng::seed_from_u64(123); + message.cap_xs.insert(id, Scalar::random(&mut rng).mul_by_generator()); + Ok(message) } } - check_evidence::("Protocol error: Round 2: sum of share changes is not zero.").unwrap(); + check_evidence_with_extensions::( + &mut OsRng, + make_entry_points(), + |entry_point| entry_point.with_extension(R1Override).with_extension(R2Override), + "Protocol error (Round 2): Sum of share changes is not zero.", + ) } #[test] -fn r2_prm_failed() { - struct Override; - - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 1 { - let round1 = round.downcast_ref::>()?; - let mut data = round1.public_data.clone(); - - let mut rng = ChaCha8Rng::seed_from_u64(123); - let secret = RPSecret::random(&mut rng); - let rp_params = RPParams::random_with_secret(&mut rng, &secret); - data.psi = PrmProof::new(&mut rng, &secret, &rp_params, &1u8); - - let message = Round1EchoBroadcast { - cap_v: data.hash(&round1.context.sid, &round1.context.my_id), - }; - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) +fn r2_prm_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] + struct R1Override; + + impl RoundExtension for R1Override { + type Round = Round1; + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result { + let mut data = round.public_data.clone(); + + let mut rng = ChaCha8Rng::seed_from_u64(123); + let secret = RPSecret::random(&mut rng); + let rp_params = RPParams::random_with_secret(&mut rng, &secret); + data.psi = PrmProof::new(&mut rng, &secret, &rp_params, &1u8); + + Ok(Round1EchoBroadcast { + cap_v: data.hash(&round.context.sid, &round.context.my_id), + }) } + } + + #[derive(Debug, Clone)] + struct R2Override; + + impl RoundExtension for R2Override { + type Round = Round2; + + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; - fn modify_normal_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 2 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); - - let mut rng = ChaCha8Rng::seed_from_u64(123); - let secret = RPSecret::random(&mut rng); - let rp_params = RPParams::random_with_secret(&mut rng, &secret); - message.psi = PrmProof::new(&mut rng, &secret, &rp_params, &1u8); - - let normal_broadcast = NormalBroadcast::new(format, message)?; - return Ok(normal_broadcast); - } - - Ok(normal_broadcast) + let mut rng = ChaCha8Rng::seed_from_u64(123); + let secret = RPSecret::random(&mut rng); + let rp_params = RPParams::random_with_secret(&mut rng, &secret); + message.psi = PrmProof::new(&mut rng, &secret, &rp_params, &1u8); + + Ok(message) } } - check_evidence::("Protocol error: Round 2: `П^{prm}` verification failed.").unwrap(); + check_evidence_with_extensions::( + &mut OsRng, + make_entry_points(), + |entry_point| entry_point.with_extension(R1Override).with_extension(R2Override), + "Protocol error (Round 2): `П^{prm}` verification failed.", + ) } #[test] -fn r3_share_change_mismatch() { +fn r3_share_change_mismatch() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_direct_message( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - _destination: &Id, - direct_message: DirectMessage, - artifact: Option, - ) -> Result<(DirectMessage, Option), LocalError> { - if round.id() == 3 { - let mut message = direct_message.deserialize::>(format).unwrap(); - message.cap_c = Scalar::random(rng); - let direct_message = DirectMessage::new(format, message)?; - return Ok((direct_message, artifact)); - } - - Ok((direct_message, artifact)) + impl RoundExtension for Override { + type Round = Round3; + + fn make_direct_message( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + destination: &Id, + ) -> Result<(Round3DirectMessage

, ()), LocalError> { + let (mut message, artifact) = round.make_direct_message(rng, destination)?; + message.cap_c = Scalar::random(rng); + Ok((message, artifact)) } } - check_evidence::("Protocol error: Round 3: secret share change does not match the public commitment.") - .unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 3): Secret share change does not match the public commitment.", + ) } #[test] -fn r3_mod_failed() { +fn r3_mod_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_normal_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, + impl RoundExtension for Override { + type Round = Round3; - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() == 3 { - let mut message = normal_broadcast - .deserialize::>(format) - .unwrap(); + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_normal_broadcast(rng)?; - let sk = SecretKeyPaillierWire::random(rng).into_precomputed(); - message.psi_prime = ModProof::new(rng, &sk, &1u8); + let sk = SecretKeyPaillierWire::random(rng).into_precomputed(); + message.psi_prime = ModProof::new(rng, &sk, &1u8); - let normal_broadcast = NormalBroadcast::new(format, message)?; - return Ok(normal_broadcast); - } - - Ok(normal_broadcast) + Ok(message) } } - check_evidence::("Protocol error: Round 3: `П^{mod}` verification failed.").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 3): `П^{mod}` verification failed.", + ) } #[test] -fn r3_fac_failed() { +fn r3_fac_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_direct_message( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - _destination: &Id, - direct_message: DirectMessage, - artifact: Option, - ) -> Result<(DirectMessage, Option), LocalError> { - if round.id() == 3 { - let mut message = direct_message.deserialize::>(format).unwrap(); - let sk = SecretKeyPaillierWire::random(&mut OsRng).into_precomputed(); - let rp_params = RPParams::random(rng); - message.psi = FacProof::new(rng, &sk, &rp_params, &1u8); - let direct_message = DirectMessage::new(format, message)?; - return Ok((direct_message, artifact)); - } - - Ok((direct_message, artifact)) + impl RoundExtension for Override { + type Round = Round3; + + fn make_direct_message( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + destination: &Id, + ) -> Result<(Round3DirectMessage

, ()), LocalError> { + let (mut message, artifact) = round.make_direct_message(rng, destination)?; + + let sk = SecretKeyPaillierWire::random(&mut OsRng).into_precomputed(); + let rp_params = RPParams::random(rng); + message.psi = FacProof::new(rng, &sk, &rp_params, &1u8); + + Ok((message, artifact)) } } - check_evidence::("Protocol error: Round 3: `П^{fac}` verification failed.").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 3): `П^{fac}` verification failed.", + ) } #[test] -fn r3_wrong_ids_hat_psi() { +fn r3_wrong_ids_hat_psi() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 3 { - let mut message = echo_broadcast - .deserialize::>(format) - .unwrap(); - message.hat_psis.pop_first(); - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) + impl RoundExtension for Override { + type Round = Round3; + + fn make_echo_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_echo_broadcast(rng)?; + message.hat_psis.pop_first(); + Ok(message) } } - check_evidence::("Protocol error: Round 3: Wrong IDs in Schnorr proofs map.").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 3): Wrong IDs in Schnorr proofs map.", + ) } #[test] -fn r3_sch_failed() { +fn r3_sch_failed() -> Result<(), LocalError> { + #[derive(Debug, Clone)] struct Override; - impl Misbehaving for Override { - type EntryPoint = KeyRefresh; - - fn modify_echo_broadcast( - rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - _behavior: &(), - format: &BoxedFormat, - - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() == 3 { - let mut message = echo_broadcast - .deserialize::>(format) - .unwrap(); - let (id, _hat_psi) = message.hat_psis.pop_last().unwrap(); - let x = Secret::init_with(|| Scalar::random(rng)); - let cap_x = x.mul_by_generator(); - let secret = SchSecret::random(rng); - let commitment = SchCommitment::new(&secret); - let hat_psi = SchProof::new(&secret, &x, &commitment, &cap_x, &1u8); - message.hat_psis.insert(id, hat_psi); - let echo_broadcast = EchoBroadcast::new(format, message)?; - return Ok(echo_broadcast); - } - - Ok(echo_broadcast) + impl RoundExtension for Override { + type Round = Round3; + + fn make_echo_broadcast( + &self, + rng: &mut impl CryptoRngCore, + round: &Self::Round, + ) -> Result, LocalError> { + let mut message = round.make_echo_broadcast(rng)?; + let (id, _hat_psi) = message.hat_psis.pop_last().unwrap(); + let x = Secret::init_with(|| Scalar::random(rng)); + let cap_x = x.mul_by_generator(); + let secret = SchSecret::random(rng); + let commitment = SchCommitment::new(&secret); + let hat_psi = SchProof::new(&secret, &x, &commitment, &cap_x, &1u8); + message.hat_psis.insert(id, hat_psi); + Ok(message) } } - check_evidence::("Protocol error: Round 3: `П^{sch}` verification failed.").unwrap(); + check_evidence_with_extension::( + &mut OsRng, + make_entry_points(), + Override, + "Protocol error (Round 3): `П^{sch}` verification failed.", + ) } diff --git a/src/tests/threshold.rs b/src/tests/threshold.rs index e1d38b41..df3c9151 100644 --- a/src/tests/threshold.rs +++ b/src/tests/threshold.rs @@ -1,10 +1,10 @@ use std::collections::{BTreeMap, BTreeSet}; use digest::typenum::Unsigned; -use ecdsa::{signature::hazmat::PrehashVerifier, VerifyingKey}; +use ecdsa::{VerifyingKey, signature::hazmat::PrehashVerifier}; use elliptic_curve::{Curve, FieldBytes}; use manul::{ - dev::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, + dev::{BinaryFormat, TestSessionParams, TestSigner, TestVerifier, run_sync}, signature::Keypair, }; use rand_core::OsRng; diff --git a/src/tools.rs b/src/tools.rs index d90053df..0577a48a 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -1,12 +1,6 @@ pub(crate) mod bitvec; -mod boxed_rng; pub(crate) mod hashing; -pub(crate) mod protocol_shortcuts; mod secret; pub(crate) mod sss; -#[cfg(test)] -pub(crate) mod protocol_shortcuts_dev; - -pub(crate) use boxed_rng::BoxedRng; pub(crate) use secret::Secret; diff --git a/src/tools/bitvec.rs b/src/tools/bitvec.rs index 2484b7e7..03bea37a 100644 --- a/src/tools/bitvec.rs +++ b/src/tools/bitvec.rs @@ -69,7 +69,7 @@ impl BitVec { Self(bit_vec.into()) } - pub fn random(rng: &mut dyn CryptoRngCore, bits: usize) -> Self { + pub fn random(rng: &mut impl CryptoRngCore, bits: usize) -> Self { let bytes = bits.div_ceil(8); let mut byte_vec = vec![0; bytes]; rng.fill_bytes(&mut byte_vec); diff --git a/src/tools/boxed_rng.rs b/src/tools/boxed_rng.rs deleted file mode 100644 index 81a9a5ea..00000000 --- a/src/tools/boxed_rng.rs +++ /dev/null @@ -1,23 +0,0 @@ -use rand_core::{CryptoRng, CryptoRngCore, RngCore}; - -/// We get a `dyn CryptoRngCore` from `manul::protocol` trait methods, but some dependencies -/// do not accept `?Sized` arguments (yet). This wrapper turns a dyn trait object into a static type -/// implementing `CryptoRngCore`. -pub(crate) struct BoxedRng<'a>(pub(crate) &'a mut dyn CryptoRngCore); - -impl CryptoRng for BoxedRng<'_> {} - -impl RngCore for BoxedRng<'_> { - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest) - } - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { - self.0.try_fill_bytes(dest) - } -} diff --git a/src/tools/protocol_shortcuts.rs b/src/tools/protocol_shortcuts.rs deleted file mode 100644 index 0dd103ac..00000000 --- a/src/tools/protocol_shortcuts.rs +++ /dev/null @@ -1,166 +0,0 @@ -use alloc::{ - collections::{BTreeMap, BTreeSet}, - format, -}; -use core::fmt::Debug; - -use manul::protocol::{ - Artifact, BoxedFormat, EchoBroadcast, LocalError, Payload, ProtocolMessagePart, ProtocolValidationError, RoundId, -}; -use serde::Deserialize; - -pub(crate) trait Without { - type Item; - fn without(self, item: &Self::Item) -> Self; -} - -impl Without for BTreeSet { - type Item = T; - fn without(self, item: &Self::Item) -> Self { - let mut set = self; - set.remove(item); - set - } -} - -pub(crate) trait MapValues { - fn map_values(self, f: F) -> BTreeMap - where - F: Fn(V) -> T; - - fn map_values_ref(&self, f: F) -> BTreeMap - where - K: Clone, - F: Fn(&V) -> T; -} - -impl MapValues for BTreeMap { - fn map_values(self, f: F) -> BTreeMap - where - F: Fn(V) -> T, - { - self.into_iter().map(|(key, value)| (key, f(value))).collect() - } - - fn map_values_ref(&self, f: F) -> BTreeMap - where - K: Clone, - F: Fn(&V) -> T, - { - self.iter().map(|(key, value)| (key.clone(), f(value))).collect() - } -} - -pub(crate) trait DowncastMap { - type Key; - fn downcast_all(self) -> Result, LocalError>; -} - -impl DowncastMap for BTreeMap { - type Key = K; - fn downcast_all(self) -> Result, LocalError> { - self.into_iter() - .map(|(k, payload)| payload.downcast::().map(|v| (k, v))) - .collect::>() - } -} - -impl DowncastMap for BTreeMap { - type Key = K; - fn downcast_all(self) -> Result, LocalError> { - self.into_iter() - .map(|(k, artifact)| artifact.downcast::().map(|v| (k, v))) - .collect::>() - } -} - -pub(crate) trait SafeGet { - fn safe_get(&self, container: &str, key: &K) -> Result<&V, LocalError>; - fn try_get(&self, container: &str, key: &K) -> Result<&V, ProtocolValidationError>; -} - -impl SafeGet for BTreeMap { - fn safe_get(&self, container: &str, key: &K) -> Result<&V, LocalError> { - self.get(key) - .ok_or_else(|| LocalError::new(format!("Key {key:?} not found in {container}"))) - } - - fn try_get(&self, container: &str, key: &K) -> Result<&V, ProtocolValidationError> { - self.get(key) - .ok_or_else(|| ProtocolValidationError::InvalidEvidence(format!("Key {key:?} not found in {container}"))) - } -} - -pub(crate) trait GetRound { - fn get_round(&self, round_id: u8) -> Result<&V, ProtocolValidationError>; -} - -impl GetRound for BTreeMap { - fn get_round(&self, round_id: u8) -> Result<&V, ProtocolValidationError> { - self.get(&RoundId::new(round_id)).ok_or_else(|| { - ProtocolValidationError::InvalidEvidence(format!("Entry for round {round_id} is not present")) - }) - } -} - -pub(crate) trait DeserializeAll { - fn deserialize_all Deserialize<'de>>( - &self, - format: &BoxedFormat, - ) -> Result, ProtocolValidationError>; -} - -impl DeserializeAll for BTreeMap { - fn deserialize_all Deserialize<'de>>( - &self, - format: &BoxedFormat, - ) -> Result, ProtocolValidationError> { - let deserialized = self - .iter() - .map(|(id, echo)| { - echo.deserialize::(format) - .map(|deserialized| (id.clone(), deserialized)) - }) - .collect::, _>>()?; - Ok(deserialized) - } -} - -pub(crate) fn verify_that(condition: bool) -> Result<(), ProtocolValidationError> { - if condition { - Ok(()) - } else { - Err(ProtocolValidationError::InvalidEvidence( - "the reported error cannot be reproduced".into(), - )) - } -} - -/// Analogous to `Iterator::sum()`, but requires a non-empty iterator -/// (so that it can be used for types with no `default()`, like `Ciphertext`) -pub(crate) fn sum_non_empty(mut iter: I, empty_error: E) -> Result -where - I: Iterator>, - T: core::ops::Add, -{ - let mut result = iter.next().ok_or(empty_error)??; - for item in iter { - result = result + item?; - } - Ok(result) -} - -/// Analogous to `Iterator::sum()`, but requires a non-empty iterator -/// (so that it can be used for types with no `default()`, like `Ciphertext`) -pub(crate) fn sum_non_empty_ref<'x, T, I, E>(mut iter: I, empty_error: E) -> Result -where - I: Iterator>, - T: 'x + Clone, - for<'a> T: core::ops::Add<&'a T, Output = T>, -{ - let mut result = iter.next().ok_or(empty_error)??.clone(); - for item in iter { - result = result + item?; - } - Ok(result) -} diff --git a/src/tools/protocol_shortcuts_dev.rs b/src/tools/protocol_shortcuts_dev.rs deleted file mode 100644 index da521bea..00000000 --- a/src/tools/protocol_shortcuts_dev.rs +++ /dev/null @@ -1,231 +0,0 @@ -use alloc::{collections::BTreeSet, format, vec::Vec}; -use core::{fmt::Debug, marker::PhantomData}; - -use manul::{ - combinators::misbehave::{Behavior, Misbehaving, MisbehavingEntryPoint}, - dev::run_sync, - dev::ExecutionResult, - protocol::{ - Artifact, BoxedFormat, BoxedRound, DirectMessage, EchoBroadcast, EntryPoint, NormalBroadcast, PartyId, - Protocol, ProtocolError, ProtocolMessagePart, - }, - session::{LocalError, SessionParameters}, - signature::Keypair, -}; -use rand_core::CryptoRngCore; - -/// Executes the sessions for the given entry points, -/// making one party (first in alphabetical order) the malicious one with the wrapper `M` and the given `behavior`. -#[allow(clippy::type_complexity)] -pub(crate) fn run_with_one_malicious_party( - rng: &mut impl CryptoRngCore, - entry_points: Vec<(SP::Signer, M::EntryPoint)>, - behavior: &B, -) -> Result>::Protocol, SP>, LocalError> -where - SP: SessionParameters, - B: Behavior + Clone, - M: Misbehaving, -{ - let ids = entry_points - .iter() - .map(|(signer, _ep)| signer.verifying_key()) - .collect::>(); - let misbehaving_id = ids - .first() - .ok_or_else(|| LocalError::new("Entry points list cannot be empty"))?; - let modified_entry_points = entry_points - .into_iter() - .map(|(signer, entry_point)| { - let id = signer.verifying_key(); - let maybe_behavior = if &id == misbehaving_id { - Some(behavior.clone()) - } else { - None - }; - let entry_point = MisbehavingEntryPoint::::new(entry_point, maybe_behavior); - (signer, entry_point) - }) - .collect(); - - run_sync::<_, SP>(rng, modified_entry_points) -} - -/// Executes [`run_with_one_malicious_party`] and checks that the malicous party -/// does not generate any provable error reports, while all the others do. -/// -/// Checks that these reports can be verified given `associated_data`, -/// and their description starts with `expected_description`, returning a `LocalError` otherwise. -#[allow(clippy::type_complexity)] -pub(crate) fn check_evidence_with_behavior( - rng: &mut impl CryptoRngCore, - entry_points: Vec<(SP::Signer, M::EntryPoint)>, - behavior: &B, - associated_data: &<<>::Protocol as Protocol>::ProtocolError as ProtocolError>::AssociatedData, - expected_description: &str, -) -> Result<(), LocalError> -where - SP: SessionParameters, - B: Behavior + Clone, - M: Misbehaving, -{ - let ids = entry_points - .iter() - .map(|(signer, _ep)| signer.verifying_key()) - .collect::>(); - let misbehaving_id = ids - .first() - .ok_or_else(|| LocalError::new("Entry points list cannot be empty"))?; - - let execution_result = run_with_one_malicious_party::(rng, entry_points, behavior)?; - let mut reports = execution_result.reports; - - let misbehaving_party_report = reports - .remove(misbehaving_id) - .ok_or_else(|| LocalError::new("Misbehaving node ID is not present in the reports"))?; - assert!(misbehaving_party_report.provable_errors.is_empty()); - - for (id, report) in reports { - if report.provable_errors.len() != 1 { - return Err(LocalError::new(format!( - "Node {id:?} reported more than one provable errors" - ))); - } - - let description = report - .provable_errors - .get(misbehaving_id) - .ok_or_else(|| LocalError::new("A lawful node did not generate a provable error report"))? - .description(); - if !description.starts_with(expected_description) { - return Err(LocalError::new(format!( - "Got {description}, expected {expected_description}" - ))); - } - - let verification_result = report.provable_errors[misbehaving_id].verify(associated_data); - if verification_result.is_err() { - return Err(LocalError::new(format!("Failed to verify: {verification_result:?}"))); - } - } - - Ok(()) -} - -/// Indicates the error for which part of the protocol message needs to be checked. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum CheckPart { - EchoBroadcast, - NormalBroadcast, - DirectMessage, -} - -#[derive(Debug, Clone, Copy)] -struct ModifyPart { - round: u8, - part: CheckPart, -} - -impl ModifyPart { - fn new(round: u8, part: CheckPart) -> Self { - Self { round, part } - } -} - -struct InvalidMessageOverride(PhantomData); - -impl Misbehaving for InvalidMessageOverride -where - EP: 'static + Debug + EntryPoint, -{ - type EntryPoint = EP; - - fn modify_echo_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - modify: &ModifyPart, - format: &BoxedFormat, - echo_broadcast: EchoBroadcast, - ) -> Result { - if round.id() != modify.round || modify.part != CheckPart::EchoBroadcast { - return Ok(echo_broadcast); - } - - // This triggers an error both in the case where the part is not supposed to be present, - // and in the case where it is (because the deserialization fails). - EchoBroadcast::new::<[u8; 0]>(format, []) - } - - fn modify_normal_broadcast( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - modify: &ModifyPart, - format: &BoxedFormat, - normal_broadcast: NormalBroadcast, - ) -> Result { - if round.id() != modify.round || modify.part != CheckPart::NormalBroadcast { - return Ok(normal_broadcast); - } - - // This triggers an error both in the case where the part is not supposed to be present, - // and in the case where it is (because the deserialization fails). - NormalBroadcast::new::<[u8; 0]>(format, []) - } - - fn modify_direct_message( - _rng: &mut dyn CryptoRngCore, - round: &BoxedRound>::Protocol>, - modify: &ModifyPart, - format: &BoxedFormat, - _destination: &Id, - direct_message: DirectMessage, - artifact: Option, - ) -> Result<(DirectMessage, Option), LocalError> { - if round.id() != modify.round || modify.part != CheckPart::DirectMessage { - return Ok((direct_message, artifact)); - } - - // This triggers an error both in the case where the part is not supposed to be present, - // and in the case where it is (because the deserialization fails). - let direct_message = DirectMessage::new::<[u8; 0]>(format, [])?; - Ok((direct_message, artifact)) - } -} - -/// Checks that generating and verifying evidence for an invalid message part works correctly. -/// -/// Pass `expecting_a_message = true` if in the round `round_num` the part `part` is expected to exist, -/// `false` otherwise. -pub(crate) fn check_invalid_message_evidence( - rng: &mut impl CryptoRngCore, - entry_points: Vec<(SP::Signer, EP)>, - round_num: u8, - part: CheckPart, - associated_data: &<>::ProtocolError as ProtocolError>::AssociatedData, - expecting_a_message: bool, -) -> Result<(), LocalError> -where - EP: 'static + Debug + EntryPoint, - SP: SessionParameters, -{ - let prefix = match part { - CheckPart::EchoBroadcast => "Echo broadcast", - CheckPart::NormalBroadcast => "Normal broadcast", - CheckPart::DirectMessage => "Direct message", - }; - let error = if expecting_a_message { - "Deserialization error" - } else { - "The payload was expected to be `None`, but contains a message" - }; - - let expected_description = format!("{prefix} error: {error}"); - - check_evidence_with_behavior::, _>( - rng, - entry_points, - &ModifyPart::new(round_num, part), - associated_data, - &expected_description, - ) -} diff --git a/src/tools/secret.rs b/src/tools/secret.rs index 68e89a7c..92ff64ca 100644 --- a/src/tools/secret.rs +++ b/src/tools/secret.rs @@ -5,12 +5,12 @@ use core::{ }; use crypto_bigint::{ + Integer, Monty, NonZero, WrappingAdd, WrappingMul, WrappingNeg, WrappingSub, modular::Retrieve, subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}, - Integer, Monty, NonZero, WrappingAdd, WrappingMul, WrappingNeg, WrappingSub, }; use secrecy::{ExposeSecret, ExposeSecretMut, SecretBox}; -use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; use serde_encoded_bytes::{Hex, SliceLike}; use zeroize::Zeroize; diff --git a/src/tools/sss.rs b/src/tools/sss.rs index 62dc0400..99d85ab5 100644 --- a/src/tools/sss.rs +++ b/src/tools/sss.rs @@ -57,7 +57,7 @@ impl

Polynomial

where P: SchemeParams, { - pub fn random(rng: &mut dyn CryptoRngCore, coeff0: Secret>, degree: usize) -> Self { + pub fn random(rng: &mut impl CryptoRngCore, coeff0: Secret>, degree: usize) -> Self { let mut coeffs = Vec::with_capacity(degree); coeffs.push(coeff0); for _ in 1..degree { diff --git a/src/uint/public_uint.rs b/src/uint/public_uint.rs index 0e7e3dd6..baffce2c 100644 --- a/src/uint/public_uint.rs +++ b/src/uint/public_uint.rs @@ -1,6 +1,6 @@ use alloc::boxed::Box; -use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; use serde_encoded_bytes::{Hex, SliceLike}; use super::BoxedEncoding; diff --git a/src/uint/secret_signed.rs b/src/uint/secret_signed.rs index 5334729e..7f38ed42 100644 --- a/src/uint/secret_signed.rs +++ b/src/uint/secret_signed.rs @@ -1,11 +1,11 @@ use core::ops::{Add, Mul, Neg, Not, Sub}; use crypto_bigint::{ + Bounded, CheckedAdd, CheckedMul, CheckedSub, Integer, NonZero, RandomMod, WrappingAdd, WrappingMul, WrappingNeg, + WrappingSub, rand_core::CryptoRngCore, subtle::{Choice, ConditionallySelectable, ConstantTimeLess, CtOption}, zeroize::Zeroize, - Bounded, CheckedAdd, CheckedMul, CheckedSub, Integer, NonZero, RandomMod, WrappingAdd, WrappingMul, WrappingNeg, - WrappingSub, }; use super::{Extendable, MulWide, PublicSigned, SecretUnsigned}; @@ -300,7 +300,7 @@ where /// sampling from $[-2^{exp-1}+1, 2^{exp-1}]$ (See Section 3, Groups & Fields). /// /// Note: variable time in `exp`. - pub fn random_in_exponent_range(rng: &mut dyn CryptoRngCore, exp: u32) -> Self { + pub fn random_in_exponent_range(rng: &mut impl CryptoRngCore, exp: u32) -> Self { assert!(exp > 0, "`exp` must be greater than zero"); assert!( exp < T::BITS, @@ -336,7 +336,7 @@ where /// sampling from $[-scale (2^{exp-1}+1), scale 2^{exp-1}]$ (See Section 3, Groups & Fields). /// /// Note: variable time in `exp` and bit size of `scale`. - pub fn random_in_exponent_range_scaled(rng: &mut dyn CryptoRngCore, exp: u32, scale: &T) -> SecretSigned + pub fn random_in_exponent_range_scaled(rng: &mut impl CryptoRngCore, exp: u32, scale: &T) -> SecretSigned where T: Extendable, W: Zeroize + Integer + Bounded + ConditionallySelectable + RandomMod, @@ -380,7 +380,7 @@ where /// /// Note: variable time in `exp` and bit size of `scale`. pub fn random_in_exponent_range_scaled_wide( - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, exp: u32, scale: &W, ) -> SecretSigned @@ -544,8 +544,8 @@ mod tests { use std::ops::Neg; use crypto_bigint::{ + Bounded, CheckedMul, CheckedSub, Integer, U128, U1024, U2048, subtle::{Choice, ConditionallySelectable}, - Bounded, CheckedMul, CheckedSub, Integer, U1024, U128, U2048, }; use rand::SeedableRng; use rand_chacha::{self, ChaCha8Rng}; diff --git a/src/uint/secret_unsigned.rs b/src/uint/secret_unsigned.rs index d0c3e203..9b945867 100644 --- a/src/uint/secret_unsigned.rs +++ b/src/uint/secret_unsigned.rs @@ -1,8 +1,8 @@ use core::ops::BitAnd; use crypto_bigint::{ - subtle::{Choice, CtOption}, Bounded, Integer, Monty, NonZero, + subtle::{Choice, CtOption}, }; use zeroize::Zeroize; diff --git a/src/uint/traits.rs b/src/uint/traits.rs index 557944a3..c9bc7063 100644 --- a/src/uint/traits.rs +++ b/src/uint/traits.rs @@ -1,8 +1,8 @@ use alloc::{boxed::Box, format, string::String, vec}; use crypto_bigint::{ - subtle::{ConditionallySelectable, CtOption}, Bounded, Encoding, Gcd, Integer, Invert, Limb, Monty, PowBoundedExp, Uint, + subtle::{ConditionallySelectable, CtOption}, }; use digest::XofReader; use zeroize::Zeroize; diff --git a/src/zk/aff_g.rs b/src/zk/aff_g.rs index 5c1c2462..38d021e5 100644 --- a/src/zk/aff_g.rs +++ b/src/zk/aff_g.rs @@ -8,7 +8,7 @@ use crate::{ Ciphertext, CiphertextWire, MaskedRandomizer, PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, Randomizer, }, - params::{public_signed_from_scalar, scalar_from_signed, secret_scalar_from_signed, SchemeParams}, + params::{SchemeParams, public_signed_from_scalar, scalar_from_signed, secret_scalar_from_signed}, tools::hashing::{Chain, Hashable, Hasher}, uint::{PublicSigned, SecretSigned}, }; @@ -68,7 +68,7 @@ pub(crate) struct AffGProof { impl AffGProof

{ pub fn new( - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, secret: AffGSecretInputs<'_, P>, public: AffGPublicInputs<'_, P>, setup: &RPParams, @@ -256,12 +256,13 @@ impl AffGProof

{ mod tests { use manul::{dev::BinaryFormat, session::WireFormat}; use rand_core::OsRng; + use serde::Deserialize; use super::{AffGProof, AffGPublicInputs, AffGSecretInputs}; use crate::{ dev::TestParams, paillier::{Ciphertext, RPParams, Randomizer, SecretKeyPaillierWire}, - params::{secret_scalar_from_signed, SchemeParams}, + params::{SchemeParams, secret_scalar_from_signed}, uint::SecretSigned, }; @@ -311,7 +312,7 @@ mod tests { // Serialization roundtrip let serialized = BinaryFormat::serialize(proof).unwrap(); - let proof = BinaryFormat::deserialize::>(&serialized).unwrap(); + let proof = AffGProof::::deserialize(BinaryFormat::deserializer(&serialized)).unwrap(); assert!(proof.verify(public, &rp_params, &aux)); } diff --git a/src/zk/aff_g_star.rs b/src/zk/aff_g_star.rs index 697c6161..e414ead6 100644 --- a/src/zk/aff_g_star.rs +++ b/src/zk/aff_g_star.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use crate::{ curve::Point, paillier::{Ciphertext, CiphertextWire, MaskedRandomizer, PaillierParams, PublicKeyPaillier, Randomizer}, - params::{scalar_from_signed, secret_scalar_from_signed, SchemeParams}, + params::{SchemeParams, scalar_from_signed, secret_scalar_from_signed}, tools::{ bitvec::BitVec, hashing::{Chain, Hashable, Hasher}, @@ -82,7 +82,7 @@ pub(crate) struct AffGStarProof { impl AffGStarProof

{ pub fn new( - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, secret: AffGStarSecretInputs<'_, P>, public: AffGStarPublicInputs<'_, P>, aux: &impl Hashable, @@ -262,12 +262,13 @@ impl AffGStarProof

{ mod tests { use manul::{dev::BinaryFormat, session::WireFormat}; use rand_core::OsRng; + use serde::Deserialize; use super::{AffGStarProof, AffGStarPublicInputs, AffGStarSecretInputs}; use crate::{ dev::TestParams, paillier::{Ciphertext, Randomizer, SecretKeyPaillierWire}, - params::{secret_scalar_from_signed, SchemeParams}, + params::{SchemeParams, secret_scalar_from_signed}, uint::SecretSigned, }; @@ -315,7 +316,7 @@ mod tests { // Serialization roundtrip let serialized = BinaryFormat::serialize(proof).unwrap(); - let proof = BinaryFormat::deserialize::>(&serialized).unwrap(); + let proof = AffGStarProof::::deserialize(BinaryFormat::deserializer(&serialized)).unwrap(); assert!(proof.verify(public, &aux)); } diff --git a/src/zk/dec.rs b/src/zk/dec.rs index cb92ac8e..e17ace3f 100644 --- a/src/zk/dec.rs +++ b/src/zk/dec.rs @@ -9,8 +9,8 @@ use crate::{ curve::Point, paillier::{Ciphertext, CiphertextWire, MaskedRandomizer, PaillierParams, PublicKeyPaillier, RPParams, Randomizer}, params::{ - scalar_from_signed, scalar_from_wide_signed, secret_scalar_from_signed, secret_scalar_from_wide_signed, - SchemeParams, + SchemeParams, scalar_from_signed, scalar_from_wide_signed, secret_scalar_from_signed, + secret_scalar_from_wide_signed, }, tools::{ bitvec::BitVec, @@ -90,7 +90,7 @@ pub(crate) struct DecProofElement { impl DecProof

{ pub fn new( - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, secret: DecSecretInputs<'_, P>, public: DecPublicInputs<'_, P>, setup: &RPParams, @@ -271,13 +271,14 @@ impl DecProof

{ mod tests { use manul::{dev::BinaryFormat, session::WireFormat}; use rand_core::OsRng; + use serde::Deserialize; use super::{DecProof, DecPublicInputs, DecSecretInputs}; use crate::{ curve::Scalar, dev::TestParams, paillier::{Ciphertext, PaillierParams, RPParams, Randomizer, SecretKeyPaillierWire}, - params::{secret_scalar_from_signed, SchemeParams}, + params::{SchemeParams, secret_scalar_from_signed}, uint::SecretSigned, }; @@ -335,7 +336,7 @@ mod tests { // Serialization roundtrip let serialized = BinaryFormat::serialize(proof).unwrap(); - let proof = BinaryFormat::deserialize::>(&serialized).unwrap(); + let proof = DecProof::::deserialize(BinaryFormat::deserializer(&serialized)).unwrap(); assert!(proof.verify(public, &setup, &aux)); } diff --git a/src/zk/elog.rs b/src/zk/elog.rs index 15d351d8..e357e75a 100644 --- a/src/zk/elog.rs +++ b/src/zk/elog.rs @@ -6,8 +6,8 @@ use crate::{ curve::{Point, Scalar}, params::SchemeParams, tools::{ - hashing::{Chain, Hashable, Hasher}, Secret, + hashing::{Chain, Hashable, Hasher}, }, }; @@ -46,7 +46,7 @@ pub(crate) struct ElogProof { impl ElogProof

{ pub fn new( - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, secret: ElogSecretInputs<'_, P>, public: ElogPublicInputs<'_, P>, aux: &impl Hashable, @@ -133,6 +133,7 @@ impl ElogProof

{ mod tests { use manul::{dev::BinaryFormat, session::WireFormat}; use rand_core::OsRng; + use serde::Deserialize; use super::{ElogProof, ElogPublicInputs, ElogSecretInputs}; use crate::{curve::Scalar, dev::TestParams, tools::Secret}; @@ -165,7 +166,7 @@ mod tests { // Serialization roundtrip let serialized = BinaryFormat::serialize(proof).unwrap(); - let proof = BinaryFormat::deserialize::>(&serialized).unwrap(); + let proof = ElogProof::::deserialize(BinaryFormat::deserializer(&serialized)).unwrap(); assert!(proof.verify(public, &aux)); } diff --git a/src/zk/enc_elg.rs b/src/zk/enc_elg.rs index 94fc9852..7bbb0a26 100644 --- a/src/zk/enc_elg.rs +++ b/src/zk/enc_elg.rs @@ -8,10 +8,10 @@ use crate::{ Ciphertext, CiphertextWire, MaskedRandomizer, PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, Randomizer, }, - params::{public_signed_from_scalar, scalar_from_signed, secret_scalar_from_signed, SchemeParams}, + params::{SchemeParams, public_signed_from_scalar, scalar_from_signed, secret_scalar_from_signed}, tools::{ - hashing::{Chain, Hashable, Hasher}, Secret, + hashing::{Chain, Hashable, Hasher}, }, uint::{PublicSigned, SecretSigned}, }; @@ -59,7 +59,7 @@ pub(crate) struct EncElgProof { impl EncElgProof

{ pub fn new( - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, secret: EncElgSecretInputs<'_, P>, public: EncElgPublicInputs<'_, P>, setup: &RPParams, @@ -194,13 +194,14 @@ impl EncElgProof

{ mod tests { use manul::{dev::BinaryFormat, session::WireFormat}; use rand_core::OsRng; + use serde::Deserialize; use super::{EncElgProof, EncElgPublicInputs, EncElgSecretInputs}; use crate::{ curve::Scalar, dev::TestParams, paillier::{Ciphertext, RPParams, Randomizer, SecretKeyPaillierWire}, - params::{secret_scalar_from_signed, SchemeParams}, + params::{SchemeParams, secret_scalar_from_signed}, tools::Secret, uint::SecretSigned, }; @@ -244,7 +245,7 @@ mod tests { // Serialization roundtrip let serialized = BinaryFormat::serialize(proof).unwrap(); - let proof = BinaryFormat::deserialize::>(&serialized).unwrap(); + let proof = EncElgProof::::deserialize(BinaryFormat::deserializer(&serialized)).unwrap(); assert!(proof.verify(public, &setup, &aux)); } diff --git a/src/zk/fac.rs b/src/zk/fac.rs index f755b110..6d17fcf1 100644 --- a/src/zk/fac.rs +++ b/src/zk/fac.rs @@ -40,7 +40,7 @@ pub(crate) struct FacProof { impl FacProof

{ pub fn new( - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, sk0: &SecretKeyPaillier, setup: &RPParams, aux: &impl Hashable, @@ -211,6 +211,7 @@ impl FacProof

{ mod tests { use manul::{dev::BinaryFormat, session::WireFormat}; use rand_core::OsRng; + use serde::Deserialize; use super::FacProof; use crate::{ @@ -235,7 +236,7 @@ mod tests { // Serialization roundtrip let serialized = BinaryFormat::serialize(proof).unwrap(); - let proof = BinaryFormat::deserialize::>(&serialized).unwrap(); + let proof = FacProof::::deserialize(BinaryFormat::deserializer(&serialized)).unwrap(); assert!(proof.verify(pk, &setup, &aux)); } diff --git a/src/zk/mod_.rs b/src/zk/mod_.rs index 3b7d597a..01e93be8 100644 --- a/src/zk/mod_.rs +++ b/src/zk/mod_.rs @@ -5,7 +5,7 @@ use alloc::vec::Vec; -use crypto_bigint::{modular::Retrieve, Gcd, Integer, Square}; +use crypto_bigint::{Gcd, Integer, Square, modular::Retrieve}; use crypto_primes::RandomPrimeWithRng; use digest::XofReader; use rand::SeedableRng; @@ -26,7 +26,7 @@ const HASH_TAG: &[u8] = b"P_mod"; struct ModCommitment(PublicUint<::Uint>); impl ModCommitment

{ - fn random(rng: &mut dyn CryptoRngCore, sk: &SecretKeyPaillier) -> Self { + fn random(rng: &mut impl CryptoRngCore, sk: &SecretKeyPaillier) -> Self { Self(sk.random_nonsquare_residue(rng).into()) } } @@ -74,7 +74,7 @@ pub(crate) struct ModProof { } impl ModProof

{ - pub fn new(rng: &mut dyn CryptoRngCore, sk: &SecretKeyPaillier, aux: &impl Hashable) -> Self { + pub fn new(rng: &mut impl CryptoRngCore, sk: &SecretKeyPaillier, aux: &impl Hashable) -> Self { let pk = sk.public_key(); let commitment = ModCommitment::

::random(rng, sk); let challenge = ModChallenge::

::new(pk, &commitment, aux); @@ -200,6 +200,7 @@ impl ModProof

{ mod tests { use manul::{dev::BinaryFormat, session::WireFormat}; use rand_core::OsRng; + use serde::Deserialize; use super::ModProof; use crate::{dev::TestParams, paillier::SecretKeyPaillierWire, params::SchemeParams}; @@ -218,7 +219,7 @@ mod tests { // Serialization roundtrip let serialized = BinaryFormat::serialize(proof).unwrap(); - let proof = BinaryFormat::deserialize::>(&serialized).unwrap(); + let proof = ModProof::::deserialize(BinaryFormat::deserializer(&serialized)).unwrap(); assert!(proof.verify(pk, &aux)); } diff --git a/src/zk/prm.rs b/src/zk/prm.rs index 31e907bb..618750e4 100644 --- a/src/zk/prm.rs +++ b/src/zk/prm.rs @@ -5,7 +5,7 @@ use alloc::vec::Vec; -use crypto_bigint::{modular::Retrieve, BitOps, Integer, PowBoundedExp}; +use crypto_bigint::{BitOps, Integer, PowBoundedExp, modular::Retrieve}; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; @@ -26,7 +26,7 @@ const HASH_TAG: &[u8] = b"P_prm"; struct PrmSecret(Vec::Uint>>); impl PrmSecret

{ - fn random(rng: &mut dyn CryptoRngCore, secret: &RPSecret) -> Self { + fn random(rng: &mut impl CryptoRngCore, secret: &RPSecret) -> Self { let secret = (0..P::SECURITY_BITS) .map(|_| secret.random_residue_mod_totient(rng)) .collect(); @@ -71,7 +71,7 @@ impl PrmProof

{ /// Create a proof that we know the `secret` /// (i.e. lambda, the power that was used to create RP parameters). pub fn new( - rng: &mut dyn CryptoRngCore, + rng: &mut impl CryptoRngCore, secret: &RPSecret, setup: &RPParams, aux: &impl Hashable, @@ -132,6 +132,7 @@ impl PrmProof

{ mod tests { use manul::{dev::BinaryFormat, session::WireFormat}; use rand_core::OsRng; + use serde::Deserialize; use super::PrmProof; use crate::{ @@ -152,7 +153,7 @@ mod tests { // Serialization roundtrip let serialized = BinaryFormat::serialize(proof).unwrap(); - let proof = BinaryFormat::deserialize::>(&serialized).unwrap(); + let proof = PrmProof::::deserialize(BinaryFormat::deserializer(&serialized)).unwrap(); assert!(proof.verify(&setup, &aux)); } diff --git a/src/zk/sch.rs b/src/zk/sch.rs index 1353771f..d1bb260f 100644 --- a/src/zk/sch.rs +++ b/src/zk/sch.rs @@ -9,8 +9,8 @@ use crate::{ curve::{Point, Scalar}, params::SchemeParams, tools::{ - hashing::{Chain, Hashable, Hasher}, Secret, + hashing::{Chain, Hashable, Hasher}, }, }; @@ -24,7 +24,7 @@ pub(crate) struct SchSecret( ); impl SchSecret

{ - pub fn random(rng: &mut dyn CryptoRngCore) -> Self { + pub fn random(rng: &mut impl CryptoRngCore) -> Self { Self(Secret::init_with(|| Scalar::random(rng))) } } @@ -86,6 +86,7 @@ impl SchProof

{ mod tests { use manul::{dev::BinaryFormat, session::WireFormat}; use rand_core::OsRng; + use serde::Deserialize; use super::{SchCommitment, SchProof, SchSecret}; use crate::{curve::Scalar, dev::TestParams, tools::Secret}; @@ -104,7 +105,7 @@ mod tests { // Serialization roundtrip let serialized = BinaryFormat::serialize(proof).unwrap(); - let proof = BinaryFormat::deserialize::>(&serialized).unwrap(); + let proof = SchProof::::deserialize(BinaryFormat::deserializer(&serialized)).unwrap(); assert!(proof.verify(&commitment, &public, &aux)); }