From e2804b693786005b208348e5c9b9e6416127ad33 Mon Sep 17 00:00:00 2001 From: klizz111 Date: Thu, 12 Mar 2026 23:43:37 +0800 Subject: [PATCH 1/3] fin basic bbs sign function --- .github/.devcontainer.json | 28 +++++++++++++++++++ .github/chatmodes/config.chatmode.md | 5 ++++ .github/workflows/rust-test.yml | 42 ++++++++++++++++++++++++++++ bbs/Cargo.lock | 28 +++++++++---------- bbs/Cargo.toml | 2 +- bbs/src/bbs_bn254/mod.rs | 3 +- bbs/src/bbs_bn254/signer.rs | 18 ++++++++---- bbs/tests/test_sign.rs | 19 +++++++++++++ 8 files changed, 124 insertions(+), 21 deletions(-) create mode 100644 .github/.devcontainer.json create mode 100644 .github/chatmodes/config.chatmode.md create mode 100644 .github/workflows/rust-test.yml create mode 100644 bbs/tests/test_sign.rs diff --git a/.github/.devcontainer.json b/.github/.devcontainer.json new file mode 100644 index 0000000..3ea35b4 --- /dev/null +++ b/.github/.devcontainer.json @@ -0,0 +1,28 @@ +{ + "name": "BBS Rust Dev Container", + "image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye", + "workspaceFolder": "/workspaces/BBS_rust", + "customizations": { + "vscode": { + "extensions": [ + "rust-lang.rust-analyzer", + "vadimcn.vscode-lldb", + "tamasfe.even-better-toml", + "serayuzgur.crates" + ], + "settings": { + "rust-analyzer.check.command": "clippy", + "rust-analyzer.cargo.features": "all", + "rust-analyzer.linkedProjects": [ + "/workspaces/BBS_rust/bbs/Cargo.toml" + ], + "editor.formatOnSave": true, + "files.watcherExclude": { + "**/bbs/target/**": true + } + } + } + }, + "postCreateCommand": "cd /workspaces/BBS_rust/bbs && rustup component add rustfmt clippy && cargo fetch", + "remoteUser": "vscode" +} diff --git a/.github/chatmodes/config.chatmode.md b/.github/chatmodes/config.chatmode.md new file mode 100644 index 0000000..490cf3e --- /dev/null +++ b/.github/chatmodes/config.chatmode.md @@ -0,0 +1,5 @@ +--- +description: 'Description of the custom chat mode.' +tools: ['runCommands', 'runTasks', 'edit', 'search', 'new', 'extensions', 'usages', 'think', 'problems', 'changes', 'testFailure', 'openSimpleBrowser', 'fetch', 'githubRepo', 'github.vscode-pull-request-github/copilotCodingAgent', 'github.vscode-pull-request-github/activePullRequest', 'github.vscode-pull-request-github/openPullRequest', 'todos'] +--- +Define the purpose of this chat mode and how AI should behave: response style, available tools, focus areas, and any mode-specific instructions or constraints. \ No newline at end of file diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml new file mode 100644 index 0000000..7cfc635 --- /dev/null +++ b/.github/workflows/rust-test.yml @@ -0,0 +1,42 @@ +name: Rust CI + +"on": + push: + branches: + - master + pull_request: + branches: + - master + workflow_dispatch: + +jobs: + build-and-test: + name: Build and test signer + runs-on: ubuntu-latest + defaults: + run: + working-directory: bbs + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install stable Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Cache cargo registry and build artifacts + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + bbs -> target + + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Build + run: cargo build --verbose + + - name: Run tests + run: cargo test --verbose diff --git a/bbs/Cargo.lock b/bbs/Cargo.lock index 7a27923..cf5d1b4 100644 --- a/bbs/Cargo.lock +++ b/bbs/Cargo.lock @@ -156,6 +156,20 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "bbs" +version = "0.1.0" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "sha2", + "substrate-bn", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -409,20 +423,6 @@ dependencies = [ "digest", ] -[[package]] -name = "signer" -version = "0.1.0" -dependencies = [ - "ark-bn254", - "ark-ec", - "ark-ff", - "ark-poly", - "ark-serialize", - "ark-std", - "sha2", - "substrate-bn", -] - [[package]] name = "spin" version = "0.9.8" diff --git a/bbs/Cargo.toml b/bbs/Cargo.toml index 3026c53..2a0a0a9 100644 --- a/bbs/Cargo.toml +++ b/bbs/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "signer" +name = "bbs" version = "0.1.0" edition = "2024" diff --git a/bbs/src/bbs_bn254/mod.rs b/bbs/src/bbs_bn254/mod.rs index cfc3822..865c631 100644 --- a/bbs/src/bbs_bn254/mod.rs +++ b/bbs/src/bbs_bn254/mod.rs @@ -11,4 +11,5 @@ pub use blind::{ commitment_pok_prove, commitment_pok_prove_with_rng, commitment_pok_verify, -}; \ No newline at end of file +}; +pub use signer::sign_no_blind; \ No newline at end of file diff --git a/bbs/src/bbs_bn254/signer.rs b/bbs/src/bbs_bn254/signer.rs index 0fae31a..d358a5f 100644 --- a/bbs/src/bbs_bn254/signer.rs +++ b/bbs/src/bbs_bn254/signer.rs @@ -1,8 +1,8 @@ //! Impl signing related functions. -use ark_bn254::{G1Affine as G1, G2Affine as G2, Fr as Scalar}; +use ark_bn254::{G1Affine as G1, Fr as Scalar}; use ark_ec::CurveGroup; -use ark_ff::PrimeField; +use ark_ff::{Field}; use ark_std::{UniformRand, rand::RngCore, test_rng}; use crate::bbs_bn254::{Parameters, PrivateKey, structs::Signature}; @@ -13,7 +13,7 @@ pub fn sign_no_blind( messages: &[Scalar] ) -> Result { let mut rng = test_rng(); - sign_no_blind_with_rng(params, sk, messages, rng) + sign_no_blind_with_rng(params, sk, messages, &mut rng) } pub fn sign_no_blind_with_rng( @@ -38,6 +38,14 @@ pub fn sign_no_blind_with_rng( // calc (x + e) ^ (-1) let mut tmp = sk.x + e; tmp = tmp.inverse().ok_or("failed to compute inverse")?; - - Ok(()) + + // calc A = C * (x + e) ^ (-1) + let a = (c * tmp).into_affine() as G1; + + // Output signature (A, e, s) + Ok(Signature { + A: a, + e, + s + }) } \ No newline at end of file diff --git a/bbs/tests/test_sign.rs b/bbs/tests/test_sign.rs new file mode 100644 index 0000000..9688925 --- /dev/null +++ b/bbs/tests/test_sign.rs @@ -0,0 +1,19 @@ + +mod tests { + use bbs::bbs_bn254::*; + use ark_bn254::Fr as Scalar; + #[test] + fn test_sign() { + let (params, pk, sk) = keygen(5); + let messages = vec![ + Scalar::from(10u64), + Scalar::from(20u64), + Scalar::from(30u64), + Scalar::from(40u64), + Scalar::from(50u64), + ]; + + let signature = sign_no_blind(¶ms, &sk, &messages).unwrap(); + assert!(signature.A.is_on_curve()); + } +} \ No newline at end of file From dac63723ddd61c129a0e48ce6bd66b172fe6b87c Mon Sep 17 00:00:00 2001 From: klizz111 Date: Mon, 16 Mar 2026 15:57:49 +0800 Subject: [PATCH 2/3] fin verify func for no blind messages --- .github/chatmodes/config.chatmode.md | 5 - bbs/src/bbs_bn254/blind.rs | 243 ++++++++++--------- bbs/src/bbs_bn254/keygen.rs | 85 +++---- bbs/src/bbs_bn254/mod.rs | 19 +- bbs/src/bbs_bn254/signer.rs | 16 +- bbs/src/bbs_bn254/structs.rs | 4 +- bbs/src/bbs_bn254/utils.rs | 4 + bbs/src/bbs_bn254/verify.rs | 45 ++++ bbs/src/lib.rs | 2 +- bbs/tests/{test_sign.rs => test_no_blind.rs} | 10 +- 10 files changed, 243 insertions(+), 190 deletions(-) delete mode 100644 .github/chatmodes/config.chatmode.md create mode 100644 bbs/src/bbs_bn254/utils.rs create mode 100644 bbs/src/bbs_bn254/verify.rs rename bbs/tests/{test_sign.rs => test_no_blind.rs} (70%) diff --git a/.github/chatmodes/config.chatmode.md b/.github/chatmodes/config.chatmode.md deleted file mode 100644 index 490cf3e..0000000 --- a/.github/chatmodes/config.chatmode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -description: 'Description of the custom chat mode.' -tools: ['runCommands', 'runTasks', 'edit', 'search', 'new', 'extensions', 'usages', 'think', 'problems', 'changes', 'testFailure', 'openSimpleBrowser', 'fetch', 'githubRepo', 'github.vscode-pull-request-github/copilotCodingAgent', 'github.vscode-pull-request-github/activePullRequest', 'github.vscode-pull-request-github/openPullRequest', 'todos'] ---- -Define the purpose of this chat mode and how AI should behave: response style, available tools, focus areas, and any mode-specific instructions or constraints. \ No newline at end of file diff --git a/bbs/src/bbs_bn254/blind.rs b/bbs/src/bbs_bn254/blind.rs index c3161b1..c7d4062 100644 --- a/bbs/src/bbs_bn254/blind.rs +++ b/bbs/src/bbs_bn254/blind.rs @@ -1,11 +1,11 @@ //! User functions impl for some user-side operations, including: //! blind, unblind and proof of knowledge of committed values. -use ark_bn254::{G1Affine as G1, G1Projective as G1Projective, Fr as Scalar}; +use ark_bn254::{Fr as Scalar, G1Affine as G1, G1Projective}; use ark_ec::CurveGroup; use ark_ff::PrimeField; use ark_serialize::CanonicalSerialize; -use ark_std::{rand::RngCore, UniformRand}; +use ark_std::{UniformRand, rand::RngCore}; use sha2::{Digest, Sha256}; use crate::bbs_bn254::structs::{BlindedCommitment, CommitmentProof, Parameters}; @@ -13,145 +13,154 @@ use crate::bbs_bn254::structs::{BlindedCommitment, CommitmentProof, Parameters}; /// Create a blinded commitment for a list of messages. /// Returns `Err` if message length exceeds available parameters. /// data with index at and behind `blind_index` will be blinded, should be between 1 and messages.len() - 1 -pub fn blind(params: &Parameters, messages: &[Scalar], blind_index: usize) -> Result { - let mut rng = ark_std::test_rng(); - blind_with_rng(params, messages, blind_index, &mut rng) +pub fn blind( + params: &Parameters, + messages: &[Scalar], + blind_index: usize, +) -> Result { + let mut rng = ark_std::test_rng(); + blind_with_rng(params, messages, blind_index, &mut rng) } /// Create a blinded commitment using a caller-supplied RNG. pub fn blind_with_rng( - params: &Parameters, - messages: &[Scalar], - blind_index: usize, - rng: &mut R, + params: &Parameters, + messages: &[Scalar], + blind_index: usize, + rng: &mut R, ) -> Result { - if messages.len() > params.L { - return Err("message length exceeds parameters"); - } - if params.H.len() < messages.len() + 1 { - return Err("parameters do not include enough message base points"); - } - if blind_index == 0 || blind_index >= messages.len() { - return Err("invalid blind index"); - } - - // calc r*H_1 - let blinding_factor = Scalar::rand(rng); - let mut commitment = params.H[0] * blinding_factor; - - // calc m_j*H_{j+1} - for j in (blind_index - 1)..messages.len() { - commitment += params.H[j + 1] * messages[j]; - } - - Ok(BlindedCommitment { - commitment: commitment.into_affine(), - blinding_factor, - }) + if messages.len() > params.L { + return Err("message length exceeds parameters"); + } + if params.H.len() < messages.len() + 1 { + return Err("parameters do not include enough message base points"); + } + if blind_index == 0 || blind_index >= messages.len() { + return Err("invalid blind index"); + } + + // calc r*H_1 + let blinding_factor = Scalar::rand(rng); + let mut commitment = params.H[0] * blinding_factor; + + // calc m_j*H_{j+1} + for j in (blind_index - 1)..messages.len() { + commitment += params.H[j + 1] * messages[j]; + } + + Ok(BlindedCommitment { + commitment: commitment.into_affine(), + blinding_factor, + }) } /// Generate a proof of knowledge for the blinded commitment. pub fn commitment_pok_prove( - params: &Parameters, - commitment: &BlindedCommitment, - messages: &[Scalar], + params: &Parameters, + commitment: &BlindedCommitment, + messages: &[Scalar], ) -> Result { - let mut rng = ark_std::test_rng(); - commitment_pok_prove_with_rng(params, commitment, messages, &mut rng) + let mut rng = ark_std::test_rng(); + commitment_pok_prove_with_rng(params, commitment, messages, &mut rng) } /// Generate a proof of knowledge for the blinded commitment with a caller RNG. pub fn commitment_pok_prove_with_rng( - params: &Parameters, - commitment: &BlindedCommitment, - messages: &[Scalar], - rng: &mut R, + params: &Parameters, + commitment: &BlindedCommitment, + messages: &[Scalar], + rng: &mut R, ) -> Result { - if params.H.len() < messages.len() + 1 { - return Err("parameters do not include enough message base points"); - } - - let r_s = Scalar::rand(rng); - let mut r_ms = Vec::with_capacity(messages.len()); - for _ in 0..messages.len() { - r_ms.push(Scalar::rand(rng)); - } - - let mut t = params.H[0] * r_s; - for (i, r_m) in r_ms.iter().enumerate() { - t += params.H[i + 1] * r_m; - } - let t_affine = t.into_affine(); - - let challenge = commitment_challenge(&commitment.commitment, &t_affine, messages.len())?; - - let s_hat = r_s + challenge * commitment.blinding_factor; - let mut m_hats = Vec::with_capacity(messages.len()); - for (m, r_m) in messages.iter().zip(r_ms.iter()) { - m_hats.push(*r_m + challenge * m); - } - - Ok(CommitmentProof { - t: t_affine, - challenge, - s_hat, - m_hats, - }) + if params.H.len() < messages.len() + 1 { + return Err("parameters do not include enough message base points"); + } + + let r_s = Scalar::rand(rng); + let mut r_ms = Vec::with_capacity(messages.len()); + for _ in 0..messages.len() { + r_ms.push(Scalar::rand(rng)); + } + + let mut t = params.H[0] * r_s; + for (i, r_m) in r_ms.iter().enumerate() { + t += params.H[i + 1] * r_m; + } + let t_affine = t.into_affine(); + + let challenge = commitment_challenge(&commitment.commitment, &t_affine, messages.len())?; + + let s_hat = r_s + challenge * commitment.blinding_factor; + let mut m_hats = Vec::with_capacity(messages.len()); + for (m, r_m) in messages.iter().zip(r_ms.iter()) { + m_hats.push(*r_m + challenge * m); + } + + Ok(CommitmentProof { + t: t_affine, + challenge, + s_hat, + m_hats, + }) } /// Verify a commitment proof of knowledge. pub fn commitment_pok_verify( - params: &Parameters, - commitment: &BlindedCommitment, - proof: &CommitmentProof, + params: &Parameters, + commitment: &BlindedCommitment, + proof: &CommitmentProof, ) -> Result { - if params.H.len() < proof.m_hats.len() + 1 { - return Err("parameters do not include enough message base points"); - } - - let expected_challenge = - commitment_challenge(&commitment.commitment, &proof.t, proof.m_hats.len())?; - if expected_challenge != proof.challenge { - return Ok(false); - } - - let mut lhs = params.H[0] * proof.s_hat; - for (i, m_hat) in proof.m_hats.iter().enumerate() { - lhs += params.H[i + 1] * m_hat; - } - - let rhs = G1Projective::from(proof.t) + (commitment.commitment * proof.challenge); - Ok(lhs.into_affine() == rhs.into_affine()) + if params.H.len() < proof.m_hats.len() + 1 { + return Err("parameters do not include enough message base points"); + } + + let expected_challenge = + commitment_challenge(&commitment.commitment, &proof.t, proof.m_hats.len())?; + if expected_challenge != proof.challenge { + return Ok(false); + } + + let mut lhs = params.H[0] * proof.s_hat; + for (i, m_hat) in proof.m_hats.iter().enumerate() { + lhs += params.H[i + 1] * m_hat; + } + + let rhs = G1Projective::from(proof.t) + (commitment.commitment * proof.challenge); + Ok(lhs.into_affine() == rhs.into_affine()) } -fn commitment_challenge(commitment: &G1, t: &G1, message_len: usize) -> Result { - let mut bytes = Vec::new(); - commitment - .serialize_compressed(&mut bytes) - .map_err(|_| "failed to serialize commitment")?; - t.serialize_compressed(&mut bytes) - .map_err(|_| "failed to serialize commitment T")?; - bytes.extend_from_slice(&(message_len as u64).to_le_bytes()); - - let hash = Sha256::digest(bytes); - Ok(Scalar::from_le_bytes_mod_order(&hash)) +fn commitment_challenge( + commitment: &G1, + t: &G1, + message_len: usize, +) -> Result { + let mut bytes = Vec::new(); + commitment + .serialize_compressed(&mut bytes) + .map_err(|_| "failed to serialize commitment")?; + t.serialize_compressed(&mut bytes) + .map_err(|_| "failed to serialize commitment T")?; + bytes.extend_from_slice(&(message_len as u64).to_le_bytes()); + + let hash = Sha256::digest(bytes); + Ok(Scalar::from_le_bytes_mod_order(&hash)) } #[cfg(test)] mod tests { - use super::*; - use crate::bbs_bn254::keygen::keygen; - - #[test] - fn blind_and_pok_roundtrip() { - let (params, _pk, _sk) = keygen(3); - let messages = vec![Scalar::from(1u64), Scalar::from(2u64), Scalar::from(3u64)]; - - let mut rng = ark_std::test_rng(); - let commitment = blind_with_rng(¶ms, &messages, 1, &mut rng).unwrap(); - let proof = commitment_pok_prove_with_rng(¶ms, &commitment, &messages, &mut rng).unwrap(); - - let ok = commitment_pok_verify(¶ms, &commitment, &proof).unwrap(); - assert!(ok); - } + use super::*; + use crate::bbs_bn254::keygen::keygen; + + #[test] + fn blind_and_pok_roundtrip() { + let (params, _pk, _sk) = keygen(3); + let messages = vec![Scalar::from(1u64), Scalar::from(2u64), Scalar::from(3u64)]; + + let mut rng = ark_std::test_rng(); + let commitment = blind_with_rng(¶ms, &messages, 1, &mut rng).unwrap(); + let proof = + commitment_pok_prove_with_rng(¶ms, &commitment, &messages, &mut rng).unwrap(); + + let ok = commitment_pok_verify(¶ms, &commitment, &proof).unwrap(); + assert!(ok); + } } diff --git a/bbs/src/bbs_bn254/keygen.rs b/bbs/src/bbs_bn254/keygen.rs index c893f0a..3b97e93 100644 --- a/bbs/src/bbs_bn254/keygen.rs +++ b/bbs/src/bbs_bn254/keygen.rs @@ -1,9 +1,9 @@ //! This is the key generation module for the BBS+ signature scheme over the BN254 curve. //! Provides functions to generate secret keys, public keys, and key pairs for signing and verification. -use ark_bn254::{G1Affine as G1, G2Affine as G2, Fr as Scalar}; +use ark_bn254::{Fr as Scalar, G1Affine as G1, G2Affine as G2}; use ark_ec::{AffineRepr, CurveGroup}; -use ark_std::{rand::RngCore, UniformRand}; +use ark_std::{UniformRand, rand::RngCore}; use super::structs::{Parameters, PrivateKey, PublicKey}; @@ -11,49 +11,52 @@ use super::structs::{Parameters, PrivateKey, PublicKey}; /// /// Returns `(parameters, public_key, private_key)`. pub fn keygen(message_count: usize) -> (Parameters, PublicKey, PrivateKey) { - let mut rng = ark_std::test_rng(); - keygen_with_rng(message_count, &mut rng) + let mut rng = ark_std::test_rng(); + keygen_with_rng(message_count, &mut rng) } /// Same as `keygen`, but uses a caller-supplied RNG. -pub fn keygen_with_rng(message_count: usize, rng: &mut R) -> (Parameters, PublicKey, PrivateKey) { - let g1 = G1::generator(); - let g2 = G2::generator(); - - let mut h = Vec::with_capacity(message_count + 1); - for _ in 0..=message_count { - h.push(G1::rand(rng)); - } - - let x = Scalar::rand(rng); - let w = (g2 * x).into_affine(); - - let params = Parameters { - L: message_count, - g1, - g2, - H: h, - }; - - let pk = PublicKey { w }; - let sk = PrivateKey { x }; - - (params, pk, sk) +pub fn keygen_with_rng( + message_count: usize, + rng: &mut R, +) -> (Parameters, PublicKey, PrivateKey) { + let g1 = G1::generator(); + let g2 = G2::generator(); + + let mut h = Vec::with_capacity(message_count + 1); + for _ in 0..=message_count { + h.push(G1::rand(rng)); + } + + let x = Scalar::rand(rng); + let w = (g2 * x).into_affine(); + + let params = Parameters { + L: message_count, + g1, + g2, + H: h, + }; + + let pk = PublicKey { w }; + let sk = PrivateKey { x }; + + (params, pk, sk) } #[cfg(test)] mod tests { - use super::*; - use ark_std::Zero; - - #[test] - fn keygen_produces_expected_sizes() { - let (params, pk, sk) = keygen(3); - assert_eq!(params.L, 3); - assert_eq!(params.H.len(), 4); - assert!(!params.g1.is_zero()); - assert!(!params.g2.is_zero()); - assert!(!pk.w.is_zero()); - assert!(!sk.x.is_zero()); - } -} \ No newline at end of file + use super::*; + use ark_std::Zero; + + #[test] + fn keygen_produces_expected_sizes() { + let (params, pk, sk) = keygen(3); + assert_eq!(params.L, 3); + assert_eq!(params.H.len(), 4); + assert!(!params.g1.is_zero()); + assert!(!params.g2.is_zero()); + assert!(!pk.w.is_zero()); + assert!(!sk.x.is_zero()); + } +} diff --git a/bbs/src/bbs_bn254/mod.rs b/bbs/src/bbs_bn254/mod.rs index 865c631..84dcc08 100644 --- a/bbs/src/bbs_bn254/mod.rs +++ b/bbs/src/bbs_bn254/mod.rs @@ -1,15 +1,14 @@ -pub mod structs; -pub mod keygen; pub mod blind; +pub mod keygen; pub mod signer; +pub mod structs; +pub mod verify; +pub mod utils; -pub use keygen::{keygen, keygen_with_rng}; -pub use structs::{Parameters, PrivateKey, PublicKey}; pub use blind::{ - blind, - blind_with_rng, - commitment_pok_prove, - commitment_pok_prove_with_rng, - commitment_pok_verify, + blind, blind_with_rng, commitment_pok_prove, commitment_pok_prove_with_rng, + commitment_pok_verify, }; -pub use signer::sign_no_blind; \ No newline at end of file +pub use keygen::{keygen, keygen_with_rng}; +pub use signer::sign_no_blind; +pub use structs::{Parameters, PrivateKey, PublicKey, Signature}; diff --git a/bbs/src/bbs_bn254/signer.rs b/bbs/src/bbs_bn254/signer.rs index d358a5f..6348db5 100644 --- a/bbs/src/bbs_bn254/signer.rs +++ b/bbs/src/bbs_bn254/signer.rs @@ -1,8 +1,8 @@ //! Impl signing related functions. -use ark_bn254::{G1Affine as G1, Fr as Scalar}; +use ark_bn254::{Fr as Scalar, G1Affine as G1}; use ark_ec::CurveGroup; -use ark_ff::{Field}; +use ark_ff::Field; use ark_std::{UniformRand, rand::RngCore, test_rng}; use crate::bbs_bn254::{Parameters, PrivateKey, structs::Signature}; @@ -10,7 +10,7 @@ use crate::bbs_bn254::{Parameters, PrivateKey, structs::Signature}; pub fn sign_no_blind( params: &Parameters, sk: &PrivateKey, - messages: &[Scalar] + messages: &[Scalar], ) -> Result { let mut rng = test_rng(); sign_no_blind_with_rng(params, sk, messages, &mut rng) @@ -20,7 +20,7 @@ pub fn sign_no_blind_with_rng( params: &Parameters, sk: &PrivateKey, messages: &[Scalar], - rng: &mut R + rng: &mut R, ) -> Result { // Sample random scalars e and s let e = Scalar::rand(rng); @@ -43,9 +43,5 @@ pub fn sign_no_blind_with_rng( let a = (c * tmp).into_affine() as G1; // Output signature (A, e, s) - Ok(Signature { - A: a, - e, - s - }) -} \ No newline at end of file + Ok(Signature { A: a, e, s }) +} diff --git a/bbs/src/bbs_bn254/structs.rs b/bbs/src/bbs_bn254/structs.rs index d7db247..b9afd64 100644 --- a/bbs/src/bbs_bn254/structs.rs +++ b/bbs/src/bbs_bn254/structs.rs @@ -2,7 +2,7 @@ #![allow(non_snake_case, dead_code)] -use ark_bn254::{G1Affine as G1, G2Affine as G2, Fr as Scalar}; +use ark_bn254::{Fr as Scalar, G1Affine as G1, G2Affine as G2}; /// BBS+ public parameters /// Contains the generators and message base points for the signing system @@ -94,4 +94,4 @@ pub struct SignatureProof { pub m_hats: Vec, /// Indices and values of the disclosed messages pub disclosed: Vec<(usize, Scalar)>, -} \ No newline at end of file +} diff --git a/bbs/src/bbs_bn254/utils.rs b/bbs/src/bbs_bn254/utils.rs new file mode 100644 index 0000000..7153672 --- /dev/null +++ b/bbs/src/bbs_bn254/utils.rs @@ -0,0 +1,4 @@ +//! Some useful tool funcs + + + \ No newline at end of file diff --git a/bbs/src/bbs_bn254/verify.rs b/bbs/src/bbs_bn254/verify.rs new file mode 100644 index 0000000..60f3627 --- /dev/null +++ b/bbs/src/bbs_bn254/verify.rs @@ -0,0 +1,45 @@ +//! Verification related functions here + +use ark_bn254::{Bn254, Fr as Scalar}; +use ark_ec::pairing::Pairing; + +use crate::bbs_bn254::{Parameters, PublicKey, Signature}; + + +/// Verify a signature without blind. +pub fn verify_no_blind( + params: &Parameters, + pk: &PublicKey, + messages: &[Scalar], + signature: &Signature, +) -> Result { + // Check message length + if messages.len() > params.L { + return Err("message length exceeds parameters"); + } + // Check points are on curve + if !signature.A.is_on_curve() { + return Err("signature A is not on curve"); + } + if !pk.w.is_on_curve() { + return Err("public key w is not on curve"); + } + + // lhs = e(A, w + e*g2) + // calc w + e * g2 + let w_e_g2 = pk.w + params.g2 * signature.e; + // calc e(A, w + e*g2) + let lhs = Bn254::pairing(signature.A, w_e_g2); + + // rhs = e(C, g2) + // calc C = g1 + s*H_0 + m_1*H_1 + ... + m_L*H_L + let mut c = params.g1 + params.H[0] * signature.s; + for i in 0..messages.len() { + c += params.H[i + 1] * messages[i]; + } + // calc e(C, g2) + let rhs = Bn254::pairing(c, params.g2); + + // Check if lhs == rhs + return Ok(lhs == rhs); +} diff --git a/bbs/src/lib.rs b/bbs/src/lib.rs index 6aa8903..72b3f7b 100644 --- a/bbs/src/lib.rs +++ b/bbs/src/lib.rs @@ -1,3 +1,3 @@ //! BBS signature scheme implementation in Rust. //! Using bn254 curve. -pub mod bbs_bn254; \ No newline at end of file +pub mod bbs_bn254; diff --git a/bbs/tests/test_sign.rs b/bbs/tests/test_no_blind.rs similarity index 70% rename from bbs/tests/test_sign.rs rename to bbs/tests/test_no_blind.rs index 9688925..961aa59 100644 --- a/bbs/tests/test_sign.rs +++ b/bbs/tests/test_no_blind.rs @@ -1,9 +1,8 @@ - mod tests { - use bbs::bbs_bn254::*; use ark_bn254::Fr as Scalar; + use bbs::bbs_bn254::{verify::verify_no_blind, *}; #[test] - fn test_sign() { + fn test_no_blind() { let (params, pk, sk) = keygen(5); let messages = vec![ Scalar::from(10u64), @@ -15,5 +14,8 @@ mod tests { let signature = sign_no_blind(¶ms, &sk, &messages).unwrap(); assert!(signature.A.is_on_curve()); + + let ok = verify_no_blind(¶ms, &pk, &messages, &signature).unwrap(); + assert!(ok); } -} \ No newline at end of file +} From 5c81e06de90bc199d637ef02468972fdb913bc34 Mon Sep 17 00:00:00 2001 From: klizz111 Date: Tue, 17 Mar 2026 00:27:52 +0800 Subject: [PATCH 3/3] fin blind sign/verify --- bbs/src/bbs_bn254/blind.rs | 33 ++++++++++++++++++----- bbs/src/bbs_bn254/mod.rs | 2 +- bbs/src/bbs_bn254/signer.rs | 51 ++++++++++++++++++++++++++++++++++++ bbs/src/bbs_bn254/verify.rs | 2 +- bbs/tests/test_with_blind.rs | 26 ++++++++++++++++++ 5 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 bbs/tests/test_with_blind.rs diff --git a/bbs/src/bbs_bn254/blind.rs b/bbs/src/bbs_bn254/blind.rs index c7d4062..78b4422 100644 --- a/bbs/src/bbs_bn254/blind.rs +++ b/bbs/src/bbs_bn254/blind.rs @@ -8,15 +8,16 @@ use ark_serialize::CanonicalSerialize; use ark_std::{UniformRand, rand::RngCore}; use sha2::{Digest, Sha256}; -use crate::bbs_bn254::structs::{BlindedCommitment, CommitmentProof, Parameters}; +use crate::bbs_bn254::{Signature, structs::{BlindedCommitment, CommitmentProof, Parameters}}; /// Create a blinded commitment for a list of messages. /// Returns `Err` if message length exceeds available parameters. -/// data with index at and behind `blind_index` will be blinded, should be between 1 and messages.len() - 1 +/// for example, if blind_index is 3, and the length of messages is 5 +/// the m0, m1, m2 are visual, while m3, m4 are blinded, the commitment will be C = r*H_0 + m3*H_4 + m4*H_5 pub fn blind( params: &Parameters, messages: &[Scalar], - blind_index: usize, + blind_index: &usize, ) -> Result { let mut rng = ark_std::test_rng(); blind_with_rng(params, messages, blind_index, &mut rng) @@ -26,7 +27,7 @@ pub fn blind( pub fn blind_with_rng( params: &Parameters, messages: &[Scalar], - blind_index: usize, + blind_index: &usize, rng: &mut R, ) -> Result { if messages.len() > params.L { @@ -35,7 +36,7 @@ pub fn blind_with_rng( if params.H.len() < messages.len() + 1 { return Err("parameters do not include enough message base points"); } - if blind_index == 0 || blind_index >= messages.len() { + if *blind_index > messages.len() { return Err("invalid blind index"); } @@ -44,7 +45,7 @@ pub fn blind_with_rng( let mut commitment = params.H[0] * blinding_factor; // calc m_j*H_{j+1} - for j in (blind_index - 1)..messages.len() { + for j in *blind_index..messages.len() { commitment += params.H[j + 1] * messages[j]; } @@ -54,6 +55,24 @@ pub fn blind_with_rng( }) } +pub fn unblind( + params: &Parameters, + signature: &Signature, + commitment: &BlindedCommitment, +) -> Result { + if params.H.len() < 2 { + return Err("parameters do not include enough message base points"); + } + + let unblinded_s = signature.s + commitment.blinding_factor; + return Ok(Signature { + A: signature.A, + e: signature.e, + s: unblinded_s, + }); +} + + /// Generate a proof of knowledge for the blinded commitment. pub fn commitment_pok_prove( params: &Parameters, @@ -156,7 +175,7 @@ mod tests { let messages = vec![Scalar::from(1u64), Scalar::from(2u64), Scalar::from(3u64)]; let mut rng = ark_std::test_rng(); - let commitment = blind_with_rng(¶ms, &messages, 1, &mut rng).unwrap(); + let commitment = blind_with_rng(¶ms, &messages, &1, &mut rng).unwrap(); let proof = commitment_pok_prove_with_rng(¶ms, &commitment, &messages, &mut rng).unwrap(); diff --git a/bbs/src/bbs_bn254/mod.rs b/bbs/src/bbs_bn254/mod.rs index 84dcc08..3b7d9e9 100644 --- a/bbs/src/bbs_bn254/mod.rs +++ b/bbs/src/bbs_bn254/mod.rs @@ -10,5 +10,5 @@ pub use blind::{ commitment_pok_verify, }; pub use keygen::{keygen, keygen_with_rng}; -pub use signer::sign_no_blind; +pub use signer::{sign_no_blind, sign_with_blind}; pub use structs::{Parameters, PrivateKey, PublicKey, Signature}; diff --git a/bbs/src/bbs_bn254/signer.rs b/bbs/src/bbs_bn254/signer.rs index 6348db5..04162df 100644 --- a/bbs/src/bbs_bn254/signer.rs +++ b/bbs/src/bbs_bn254/signer.rs @@ -45,3 +45,54 @@ pub fn sign_no_blind_with_rng( // Output signature (A, e, s) Ok(Signature { A: a, e, s }) } + + +pub fn sign_with_blind( + params: &Parameters, + sk: &PrivateKey, + blind_index: &usize, + commitment: &G1, + visual_messages: &[Scalar], +) -> Result { + let mut rng = test_rng(); + sign_with_blind_with_rng(params, sk, blind_index, commitment, visual_messages, &mut rng) +} + +pub fn sign_with_blind_with_rng( + params: &Parameters, + sk: &PrivateKey, + blind_index: &usize, + commitment: &G1, + visual_messages: &[Scalar], + rng: &mut R, +) -> Result { + // Check + if visual_messages.len() > params.L { + return Err("message length exceeds parameters"); + } + if *blind_index > visual_messages.len() { + return Err("invalid blind index"); + } + + // Sample random scalars e and s' + let e = Scalar::rand(rng); + let s_prime = Scalar::rand(rng); + + // Construct C' = g_1 + commitment + s'*H_0 + sum(m_i*H_i) + let mut c_p = params.g1 + commitment + params.H[0] * s_prime; + for (j, m_j) in visual_messages.iter().enumerate() { + if j >= params.L { + return Err("message length exceeds parameters"); + } + c_p += params.H[j + 1] * m_j; + } + + // calc (x + e) ^ (-1) + let mut tmp = sk.x + e; + tmp = tmp.inverse().ok_or("failed to compute inverse")?; + + // calc A = C' * (x + e) ^ (-1) + let a = (c_p * tmp).into_affine() as G1; + + return Ok(Signature { A: a, e, s: s_prime }); +} \ No newline at end of file diff --git a/bbs/src/bbs_bn254/verify.rs b/bbs/src/bbs_bn254/verify.rs index 60f3627..537d2ce 100644 --- a/bbs/src/bbs_bn254/verify.rs +++ b/bbs/src/bbs_bn254/verify.rs @@ -42,4 +42,4 @@ pub fn verify_no_blind( // Check if lhs == rhs return Ok(lhs == rhs); -} +} \ No newline at end of file diff --git a/bbs/tests/test_with_blind.rs b/bbs/tests/test_with_blind.rs new file mode 100644 index 0000000..42bb13b --- /dev/null +++ b/bbs/tests/test_with_blind.rs @@ -0,0 +1,26 @@ +mod tests { + use ark_bn254::Fr as Scalar; + use bbs::bbs_bn254::{blind::unblind, verify::verify_no_blind, *}; + #[test] + fn test_no_blind() { + let (params, pk, sk) = keygen(5); + let messages = vec![ + Scalar::from(10u64), + Scalar::from(20u64), + Scalar::from(30u64), + Scalar::from(40u64), + Scalar::from(50u64), + ]; + + let commitment = blind(¶ms, &messages, &3).unwrap(); + + // m0, m1, m2 are visual, m3, m4 are blinded + let visual_messages = messages[..3].to_vec(); + let mut signature = sign_with_blind(¶ms, &sk, &3, &commitment.commitment, &visual_messages).unwrap(); + + signature = unblind(¶ms, &signature, &commitment).unwrap(); + + let ok = verify_no_blind(¶ms, &pk, &messages, &signature).unwrap(); + assert!(ok); + } +}