From 6835c9a26cabbcfc96bc701b7d6511ff27217bc2 Mon Sep 17 00:00:00 2001 From: maurges Date: Thu, 23 Jan 2025 14:30:53 +0100 Subject: [PATCH 1/9] Add dlog_eq zk proof Signed-off-by: maurges --- Cargo.lock | 9 +- Cargo.toml | 2 + generic-ec-zkp/Cargo.toml | 4 +- generic-ec-zkp/src/dlog_eq.rs | 277 ++++++++++++++++++++++++++++++++++ generic-ec-zkp/src/lib.rs | 1 + tests/Cargo.toml | 3 + tests/tests/dlog_eq.rs | 118 +++++++++++++++ 7 files changed, 411 insertions(+), 3 deletions(-) create mode 100644 generic-ec-zkp/src/dlog_eq.rs create mode 100644 tests/tests/dlog_eq.rs diff --git a/Cargo.lock b/Cargo.lock index 7ed0410..10e9d25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -729,7 +729,7 @@ dependencies = [ [[package]] name = "generic-ec-core" -version = "0.2.2" +version = "0.2.3" dependencies = [ "generic-array", "rand_core", @@ -740,7 +740,7 @@ dependencies = [ [[package]] name = "generic-ec-curves" -version = "0.2.3" +version = "0.2.4" dependencies = [ "criterion", "curve25519-dalek", @@ -764,7 +764,9 @@ version = "0.1.0" dependencies = [ "anyhow", "criterion", + "digest", "generic-ec", + "generic-ec-zkp", "generic-tests", "hex", "plotters", @@ -775,6 +777,7 @@ dependencies = [ "serde_json", "serde_test", "serde_with", + "sha2", "tabled", ] @@ -783,12 +786,14 @@ name = "generic-ec-zkp" version = "0.4.3" dependencies = [ "criterion", + "digest", "generic-array", "generic-ec", "generic-tests", "rand", "rand_core", "rand_dev", + "rand_hash", "serde", "sha2", "subtle", diff --git a/Cargo.toml b/Cargo.toml index d358916..be11819 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ hex = { version = "0.4", default-features = false } rand = "0.8" rand_core = { version = "0.6", default-features = false } +rand_hash = { version = "0.1", default-features = false } serde = { version = "1", default-features = false } serde_json = "1" @@ -28,6 +29,7 @@ sha2 = { version = "0.10", default-features = false } subtle = { version = "2.4", default-features = false } +digest = "0.10" udigest = { version = "0.2.1", default-features = false } zeroize = { version = "1", default-features = false } diff --git a/generic-ec-zkp/Cargo.toml b/generic-ec-zkp/Cargo.toml index 37c67fd..78b35c6 100644 --- a/generic-ec-zkp/Cargo.toml +++ b/generic-ec-zkp/Cargo.toml @@ -13,10 +13,12 @@ keywords = ["elliptic-curves", "zk-proof"] [dependencies] generic-ec = { version = "0.4.0", path = "../generic-ec", default-features = false } +digest = { workspace = true, optional = true } udigest = { workspace = true, features = ["derive"], optional = true } subtle.workspace = true rand_core.workspace = true +rand_hash.workspace = true serde = { workspace = true, features = ["derive"], optional = true } @@ -39,7 +41,7 @@ default = ["std"] std = ["alloc"] alloc = ["udigest?/alloc", "serde?/alloc"] serde = ["dep:serde", "generic-ec/serde", "generic-array/serde"] -udigest = ["dep:udigest", "generic-ec/udigest"] +udigest = ["dep:udigest", "dep:digest", "generic-ec/udigest"] [package.metadata.docs.rs] all-features = true diff --git a/generic-ec-zkp/src/dlog_eq.rs b/generic-ec-zkp/src/dlog_eq.rs new file mode 100644 index 0000000..dd998c4 --- /dev/null +++ b/generic-ec-zkp/src/dlog_eq.rs @@ -0,0 +1,277 @@ +//! Knowledge of equality of two discrete logarithms $\Pi^\text{dlog_eq}$ +//! +//! This is a protocol that lets the prover $\P$ convince the +//! verifier $\V$ that it knows the discrete logarithms of two values, and that +//! those logarithms are equal. $\P$ knows the secret data $x$ - the discrete +//! logarithm. $\P$ and $\V$ share common data: +//! - $H$ - a point on an elliptic curve +//! - $X = x \cdot G$ +//! - $Z = x \cdot H$ +//! +//! Thus $log_G X = log_H Z = x$. Then $\P$ proves this fact with this protocol. +//! A common instantiation is when $x$ is a private key. Then $X$ is a public +//! key, a fact known to both parties, and the proof is that $H \cdot x = Z$ +//! without disclosing $x$. +//! +//! ## Non-interactive example +//! +//! ```rust +//! # use generic_ec::{Curve, Scalar, SecretScalar, Point}; +//! # use generic_ec_zkp::dlog_eq::non_interactive; +//! # use rand::rngs::OsRng; +//! # use generic_ec::curves::Secp256k1 as E; +//! # fn send(_: T) {} +//! +//! // `x` is a secret discrete logarithm only known to prover +//! let x = SecretScalar::::random(&mut OsRng); +//! +//! // `X` is a public point corresponding to `x` +//! let X = Point::generator() * &x; +//! // `H` is some point known to both parties +//! let H = Point::generator() * Scalar::random(&mut OsRng); +//! // `Z` is the object of the proof +//! let Z = H * &x; +//! +//! let data = non_interactive::Data { +//! pub_share: X, +//! base: H, +//! exp: Z, +//! }; +//! +//! // Prover chooses a private nonce +//! let r = Scalar::random(&mut OsRng); +//! // Prover proves in zero knowledge the equality of `H * x = Z` +//! let proof = non_interactive::prove::(&"shared_state", &x, data, r); +//! // The proof is sent to the Verifier +//! send(proof); +//! +//! // Verifier checks that the proof is correct +//! non_interactive::verify::(&"shared_state", data, proof) +//! .expect("Verification failed!"); +//! ``` +//! +//! ## Interactive example +//! +//! ```rust +//! # use generic_ec::{Curve, Scalar, SecretScalar, Point}; +//! # use generic_ec_zkp::dlog_eq::interactive; +//! # use rand::rngs::OsRng; +//! # use generic_ec::curves::Secp256k1 as E; +//! # fn send(_: T) {} +//! +//! // `x` is a secret discrete logarithm only known to prover +//! let x = SecretScalar::::random(&mut OsRng); +//! +//! // `X` is a public point corresponding to `x` +//! let X = Point::generator() * &x; +//! // `H` is some point known to both parties +//! let H = Point::generator() * Scalar::random(&mut OsRng); +//! // `Z` is the object of the proof +//! let Z = H * &x; +//! +//! let data = interactive::Data { +//! pub_share: X, +//! base: H, +//! exp: Z, +//! }; +//! +//! // Prover chooses a nonce for itself +//! let r = Scalar::random(&mut OsRng); +//! // Prover commits to the data +//! let commitment = interactive::commit_data(data, r); +//! // Prover sends this commitment to the verifier +//! send(commitment); +//! +//! // Verifier sends a challenge to the prover +//! let challenge = Scalar::random(&mut OsRng); +//! send(challenge); +//! +//! // Prover receives the challenge and computes the proof of equality `H * x = Z` +//! let proof = interactive::prove(r, challenge, &x); +//! // This proof is sent to the verifier +//! send(proof); +//! +//! // Verifier checks that the proof is correct +//! interactive::verify(data, commitment, challenge, proof) +//! .expect("Verification failed!"); +//! ``` + +use generic_ec::{Curve, Point}; + +/// Object of the proof +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "udigest", derive(udigest::Digestable), udigest(bound = ""))] +pub struct Data { + /// `X`, the value inside `log_G` + pub pub_share: Point, + /// `H`, the base for the second logarithm + pub base: Point, + /// `Z`, the value inside the second logarithm `log_H` + pub exp: Point, +} + +/// Functions for interactive protocol for the proof. See usage example in +/// [`generic_ec_zkp::dlog_eq`] +/// +/// Based on "Zero-Knowledge Proofs Notes", 2024 by Jorge L. Villar +pub mod interactive { + use generic_ec::{Curve, Point, Scalar, SecretScalar}; + + /// Commitment to `H`, sent in the first round + pub type Commitment = (Point, Point); + /// Challenge, sent to the Prover in the first round + pub type Challenge = Scalar; + /// Proof data, sent by prover to the verifier + pub type Proof = Scalar; + pub use super::Data; + pub use super::InvalidProof; + + /// First round of the protocol: commit to `H` by itself. Produces + /// [`Commitment`] to be sent + /// + /// - `r` - random nonce. Can be produced by `Scalar::random(&mut rng)` + pub fn commit(base: Point, r: Scalar) -> Commitment { + (Point::generator() * r, base * r) + } + /// First round of the protocol: commit to well-constructed [`Data`] by + /// itself. Produces [`Commitment`] to be sent + /// + /// - `r` - random nonce. Can be produced by `Scalar::random(&mut rng)` + pub fn commit_data(data: Data, r: Scalar) -> Commitment { + commit(data.base, r) + } + + /// Second round of the protocol: produce the proof for [`Data`] commited to + /// with the given nonce `r` + /// + /// - `r` - nonce, the same as used in [`commit`] or [`commit_data`] + /// - `challenge` - challenge sent by the Verifier + /// - `x` - secret value of discrete logarithm + pub fn prove(r: Scalar, challenge: Scalar, x: &SecretScalar) -> Proof { + r + challenge * x + } + + /// Verify the validity of the ZK proof + /// + /// - `data` - data for which the proof was computed + /// - `comm` - commitment sent by the Prover in the first round + /// - `challenge` - the same challenge as was sent by this verifier in the + /// first round + /// - `proof` - produced by the Prover in the second round for this data, + /// commitment and challenge + pub fn verify( + data: Data, + comm: Commitment, + challenge: Scalar, + proof: Proof, + ) -> Result<(), InvalidProof> { + let (a1, a2) = comm; + // equation 1 + let lhs = Point::generator() * proof; + let rhs = a1 + data.pub_share * challenge; + if lhs != rhs { + return Err(InvalidProof); + } + // equation 2 + let lhs = data.base * proof; + let rhs = a2 + data.exp * challenge; + if lhs != rhs { + return Err(InvalidProof); + } + + Ok(()) + } +} + +/// Functions for non interactive variant of the proof. See usage example in +/// [`generic_ec_zkp::dlog_eq`] +/// +/// Compared to naive protocol produced by Fiat-Shamir heuristic, this is +/// optimized to send less data in proof. It's based on `PrEq` functionality in +/// this paper: https://eprint.iacr.org/2020/096 +#[cfg(feature = "udigest")] +pub mod non_interactive { + use generic_ec::{Curve, Point, Scalar, SecretScalar}; + + const TAG: &str = "bls-style-digest.zkp"; + + pub use super::Data; + pub use super::InvalidProof; + + /// Proof data, sent by prover to the verifier + #[derive(Debug, Clone, Copy)] + #[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(bound = "") + )] + pub struct Proof { + /// Deterministic challenge + pub ch: generic_ec::Scalar, + /// Proof itself + pub res: generic_ec::Scalar, + } + + /// Create a proof deterministically for given data + /// + /// - `shared_state` - shared data not known to this proof, used to protect + /// from replay attacks + /// - `share` - `x`, secret value of discrete logarithm + /// - `data` - data to compute the proof for + /// - `r` - random nonce. Can be produced by `Scalar::random(&mut rng)` + pub fn prove( + shared_state: &impl udigest::Digestable, + share: &SecretScalar, + data: Data, + r: Scalar, + ) -> Proof { + let com1 = Point::generator() * r; + let com2 = data.base * r; + + let seed = udigest::inline_struct!(TAG { + shared_state, + data, + com1, + com2, + }); + let mut rng = rand_hash::HashRng::::from_seed(seed); + let ch = Scalar::random(&mut rng); + + let res = r + share * ch; + Proof { ch, res } + } + + /// Verify the validity of the ZK proof + /// + /// - `shared_state` - shared data not known to this proof, used to protect + /// from replay attacks + /// - `data` - data for which the proof was computed + /// - `proof` - produced by the Prover in the second round for this data and + /// shared_state + pub fn verify( + shared_state: &impl udigest::Digestable, + data: Data, + proof: Proof, + ) -> Result<(), InvalidProof> { + let com1 = Point::generator() * proof.res - data.pub_share * proof.ch; + let com2 = data.base * proof.res - data.exp * proof.ch; + + let seed = udigest::inline_struct!(TAG { + shared_state, + data, + com1, + com2, + }); + let mut rng = rand_hash::HashRng::::from_seed(seed); + let ch = Scalar::random(&mut rng); + + if ch != proof.ch { + Err(InvalidProof) + } else { + Ok(()) + } + } +} + +#[derive(Debug)] +pub struct InvalidProof; diff --git a/generic-ec-zkp/src/lib.rs b/generic-ec-zkp/src/lib.rs index 9f26498..5f9cf31 100644 --- a/generic-ec-zkp/src/lib.rs +++ b/generic-ec-zkp/src/lib.rs @@ -13,5 +13,6 @@ extern crate alloc; // We don't want this dependency to trigger unused dep lint use generic_array as _; +pub mod dlog_eq; pub mod polynomial; pub mod schnorr_pok; diff --git a/tests/Cargo.toml b/tests/Cargo.toml index f0badeb..8a9b549 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -8,12 +8,14 @@ publish = false [dependencies] generic-ec = { path = "../generic-ec", default-features = false, features = ["all-curves", "serde"] } +generic-ec-zkp = { path = "../generic-ec-zkp", default-features = false, features = ["serde", "udigest"] } plotters = "0.3" anyhow = "1" regex = "1.10" tabled = { version = "0.15", default-features = false, features = ["std"] } +digest.workspace = true serde = { workspace = true, features = ["derive"] } serde_with.workspace = true serde_test.workspace = true @@ -24,6 +26,7 @@ hex.workspace = true generic-tests.workspace = true rand_dev.workspace = true rand.workspace = true +sha2.workspace = true criterion = { workspace = true, features = ["html_reports"] } diff --git a/tests/tests/dlog_eq.rs b/tests/tests/dlog_eq.rs new file mode 100644 index 0000000..935003d --- /dev/null +++ b/tests/tests/dlog_eq.rs @@ -0,0 +1,118 @@ +#[generic_tests::define] +mod interactive { + use generic_ec_zkp::dlog_eq::interactive as dlog_eq; + + #[test] + fn passing_test() { + let mut rng = rand_dev::DevRng::new(); + + let secret_share = generic_ec::SecretScalar::::random(&mut rng); + let public_share = generic_ec::Point::generator() * &secret_share; + + let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); + let exp = base * &secret_share; + + let r = generic_ec::Scalar::random(&mut rng); + + let comm = dlog_eq::commit(base, r); + let challenge = generic_ec::Scalar::random(&mut rng); + let proof = dlog_eq::prove(r, challenge, &secret_share); + let data = dlog_eq::Data { + pub_share: public_share, + base, + exp, + }; + dlog_eq::verify(data, comm, challenge, proof).unwrap() + } + #[test] + fn failing_test() { + let mut rng = rand_dev::DevRng::new(); + + let secret_share = generic_ec::SecretScalar::::random(&mut rng); + let public_share = generic_ec::Point::generator() * &secret_share; + + let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); + // Replace the exponentiation with something wrong + let exp = base * &secret_share + generic_ec::Point::generator(); + + let r = generic_ec::Scalar::random(&mut rng); + + let comm = dlog_eq::commit(base, r); + let challenge = generic_ec::Scalar::random(&mut rng); + let proof = dlog_eq::prove(r, challenge, &secret_share); + let data = dlog_eq::Data { + pub_share: public_share, + base, + exp, + }; + assert!( + dlog_eq::verify(data, comm, challenge, proof).is_err(), + "proof should fail" + ) + } + + #[instantiate_tests()] + mod secp256k1 {} + #[instantiate_tests()] + mod secp256r1 {} + #[instantiate_tests()] + mod stark {} + #[instantiate_tests()] + mod ed25519 {} +} + +#[generic_tests::define] +mod non_interactive { + use generic_ec_zkp::dlog_eq::non_interactive as dlog_eq; + + #[test] + fn passing_test() { + let mut rng = rand_dev::DevRng::new(); + let shared_state = "shared state"; + + let secret_share = generic_ec::SecretScalar::random(&mut rng); + let public_share = generic_ec::Point::generator() * &secret_share; + + let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); + let exp = base * &secret_share; + + let r = generic_ec::Scalar::random(&mut rng); + let data = dlog_eq::Data { + pub_share: public_share, + base, + exp, + }; + let proof = dlog_eq::prove::(&shared_state, &secret_share, data, r); + dlog_eq::verify::(&shared_state, data, proof).unwrap(); + } + + #[test] + fn failing_test() { + let mut rng = rand_dev::DevRng::new(); + let shared_state = "shared state"; + + let secret_share = generic_ec::SecretScalar::random(&mut rng); + let public_share = generic_ec::Point::generator() * &secret_share; + + let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); + let exp = base * &secret_share + generic_ec::Point::generator(); + + let r = generic_ec::Scalar::random(&mut rng); + let data = dlog_eq::Data { + pub_share: public_share, + base, + exp, + }; + let proof = dlog_eq::prove::(&shared_state, &secret_share, data, r); + assert!(dlog_eq::verify::(&shared_state, data, proof).is_err()); + } + + #[instantiate_tests()] + mod secp256k1_sha256 {} + #[instantiate_tests()] + mod secp256r1_sha256 {} + #[instantiate_tests()] + mod stark_sha256 {} + #[instantiate_tests()] + mod ed25519_sha256 {} +} From 16d7b1da6fa6a9321691e39a295f194f5f78c3a9 Mon Sep 17 00:00:00 2001 From: maurges Date: Thu, 23 Jan 2025 14:32:29 +0100 Subject: [PATCH 2/9] Slightly optimize doctests Signed-off-by: maurges --- generic-ec-zkp/src/schnorr_pok.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/generic-ec-zkp/src/schnorr_pok.rs b/generic-ec-zkp/src/schnorr_pok.rs index e2f1a54..df2f668 100644 --- a/generic-ec-zkp/src/schnorr_pok.rs +++ b/generic-ec-zkp/src/schnorr_pok.rs @@ -6,7 +6,7 @@ //! ## Example //! //! 0. $\P$ knows a secret $x$ and wants to prove its knowledge. -//! ```rust +//! ```rust,no_run //! # use generic_ec::{Curve, SecretScalar, Point}; //! # use rand::rngs::OsRng; //! # fn doc_fn() { @@ -15,7 +15,7 @@ //! # } //! ``` //! 1. $\P$ generates and commits an ephemeral secret. Committed secret is sent to $\V$. -//! ```rust +//! ```rust,no_run //! # use generic_ec::Curve; //! # use generic_ec_zkp::schnorr_pok::*; //! # use rand::rngs::OsRng; @@ -26,7 +26,7 @@ //! # fn send(_: T) {} //! ``` //! 2. $\V$ receives commitment, and responds with challenge. -//! ```rust +//! ```rust,no_run //! # use generic_ec::Curve; //! # use generic_ec_zkp::schnorr_pok::*; //! # use rand::rngs::OsRng; @@ -39,7 +39,7 @@ //! # fn receive() -> T { unimplemented!() } //! ``` //! 3. $\P$ receives a challenge and responds with proof. -//! ```rust +//! ```rust,no_run //! # use generic_ec::{Curve, SecretScalar}; //! # use generic_ec_zkp::schnorr_pok::*; //! # use rand::rngs::OsRng; @@ -54,7 +54,7 @@ //! # fn recall() -> T { unimplemented!() } //! ``` //! 4. $\V$ receives a proof and verifies it. -//! ```rust +//! ```rust,no_run //! # use generic_ec::{Curve, Point}; //! # use generic_ec_zkp::schnorr_pok::*; //! # use rand::rngs::OsRng; From 46b0fce912b57ecb6cdd50bfe07649b40fcae8b9 Mon Sep 17 00:00:00 2001 From: maurges Date: Thu, 23 Jan 2025 14:43:55 +0100 Subject: [PATCH 3/9] Fix docs Signed-off-by: maurges --- generic-ec-zkp/src/dlog_eq.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/generic-ec-zkp/src/dlog_eq.rs b/generic-ec-zkp/src/dlog_eq.rs index dd998c4..72df61e 100644 --- a/generic-ec-zkp/src/dlog_eq.rs +++ b/generic-ec-zkp/src/dlog_eq.rs @@ -111,7 +111,7 @@ pub struct Data { } /// Functions for interactive protocol for the proof. See usage example in -/// [`generic_ec_zkp::dlog_eq`] +/// [`dlog_eq`](crate::dlog_eq) /// /// Based on "Zero-Knowledge Proofs Notes", 2024 by Jorge L. Villar pub mod interactive { @@ -184,11 +184,11 @@ pub mod interactive { } /// Functions for non interactive variant of the proof. See usage example in -/// [`generic_ec_zkp::dlog_eq`] +/// [`dlog_eq`](crate::dlog_eq) /// /// Compared to naive protocol produced by Fiat-Shamir heuristic, this is /// optimized to send less data in proof. It's based on `PrEq` functionality in -/// this paper: https://eprint.iacr.org/2020/096 +/// this paper: #[cfg(feature = "udigest")] pub mod non_interactive { use generic_ec::{Curve, Point, Scalar, SecretScalar}; From 6d68653b79aac01410bc5c22a4e27e4acda5c14a Mon Sep 17 00:00:00 2001 From: maurges Date: Thu, 23 Jan 2025 14:46:27 +0100 Subject: [PATCH 4/9] Bump version Signed-off-by: maurges --- generic-ec-zkp/CHANGELOG.md | 5 +++++ generic-ec-zkp/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/generic-ec-zkp/CHANGELOG.md b/generic-ec-zkp/CHANGELOG.md index 03a9169..571474f 100644 --- a/generic-ec-zkp/CHANGELOG.md +++ b/generic-ec-zkp/CHANGELOG.md @@ -1,3 +1,8 @@ +## v0.4.4 +* Add `dlog_eq` proof [#54] + +[#54]: https://github.com/LFDT-Lockness/generic-ec/pull/54 + ## v0.4.3 * Fix double header menu issue on docs.rs [#49] diff --git a/generic-ec-zkp/Cargo.toml b/generic-ec-zkp/Cargo.toml index 78b35c6..39fab4c 100644 --- a/generic-ec-zkp/Cargo.toml +++ b/generic-ec-zkp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "generic-ec-zkp" -version = "0.4.3" +version = "0.4.4" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/LFDT-Lockness/generic-ec" From ea7d3182657b76ce7d2fc4d99ed2ae2695fcdce4 Mon Sep 17 00:00:00 2001 From: maurges Date: Thu, 23 Jan 2025 18:33:22 +0100 Subject: [PATCH 5/9] Improvements to challenge generation Signed-off-by: maurges --- Cargo.lock | 3 +-- generic-ec-zkp/Cargo.toml | 3 +-- generic-ec-zkp/src/dlog_eq.rs | 12 ++++++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 10e9d25..679838a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -783,7 +783,7 @@ dependencies = [ [[package]] name = "generic-ec-zkp" -version = "0.4.3" +version = "0.4.4" dependencies = [ "criterion", "digest", @@ -793,7 +793,6 @@ dependencies = [ "rand", "rand_core", "rand_dev", - "rand_hash", "serde", "sha2", "subtle", diff --git a/generic-ec-zkp/Cargo.toml b/generic-ec-zkp/Cargo.toml index 39fab4c..efff7e9 100644 --- a/generic-ec-zkp/Cargo.toml +++ b/generic-ec-zkp/Cargo.toml @@ -18,7 +18,6 @@ udigest = { workspace = true, features = ["derive"], optional = true } subtle.workspace = true rand_core.workspace = true -rand_hash.workspace = true serde = { workspace = true, features = ["derive"], optional = true } @@ -41,7 +40,7 @@ default = ["std"] std = ["alloc"] alloc = ["udigest?/alloc", "serde?/alloc"] serde = ["dep:serde", "generic-ec/serde", "generic-array/serde"] -udigest = ["dep:udigest", "dep:digest", "generic-ec/udigest"] +udigest = ["dep:udigest", "dep:digest", "generic-ec/udigest", "generic-ec/hash-to-scalar"] [package.metadata.docs.rs] all-features = true diff --git a/generic-ec-zkp/src/dlog_eq.rs b/generic-ec-zkp/src/dlog_eq.rs index 72df61e..af39351 100644 --- a/generic-ec-zkp/src/dlog_eq.rs +++ b/generic-ec-zkp/src/dlog_eq.rs @@ -141,6 +141,12 @@ pub mod interactive { commit(data.base, r) } + /// First round of the protocol: verifier generates a challenge. A challenge + /// is simply a random scalar + pub fn challenge(rng: &mut impl rand_core::RngCore) -> Challenge { + Scalar::random(rng) + } + /// Second round of the protocol: produce the proof for [`Data`] commited to /// with the given nonce `r` /// @@ -234,8 +240,7 @@ pub mod non_interactive { com1, com2, }); - let mut rng = rand_hash::HashRng::::from_seed(seed); - let ch = Scalar::random(&mut rng); + let ch = Scalar::from_hash::(&seed); let res = r + share * ch; Proof { ch, res } @@ -262,8 +267,7 @@ pub mod non_interactive { com1, com2, }); - let mut rng = rand_hash::HashRng::::from_seed(seed); - let ch = Scalar::random(&mut rng); + let ch = Scalar::from_hash::(&seed); if ch != proof.ch { Err(InvalidProof) From 24164d3d45bf113e9ce3752fe6081d26eb1c60ac Mon Sep 17 00:00:00 2001 From: maurges Date: Thu, 23 Jan 2025 18:48:34 +0100 Subject: [PATCH 6/9] Make the proof more general Signed-off-by: maurges --- generic-ec-zkp/src/dlog_eq.rs | 69 ++++++++++++++++++++--------------- tests/tests/dlog_eq.rs | 40 +++++--------------- 2 files changed, 50 insertions(+), 59 deletions(-) diff --git a/generic-ec-zkp/src/dlog_eq.rs b/generic-ec-zkp/src/dlog_eq.rs index af39351..82437ee 100644 --- a/generic-ec-zkp/src/dlog_eq.rs +++ b/generic-ec-zkp/src/dlog_eq.rs @@ -32,11 +32,8 @@ //! // `Z` is the object of the proof //! let Z = H * &x; //! -//! let data = non_interactive::Data { -//! pub_share: X, -//! base: H, -//! exp: Z, -//! }; +//! let data = non_interactive::Data::from_secret_key(&x, H); +//! assert_eq!(Z, data.exp2); //! //! // Prover chooses a private nonce //! let r = Scalar::random(&mut OsRng); @@ -69,11 +66,8 @@ //! // `Z` is the object of the proof //! let Z = H * &x; //! -//! let data = interactive::Data { -//! pub_share: X, -//! base: H, -//! exp: Z, -//! }; +//! let data = interactive::Data::from_secret_key(&x, H); +//! assert_eq!(Z, data.exp2); //! //! // Prover chooses a nonce for itself //! let r = Scalar::random(&mut OsRng); @@ -98,16 +92,33 @@ use generic_ec::{Curve, Point}; -/// Object of the proof +/// Object of the proof: `log_base1 exp1 == log_base2 exp2` #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "udigest", derive(udigest::Digestable), udigest(bound = ""))] pub struct Data { - /// `X`, the value inside `log_G` - pub pub_share: Point, - /// `H`, the base for the second logarithm - pub base: Point, - /// `Z`, the value inside the second logarithm `log_H` - pub exp: Point, + /// `G`, the base for one logarithm + pub base1: Point, + /// `X`, the value inside one logarithm + pub exp1: Point, + /// `H`, the base for the other logarithm + pub base2: Point, + /// `Z`, the value inside the other logarithm + pub exp2: Point, +} + +impl Data { + /// Create the data for the common case where `G` is the principal group + /// generator, and `x` is a secret key + /// + /// In this case, we set `H = base` as base2 and `Z = G * x` as exp2 + pub fn from_secret_key(x: &generic_ec::SecretScalar, base: Point) -> Data { + Self { + base1: Point::generator().into(), + exp1: Point::generator() * x, + base2: base, + exp2: base * x, + } + } } /// Functions for interactive protocol for the proof. See usage example in @@ -130,15 +141,15 @@ pub mod interactive { /// [`Commitment`] to be sent /// /// - `r` - random nonce. Can be produced by `Scalar::random(&mut rng)` - pub fn commit(base: Point, r: Scalar) -> Commitment { - (Point::generator() * r, base * r) + pub fn commit(base1: Point, base2: Point, r: Scalar) -> Commitment { + (base1 * r, base2 * r) } /// First round of the protocol: commit to well-constructed [`Data`] by /// itself. Produces [`Commitment`] to be sent /// /// - `r` - random nonce. Can be produced by `Scalar::random(&mut rng)` pub fn commit_data(data: Data, r: Scalar) -> Commitment { - commit(data.base, r) + commit(data.base1, data.base2, r) } /// First round of the protocol: verifier generates a challenge. A challenge @@ -173,14 +184,14 @@ pub mod interactive { ) -> Result<(), InvalidProof> { let (a1, a2) = comm; // equation 1 - let lhs = Point::generator() * proof; - let rhs = a1 + data.pub_share * challenge; + let lhs = data.base1 * proof; + let rhs = a1 + data.exp1 * challenge; if lhs != rhs { return Err(InvalidProof); } // equation 2 - let lhs = data.base * proof; - let rhs = a2 + data.exp * challenge; + let lhs = data.base2 * proof; + let rhs = a2 + data.exp2 * challenge; if lhs != rhs { return Err(InvalidProof); } @@ -197,7 +208,7 @@ pub mod interactive { /// this paper: #[cfg(feature = "udigest")] pub mod non_interactive { - use generic_ec::{Curve, Point, Scalar, SecretScalar}; + use generic_ec::{Curve, Scalar, SecretScalar}; const TAG: &str = "bls-style-digest.zkp"; @@ -231,8 +242,8 @@ pub mod non_interactive { data: Data, r: Scalar, ) -> Proof { - let com1 = Point::generator() * r; - let com2 = data.base * r; + let com1 = data.base1 * r; + let com2 = data.base2 * r; let seed = udigest::inline_struct!(TAG { shared_state, @@ -258,8 +269,8 @@ pub mod non_interactive { data: Data, proof: Proof, ) -> Result<(), InvalidProof> { - let com1 = Point::generator() * proof.res - data.pub_share * proof.ch; - let com2 = data.base * proof.res - data.exp * proof.ch; + let com1 = data.base1 * proof.res - data.exp1 * proof.ch; + let com2 = data.base2 * proof.res - data.exp2 * proof.ch; let seed = udigest::inline_struct!(TAG { shared_state, diff --git a/tests/tests/dlog_eq.rs b/tests/tests/dlog_eq.rs index 935003d..5411e79 100644 --- a/tests/tests/dlog_eq.rs +++ b/tests/tests/dlog_eq.rs @@ -7,21 +7,15 @@ mod interactive { let mut rng = rand_dev::DevRng::new(); let secret_share = generic_ec::SecretScalar::::random(&mut rng); - let public_share = generic_ec::Point::generator() * &secret_share; let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); - let exp = base * &secret_share; + let data = dlog_eq::Data::from_secret_key(&secret_share, base); let r = generic_ec::Scalar::random(&mut rng); - let comm = dlog_eq::commit(base, r); + let comm = dlog_eq::commit_data(data, r); let challenge = generic_ec::Scalar::random(&mut rng); let proof = dlog_eq::prove(r, challenge, &secret_share); - let data = dlog_eq::Data { - pub_share: public_share, - base, - exp, - }; dlog_eq::verify(data, comm, challenge, proof).unwrap() } #[test] @@ -29,22 +23,17 @@ mod interactive { let mut rng = rand_dev::DevRng::new(); let secret_share = generic_ec::SecretScalar::::random(&mut rng); - let public_share = generic_ec::Point::generator() * &secret_share; let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); + let mut data = dlog_eq::Data::from_secret_key(&secret_share, base); // Replace the exponentiation with something wrong - let exp = base * &secret_share + generic_ec::Point::generator(); + data.exp2 += generic_ec::Point::generator(); let r = generic_ec::Scalar::random(&mut rng); - let comm = dlog_eq::commit(base, r); + let comm = dlog_eq::commit_data(data, r); let challenge = generic_ec::Scalar::random(&mut rng); let proof = dlog_eq::prove(r, challenge, &secret_share); - let data = dlog_eq::Data { - pub_share: public_share, - base, - exp, - }; assert!( dlog_eq::verify(data, comm, challenge, proof).is_err(), "proof should fail" @@ -71,17 +60,11 @@ mod non_interactive { let shared_state = "shared state"; let secret_share = generic_ec::SecretScalar::random(&mut rng); - let public_share = generic_ec::Point::generator() * &secret_share; let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); - let exp = base * &secret_share; + let data = dlog_eq::Data::from_secret_key(&secret_share, base); let r = generic_ec::Scalar::random(&mut rng); - let data = dlog_eq::Data { - pub_share: public_share, - base, - exp, - }; let proof = dlog_eq::prove::(&shared_state, &secret_share, data, r); dlog_eq::verify::(&shared_state, data, proof).unwrap(); } @@ -92,17 +75,14 @@ mod non_interactive { let shared_state = "shared state"; let secret_share = generic_ec::SecretScalar::random(&mut rng); - let public_share = generic_ec::Point::generator() * &secret_share; let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); - let exp = base * &secret_share + generic_ec::Point::generator(); + let mut data = dlog_eq::Data::from_secret_key(&secret_share, base); + // make exp2 wrong so that proof won't hold + data.exp2 += generic_ec::Point::generator(); let r = generic_ec::Scalar::random(&mut rng); - let data = dlog_eq::Data { - pub_share: public_share, - base, - exp, - }; + let proof = dlog_eq::prove::(&shared_state, &secret_share, data, r); assert!(dlog_eq::verify::(&shared_state, data, proof).is_err()); } From d2b47ddbf6133b9994a7b3c90a3693c66f3d1a39 Mon Sep 17 00:00:00 2001 From: maurges Date: Thu, 23 Jan 2025 18:51:00 +0100 Subject: [PATCH 7/9] Fix the docs and the tag Signed-off-by: maurges --- generic-ec-zkp/src/dlog_eq.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generic-ec-zkp/src/dlog_eq.rs b/generic-ec-zkp/src/dlog_eq.rs index 82437ee..161eaaf 100644 --- a/generic-ec-zkp/src/dlog_eq.rs +++ b/generic-ec-zkp/src/dlog_eq.rs @@ -4,7 +4,7 @@ //! verifier $\V$ that it knows the discrete logarithms of two values, and that //! those logarithms are equal. $\P$ knows the secret data $x$ - the discrete //! logarithm. $\P$ and $\V$ share common data: -//! - $H$ - a point on an elliptic curve +//! - $G$, $H$ - some generators of the elliptic curve //! - $X = x \cdot G$ //! - $Z = x \cdot H$ //! @@ -210,7 +210,7 @@ pub mod interactive { pub mod non_interactive { use generic_ec::{Curve, Scalar, SecretScalar}; - const TAG: &str = "bls-style-digest.zkp"; + const TAG: &str = "generic-ec-zkp.dlog_eq.non_interactive"; pub use super::Data; pub use super::InvalidProof; From 7fec99d254c1e06220e0cb8aa859ff8f32158466 Mon Sep 17 00:00:00 2001 From: maurges Date: Mon, 27 Jan 2025 11:17:38 +0100 Subject: [PATCH 8/9] Use rng instead of explicit nonces Signed-off-by: maurges --- generic-ec-zkp/src/dlog_eq.rs | 55 ++++++++++++++++++++++------------- tests/tests/dlog_eq.rs | 19 ++++-------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/generic-ec-zkp/src/dlog_eq.rs b/generic-ec-zkp/src/dlog_eq.rs index 161eaaf..7176ed5 100644 --- a/generic-ec-zkp/src/dlog_eq.rs +++ b/generic-ec-zkp/src/dlog_eq.rs @@ -35,10 +35,8 @@ //! let data = non_interactive::Data::from_secret_key(&x, H); //! assert_eq!(Z, data.exp2); //! -//! // Prover chooses a private nonce -//! let r = Scalar::random(&mut OsRng); //! // Prover proves in zero knowledge the equality of `H * x = Z` -//! let proof = non_interactive::prove::(&"shared_state", &x, data, r); +//! let proof = non_interactive::prove::(&"shared_state", &x, data, &mut OsRng); //! // The proof is sent to the Verifier //! send(proof); //! @@ -69,10 +67,8 @@ //! let data = interactive::Data::from_secret_key(&x, H); //! assert_eq!(Z, data.exp2); //! -//! // Prover chooses a nonce for itself -//! let r = Scalar::random(&mut OsRng); //! // Prover commits to the data -//! let commitment = interactive::commit_data(data, r); +//! let (commitment, private_commitment) = interactive::commit_data(data, &mut OsRng); //! // Prover sends this commitment to the verifier //! send(commitment); //! @@ -81,7 +77,7 @@ //! send(challenge); //! //! // Prover receives the challenge and computes the proof of equality `H * x = Z` -//! let proof = interactive::prove(r, challenge, &x); +//! let proof = interactive::prove(private_commitment, challenge, &x); //! // This proof is sent to the verifier //! send(proof); //! @@ -130,6 +126,8 @@ pub mod interactive { /// Commitment to `H`, sent in the first round pub type Commitment = (Point, Point); + /// Private part of commitment, the nonce `r`. Kept by the Prover + pub type PrivateCommitment = Scalar; /// Challenge, sent to the Prover in the first round pub type Challenge = Scalar; /// Proof data, sent by prover to the verifier @@ -140,16 +138,25 @@ pub mod interactive { /// First round of the protocol: commit to `H` by itself. Produces /// [`Commitment`] to be sent /// - /// - `r` - random nonce. Can be produced by `Scalar::random(&mut rng)` - pub fn commit(base1: Point, base2: Point, r: Scalar) -> Commitment { - (base1 * r, base2 * r) + /// `rng` is used to generate the nonce for the private commitment + pub fn commit( + base1: Point, + base2: Point, + rng: &mut impl rand_core::RngCore, + ) -> (Commitment, PrivateCommitment) { + let r = Scalar::random(rng); + let comm = (base1 * r, base2 * r); + (comm, r) } /// First round of the protocol: commit to well-constructed [`Data`] by /// itself. Produces [`Commitment`] to be sent /// - /// - `r` - random nonce. Can be produced by `Scalar::random(&mut rng)` - pub fn commit_data(data: Data, r: Scalar) -> Commitment { - commit(data.base1, data.base2, r) + /// `rng` is used to generate the nonce for the private commitment + pub fn commit_data( + data: Data, + rng: &mut impl rand_core::RngCore, + ) -> (Commitment, PrivateCommitment) { + commit(data.base1, data.base2, rng) } /// First round of the protocol: verifier generates a challenge. A challenge @@ -158,13 +165,18 @@ pub mod interactive { Scalar::random(rng) } - /// Second round of the protocol: produce the proof for [`Data`] commited to - /// with the given nonce `r` + /// Second round of the protocol: produce the proof for [`Data`] with the + /// given private commitment, public part of which was communicated to the + /// verifier /// - /// - `r` - nonce, the same as used in [`commit`] or [`commit_data`] + /// - `r` - private commitment, the same as given by [`commit`] or [`commit_data`] /// - `challenge` - challenge sent by the Verifier /// - `x` - secret value of discrete logarithm - pub fn prove(r: Scalar, challenge: Scalar, x: &SecretScalar) -> Proof { + pub fn prove( + r: PrivateCommitment, + challenge: Scalar, + x: &SecretScalar, + ) -> Proof { r + challenge * x } @@ -204,8 +216,8 @@ pub mod interactive { /// [`dlog_eq`](crate::dlog_eq) /// /// Compared to naive protocol produced by Fiat-Shamir heuristic, this is -/// optimized to send less data in proof. It's based on `PrEq` functionality in -/// this paper: +/// optimized to send less data in the proof. It's based on `PrEq` functionality +/// in this paper: #[cfg(feature = "udigest")] pub mod non_interactive { use generic_ec::{Curve, Scalar, SecretScalar}; @@ -235,13 +247,14 @@ pub mod non_interactive { /// from replay attacks /// - `share` - `x`, secret value of discrete logarithm /// - `data` - data to compute the proof for - /// - `r` - random nonce. Can be produced by `Scalar::random(&mut rng)` + /// - `rng` - used to generate a random nonce for proof pub fn prove( shared_state: &impl udigest::Digestable, share: &SecretScalar, data: Data, - r: Scalar, + rng: &mut impl rand_core::RngCore, ) -> Proof { + let r = Scalar::random(rng); let com1 = data.base1 * r; let com2 = data.base2 * r; diff --git a/tests/tests/dlog_eq.rs b/tests/tests/dlog_eq.rs index 5411e79..7e7401b 100644 --- a/tests/tests/dlog_eq.rs +++ b/tests/tests/dlog_eq.rs @@ -11,11 +11,9 @@ mod interactive { let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); let data = dlog_eq::Data::from_secret_key(&secret_share, base); - let r = generic_ec::Scalar::random(&mut rng); - - let comm = dlog_eq::commit_data(data, r); + let (comm, pcomm) = dlog_eq::commit_data(data, &mut rng); let challenge = generic_ec::Scalar::random(&mut rng); - let proof = dlog_eq::prove(r, challenge, &secret_share); + let proof = dlog_eq::prove(pcomm, challenge, &secret_share); dlog_eq::verify(data, comm, challenge, proof).unwrap() } #[test] @@ -29,11 +27,9 @@ mod interactive { // Replace the exponentiation with something wrong data.exp2 += generic_ec::Point::generator(); - let r = generic_ec::Scalar::random(&mut rng); - - let comm = dlog_eq::commit_data(data, r); + let (comm, pcomm) = dlog_eq::commit_data(data, &mut rng); let challenge = generic_ec::Scalar::random(&mut rng); - let proof = dlog_eq::prove(r, challenge, &secret_share); + let proof = dlog_eq::prove(pcomm, challenge, &secret_share); assert!( dlog_eq::verify(data, comm, challenge, proof).is_err(), "proof should fail" @@ -64,8 +60,7 @@ mod non_interactive { let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); let data = dlog_eq::Data::from_secret_key(&secret_share, base); - let r = generic_ec::Scalar::random(&mut rng); - let proof = dlog_eq::prove::(&shared_state, &secret_share, data, r); + let proof = dlog_eq::prove::(&shared_state, &secret_share, data, &mut rng); dlog_eq::verify::(&shared_state, data, proof).unwrap(); } @@ -81,9 +76,7 @@ mod non_interactive { // make exp2 wrong so that proof won't hold data.exp2 += generic_ec::Point::generator(); - let r = generic_ec::Scalar::random(&mut rng); - - let proof = dlog_eq::prove::(&shared_state, &secret_share, data, r); + let proof = dlog_eq::prove::(&shared_state, &secret_share, data, &mut rng); assert!(dlog_eq::verify::(&shared_state, data, proof).is_err()); } From 564e8e85cd26b3217aeb1aa65c1fb0cc48289a2e Mon Sep 17 00:00:00 2001 From: maurges Date: Tue, 28 Jan 2025 11:13:00 +0100 Subject: [PATCH 9/9] Improve docs and namings and parameter orders Signed-off-by: maurges --- generic-ec-zkp/src/dlog_eq.rs | 88 +++++++++++++++++++---------------- tests/tests/dlog_eq.rs | 20 ++++---- 2 files changed, 57 insertions(+), 51 deletions(-) diff --git a/generic-ec-zkp/src/dlog_eq.rs b/generic-ec-zkp/src/dlog_eq.rs index 7176ed5..165f62e 100644 --- a/generic-ec-zkp/src/dlog_eq.rs +++ b/generic-ec-zkp/src/dlog_eq.rs @@ -13,6 +13,10 @@ //! key, a fact known to both parties, and the proof is that $H \cdot x = Z$ //! without disclosing $x$. //! +//! Since in this library we use additive notation, some terminology has been +//! adjusted in function arguments, for example what would be an `exponent` is +//! called a `product`. +//! //! ## Non-interactive example //! //! ```rust @@ -33,15 +37,15 @@ //! let Z = H * &x; //! //! let data = non_interactive::Data::from_secret_key(&x, H); -//! assert_eq!(Z, data.exp2); +//! assert_eq!(Z, data.prod2); //! //! // Prover proves in zero knowledge the equality of `H * x = Z` -//! let proof = non_interactive::prove::(&"shared_state", &x, data, &mut OsRng); +//! let proof = non_interactive::prove::(&mut OsRng, &"shared_state", &x, data); //! // The proof is sent to the Verifier //! send(proof); //! //! // Verifier checks that the proof is correct -//! non_interactive::verify::(&"shared_state", data, proof) +//! non_interactive::verify::(&"shared_state", data, proof) //! .expect("Verification failed!"); //! ``` //! @@ -65,10 +69,10 @@ //! let Z = H * &x; //! //! let data = interactive::Data::from_secret_key(&x, H); -//! assert_eq!(Z, data.exp2); +//! assert_eq!(Z, data.prod2); //! //! // Prover commits to the data -//! let (commitment, private_commitment) = interactive::commit_data(data, &mut OsRng); +//! let (commitment, private_commitment) = interactive::commit_data(&mut OsRng, data); //! // Prover sends this commitment to the verifier //! send(commitment); //! @@ -88,31 +92,31 @@ use generic_ec::{Curve, Point}; -/// Object of the proof: `log_base1 exp1 == log_base2 exp2` +/// Object of the proof: `log_gen1 prod1 == log_gen2 prod2` #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "udigest", derive(udigest::Digestable), udigest(bound = ""))] pub struct Data { - /// `G`, the base for one logarithm - pub base1: Point, - /// `X`, the value inside one logarithm - pub exp1: Point, - /// `H`, the base for the other logarithm - pub base2: Point, - /// `Z`, the value inside the other logarithm - pub exp2: Point, + /// `G`, a generator, in multiplicative notation the base for one logarithm + pub gen1: Point, + /// `X`, a point `G * x`, in multiplicative notation the value inside one logarithm + pub prod1: Point, + /// `H`, a generator, in multiplicative notation the base for the other logarithm + pub gen2: Point, + /// `Z`, a point `H * x`, in multiplicative notation the value inside the other logarithm + pub prod2: Point, } impl Data { /// Create the data for the common case where `G` is the principal group /// generator, and `x` is a secret key /// - /// In this case, we set `H = base` as base2 and `Z = G * x` as exp2 - pub fn from_secret_key(x: &generic_ec::SecretScalar, base: Point) -> Data { + /// In this case, we set `H = gen` as gen2 and `Z = G * x` as prod2 + pub fn from_secret_key(x: &generic_ec::SecretScalar, gen: Point) -> Data { Self { - base1: Point::generator().into(), - exp1: Point::generator() * x, - base2: base, - exp2: base * x, + gen1: Point::generator().into(), + prod1: Point::generator() * x, + gen2: gen, + prod2: gen * x, } } } @@ -140,12 +144,12 @@ pub mod interactive { /// /// `rng` is used to generate the nonce for the private commitment pub fn commit( - base1: Point, - base2: Point, - rng: &mut impl rand_core::RngCore, + rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng), + gen1: Point, + gen2: Point, ) -> (Commitment, PrivateCommitment) { let r = Scalar::random(rng); - let comm = (base1 * r, base2 * r); + let comm = (gen1 * r, gen2 * r); (comm, r) } /// First round of the protocol: commit to well-constructed [`Data`] by @@ -153,15 +157,17 @@ pub mod interactive { /// /// `rng` is used to generate the nonce for the private commitment pub fn commit_data( + rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng), data: Data, - rng: &mut impl rand_core::RngCore, ) -> (Commitment, PrivateCommitment) { - commit(data.base1, data.base2, rng) + commit(rng, data.gen1, data.gen2) } /// First round of the protocol: verifier generates a challenge. A challenge /// is simply a random scalar - pub fn challenge(rng: &mut impl rand_core::RngCore) -> Challenge { + pub fn challenge( + rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng), + ) -> Challenge { Scalar::random(rng) } @@ -196,14 +202,14 @@ pub mod interactive { ) -> Result<(), InvalidProof> { let (a1, a2) = comm; // equation 1 - let lhs = data.base1 * proof; - let rhs = a1 + data.exp1 * challenge; + let lhs = data.gen1 * proof; + let rhs = a1 + data.prod1 * challenge; if lhs != rhs { return Err(InvalidProof); } // equation 2 - let lhs = data.base2 * proof; - let rhs = a2 + data.exp2 * challenge; + let lhs = data.gen2 * proof; + let rhs = a2 + data.prod2 * challenge; if lhs != rhs { return Err(InvalidProof); } @@ -245,18 +251,18 @@ pub mod non_interactive { /// /// - `shared_state` - shared data not known to this proof, used to protect /// from replay attacks - /// - `share` - `x`, secret value of discrete logarithm + /// - `x` - the secret value of discrete logarithm /// - `data` - data to compute the proof for /// - `rng` - used to generate a random nonce for proof - pub fn prove( + pub fn prove( + rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng), shared_state: &impl udigest::Digestable, - share: &SecretScalar, + x: &SecretScalar, data: Data, - rng: &mut impl rand_core::RngCore, ) -> Proof { let r = Scalar::random(rng); - let com1 = data.base1 * r; - let com2 = data.base2 * r; + let com1 = data.gen1 * r; + let com2 = data.gen2 * r; let seed = udigest::inline_struct!(TAG { shared_state, @@ -266,7 +272,7 @@ pub mod non_interactive { }); let ch = Scalar::from_hash::(&seed); - let res = r + share * ch; + let res = r + x * ch; Proof { ch, res } } @@ -277,13 +283,13 @@ pub mod non_interactive { /// - `data` - data for which the proof was computed /// - `proof` - produced by the Prover in the second round for this data and /// shared_state - pub fn verify( + pub fn verify( shared_state: &impl udigest::Digestable, data: Data, proof: Proof, ) -> Result<(), InvalidProof> { - let com1 = data.base1 * proof.res - data.exp1 * proof.ch; - let com2 = data.base2 * proof.res - data.exp2 * proof.ch; + let com1 = data.gen1 * proof.res - data.prod1 * proof.ch; + let com2 = data.gen2 * proof.res - data.prod2 * proof.ch; let seed = udigest::inline_struct!(TAG { shared_state, diff --git a/tests/tests/dlog_eq.rs b/tests/tests/dlog_eq.rs index 7e7401b..304abe4 100644 --- a/tests/tests/dlog_eq.rs +++ b/tests/tests/dlog_eq.rs @@ -11,8 +11,8 @@ mod interactive { let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); let data = dlog_eq::Data::from_secret_key(&secret_share, base); - let (comm, pcomm) = dlog_eq::commit_data(data, &mut rng); - let challenge = generic_ec::Scalar::random(&mut rng); + let (comm, pcomm) = dlog_eq::commit_data(&mut rng, data); + let challenge = dlog_eq::challenge(&mut rng); let proof = dlog_eq::prove(pcomm, challenge, &secret_share); dlog_eq::verify(data, comm, challenge, proof).unwrap() } @@ -25,10 +25,10 @@ mod interactive { let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); let mut data = dlog_eq::Data::from_secret_key(&secret_share, base); // Replace the exponentiation with something wrong - data.exp2 += generic_ec::Point::generator(); + data.prod2 += generic_ec::Point::generator(); - let (comm, pcomm) = dlog_eq::commit_data(data, &mut rng); - let challenge = generic_ec::Scalar::random(&mut rng); + let (comm, pcomm) = dlog_eq::commit_data(&mut rng, data); + let challenge = dlog_eq::challenge(&mut rng); let proof = dlog_eq::prove(pcomm, challenge, &secret_share); assert!( dlog_eq::verify(data, comm, challenge, proof).is_err(), @@ -60,8 +60,8 @@ mod non_interactive { let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); let data = dlog_eq::Data::from_secret_key(&secret_share, base); - let proof = dlog_eq::prove::(&shared_state, &secret_share, data, &mut rng); - dlog_eq::verify::(&shared_state, data, proof).unwrap(); + let proof = dlog_eq::prove::(&mut rng, &shared_state, &secret_share, data); + dlog_eq::verify::(&shared_state, data, proof).unwrap(); } #[test] @@ -74,10 +74,10 @@ mod non_interactive { let base = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); let mut data = dlog_eq::Data::from_secret_key(&secret_share, base); // make exp2 wrong so that proof won't hold - data.exp2 += generic_ec::Point::generator(); + data.prod2 += generic_ec::Point::generator(); - let proof = dlog_eq::prove::(&shared_state, &secret_share, data, &mut rng); - assert!(dlog_eq::verify::(&shared_state, data, proof).is_err()); + let proof = dlog_eq::prove::(&mut rng, &shared_state, &secret_share, data); + assert!(dlog_eq::verify::(&shared_state, data, proof).is_err()); } #[instantiate_tests()]