diff --git a/.github/workflows/ci-detox-android.yml b/.github/workflows/ci-detox-android.yml index 068cb578..70dd2fb2 100644 --- a/.github/workflows/ci-detox-android.yml +++ b/.github/workflows/ci-detox-android.yml @@ -69,15 +69,19 @@ jobs: runs-on: ${{ matrix.runners }} strategy: matrix: - runners: [[self-hosted, macos, X64, general, runner03]] + runners: [[self-hosted, macos, general, ARM64]] concurrency: - group: ci-detox-android-${{ matrix.runners }}-${{ github.head_ref }} + group: ci-detox-android-${{ matrix.runners }}-${{ github.head_ref || github.run_id}} cancel-in-progress: true - timeout-minutes: 30 + timeout-minutes: 60 + + env: + NODE_OPTIONS: "--max-old-space-size=5120" defaults: run: working-directory: wrappers/react-native + shell: zsh -il {0} # load ~/.zshrc steps: - name: Checkout diff --git a/.github/workflows/ci-detox-ios.yml b/.github/workflows/ci-detox-ios.yml index 7b7d0939..d3dbec52 100644 --- a/.github/workflows/ci-detox-ios.yml +++ b/.github/workflows/ci-detox-ios.yml @@ -44,21 +44,23 @@ jobs: ios: name: Detox iOS E2E Testing needs: [libraries] - runs-on: ${{ matrix.runners }} - strategy: - matrix: - runners: [[self-hosted, macos, X64, general]] - concurrency: - group: ci-detox-ios-${{ matrix.runners }}-${{ github.head_ref }} - cancel-in-progress: true - timeout-minutes: 30 + runs-on: macos-latest + # strategy: + # matrix: + # runners: [[self-hosted, macos, general, detox-ios]] + # concurrency: + # group: ci-detox-ios-${{ matrix.runners }}-${{ github.head_ref || github.run_id }} + # cancel-in-progress: true + timeout-minutes: 120 defaults: run: working-directory: wrappers/react-native + shell: zsh -il {0} # load ~/.zshrc env: - DEVELOPER_DIR: /Applications/Xcode_13.2.1.app + DEVELOPER_DIR: /Applications/Xcode_14.2.app + NODE_OPTIONS: "--max-old-space-size=5120" # Do not launch packager during CI build RCT_NO_LAUNCH_PACKAGER: 'true' diff --git a/Cargo.toml b/Cargo.toml index f3ac037f..2d5d43d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ sha2 = "0.9" sha3 = "0.9" [dev-dependencies] -criterion = { version = "0.4.0", features = ["html_reports"] } +criterion = { version = "0.5.1", features = ["html_reports"] } hex = "0.4" rand_xorshift = "0.3" bbs-fixtures-generator = {version = "0.1.0", path = "tools/bbs-fixtures-generator"} diff --git a/src/common/key_pair.rs b/src/common/key_pair.rs index 0f68cb8b..67ad4401 100644 --- a/src/common/key_pair.rs +++ b/src/common/key_pair.rs @@ -100,7 +100,7 @@ macro_rules! bbs_bls_key_pair_impl { /// Convert a vector of bytes of big-endian representation of the /// secret key. - pub fn from_vec(bytes: &Vec) -> Result { + pub fn from_vec(bytes: &[u8]) -> Result { match vec_to_byte_array::<{ Self::SIZE_BYTES }>(bytes) { Ok(result) => Self::from_bytes(&result), Err(e) => Err(e), @@ -195,7 +195,7 @@ macro_rules! bbs_bls_key_pair_impl { /// Convert a vector of bytes of big-endian representation of the /// public key. - pub fn from_vec(bytes: &Vec) -> Result { + pub fn from_vec(bytes: &[u8]) -> Result { match vec_to_byte_array::<{ Self::SIZE_BYTES }>(bytes) { Ok(result) => Self::from_octets(&result), Err(e) => Err(e), diff --git a/src/common/util.rs b/src/common/util.rs index 0b44d85f..1c134deb 100644 --- a/src/common/util.rs +++ b/src/common/util.rs @@ -14,11 +14,9 @@ macro_rules! print_byte_array { pub(crate) use print_byte_array; -pub fn vec_to_byte_array( - vec: &Vec, -) -> Result<[u8; N], Error> { +pub fn vec_to_byte_array(vec: &[u8]) -> Result<[u8; N], Error> { let data_len = vec.len(); - match <[u8; N]>::try_from(vec.clone()) { + match <[u8; N]>::try_from(vec) { Ok(result) => Ok(result), Err(_) => Err(Error::Conversion { cause: format!( diff --git a/src/curves/bls12_381.rs b/src/curves/bls12_381.rs index f242738b..6c423515 100644 --- a/src/curves/bls12_381.rs +++ b/src/curves/bls12_381.rs @@ -1,4 +1,4 @@ -pub use blstrs::{Bls12 as pairing_engine, *}; +pub use blstrs::*; /// Number of bytes to store a scalar. pub(crate) const OCTET_SCALAR_LENGTH: usize = 32; diff --git a/src/error.rs b/src/error.rs index 9cdeb492..95f33723 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,5 @@ +use std::usize; + /// Error enumerates all possible errors occuring in this library. /// An error returned by the crypto component. #[derive(Clone, PartialEq, Eq, Hash)] @@ -68,6 +70,14 @@ pub enum Error { messages: usize, }, + /// Not enough random scalars during Proof initialization. + UndisclosedIndexesRandomScalarsLengthMismatch { + /// Number of random scalars. + random_scalars: usize, + /// Number of messages. + undisclosed_indexes: usize, + }, + /// The given point(from `G1` or `G2`) is an `Identity` element of /// respective subgroup. PointIsIdentity, @@ -135,6 +145,16 @@ impl core::fmt::Debug for Error { #messages: {messages}." ) } + Error::UndisclosedIndexesRandomScalarsLengthMismatch { + random_scalars, + undisclosed_indexes, + } => { + write!( + f, + "length mismatch #random_scalars: {random_scalars}, \ + #undisclosed_indexes: {undisclosed_indexes}." + ) + } Error::PointIsIdentity => { write!(f, "unexpected `Identity` element.") } diff --git a/src/schemes/bbs/api/proof.rs b/src/schemes/bbs/api/proof.rs index b56cf473..5e2b2ff1 100644 --- a/src/schemes/bbs/api/proof.rs +++ b/src/schemes/bbs/api/proof.rs @@ -136,7 +136,6 @@ where request.presentation_header.as_ref(), &generators, &messages, - Some(total_message_count), ) } diff --git a/src/schemes/bbs/core/generator.rs b/src/schemes/bbs/core/generator.rs index e28f1134..7175928b 100644 --- a/src/schemes/bbs/core/generator.rs +++ b/src/schemes/bbs/core/generator.rs @@ -21,7 +21,7 @@ pub(crate) trait Generators: Debug + Clone { /// Note - `MessageGenerators` is zero indexed, so passed `index` value /// should be in [0, `length`) range. In case of an invalid `index`, `None` /// value is returned. - fn get_message_generator(&mut self, index: usize) -> Option; + fn get_message_generator(&self, index: usize) -> Option; /// Get a `Iterator` for message generators. fn message_generators_iter(&self) -> MessageGeneratorsIter { diff --git a/src/schemes/bbs/core/generator/memory_cached_generator.rs b/src/schemes/bbs/core/generator/memory_cached_generator.rs index 6c6a7b6d..4ca2cdb4 100644 --- a/src/schemes/bbs/core/generator/memory_cached_generator.rs +++ b/src/schemes/bbs/core/generator/memory_cached_generator.rs @@ -76,7 +76,7 @@ impl Generators /// Note `MessageGenerators` is zero indexed, so passed `index` value should /// be in [0, `length`) range. In case of invalid `index`, `None` value /// is returned. - fn get_message_generator(&mut self, index: usize) -> Option { + fn get_message_generator(&self, index: usize) -> Option { if index >= self.H_list.len() { return None; } diff --git a/src/schemes/bbs/core/proof.rs b/src/schemes/bbs/core/proof.rs index 374eaa6c..456d21eb 100644 --- a/src/schemes/bbs/core/proof.rs +++ b/src/schemes/bbs/core/proof.rs @@ -4,7 +4,13 @@ use super::{ generator::Generators, key_pair::PublicKey, signature::Signature, - types::{Challenge, FiatShamirProof, Message, ProofMessage}, + types::{ + Challenge, + FiatShamirProof, + Message, + ProofInitResult, + ProofMessage, + }, utils::{compute_B, compute_challenge, compute_domain}, }; use crate::{ @@ -43,6 +49,24 @@ macro_rules! slicer { }; } +#[derive(Default)] +pub(crate) struct RandomScalars { + pub r1: Scalar, + pub r2_tilde: Scalar, + pub z_tilde: Scalar, + pub m_tilde_scalars: Vec, +} + +impl RandomScalars { + fn insert_m_tilde(&mut self, m_tilde: Scalar) { + self.m_tilde_scalars.push(m_tilde); + } + + fn m_tilde_scalars_len(&self) -> usize { + self.m_tilde_scalars.len() + } +} + /// The zero-knowledge proof-of-knowledge of a signature that is sent from /// prover to verifier. Contains the proof of 2 discrete log relations. /// The `ProofGen` procedure is specified here @@ -130,42 +154,245 @@ impl Proof { }); } - // The following steps from the `ProofGen` operation defined in https://identity.foundation/bbs-signature/draft-bbs-signatures.html#name-proofgen are implicit in this - // implementation - // signature_result = octets_to_signature(signature) - // (i1, i2,..., iR) = RevealedIndexes - // (j1, j2,..., jU) = [L] \ RevealedIndexes - // if signature_result is INVALID, return INVALID - // (A, e) = signature_result - // generators = (Q || || H_1 || ... || H_L) + // (r1, r2, r3, m~_j1, ..., m~_jU) = calculate_random_scalars(3+U) + let mut random_scalars = RandomScalars { + r1: create_random_scalar(&mut rng)?, + r2_tilde: create_random_scalar(&mut rng)?, + z_tilde: create_random_scalar(&mut rng)?, + ..Default::default() + }; - // domain - // = hash_to_scalar((PK||L||generators||Ciphersuite_ID||header), 1) - let domain = - compute_domain::<_, _, C>(PK, header, messages.len(), generators)?; + // Deserialization steps of the `CoreProofGen` operation defined in https://identity.foundation/bbs-signature/draft-irtf-cfrg-bbs-signatures.html#name-coreproofgen + // + // Deserialization: + // ...(implicit steps)... + // 9. undisclosed_indexes = range(1, L) \ disclosed_indexes + // 10. disclosed_messages = (messages[i1], ..., messages[iR]) + let message_scalars: Vec = + messages.iter().map(|m| m.get_message().0).collect(); + let mut undisclosed_indexes = Vec::new(); + let mut disclosed_messages = BTreeMap::new(); + let mut undisclosed_message_scalars = Vec::new(); + for (i, message) in messages.iter().enumerate() { + match message { + ProofMessage::Revealed(m) => { + disclosed_messages.insert(i, *m); + } + ProofMessage::Hidden(m) => { + undisclosed_indexes.push(i); + undisclosed_message_scalars.push(m.0); + + // Get the random scalars m~_j1, ..., m~_jU + random_scalars + .insert_m_tilde(create_random_scalar(&mut rng)?); + } + } + } + + // initialize proof generation + let init_result: ProofInitResult = Self::proof_init::( + PK, + signature, + generators, + &random_scalars, + header, + message_scalars, + undisclosed_indexes, + )?; + + // calculate the challenge + let c = + compute_challenge::<_, C>(&init_result, &disclosed_messages, ph)?; + + // finalize the proof + Self::proof_finalize( + c, + signature.e, + random_scalars, + init_result, + undisclosed_message_scalars, + ) + } + + /// Verify the zero-knowledge proof-of-knowledge of a signature with + /// optionally selectively disclosed messages from the original set of signed messages as defined in `ProofGen` API in BBS Signature specification . + pub fn verify( + &self, + PK: &PublicKey, + header: Option, + ph: Option, + generators: &G, + disclosed_messages: &BTreeMap, + ) -> Result + where + T: AsRef<[u8]>, + G: Generators, + C: BbsCiphersuiteParameters, + { + // if KeyValidate(PK) is INVALID, return INVALID + // `PK` should not be an identity and should belong to subgroup G2 + if PK.is_valid().unwrap_u8() == 0u8 { + return Err(Error::InvalidPublicKey); + } - // (r1, e~, r2~, r3~, z~) = hash_to_scalar(PRF(8*ceil(log2(r))), 6) - let r1 = create_random_scalar(&mut rng)?; - let r2_tilde = create_random_scalar(&mut rng)?; - let z_tilde = create_random_scalar(&mut rng)?; + // initialize the proof verification procedure + let init_res = self.proof_verify_init::( + PK, + header, + generators, + disclosed_messages, + )?; - // (m~_j1, ..., m~_jU) = hash_to_scalar(PRF(8*ceil(log2(r))), U) - // these random scalars will be generated further below during `C2` - // computation + // cv_array = (A', Abar, D, C1, C2, R, i1, ..., iR, msg_i1, ..., + // msg_iR, domain, ph) + // cv_for_hash = encode_for_hash(cv_array) + // if cv_for_hash is INVALID, return INVALID + // cv = hash_to_scalar(cv_for_hash, 1) + let cv = compute_challenge::<_, C>(&init_res, disclosed_messages, ph)?; - let msg: Vec<_> = messages.iter().map(|m| m.get_message()).collect(); + // Check the selective disclosure proof + // if c != cv, return INVALID + if self.c != cv { + return Ok(false); + } + + // This check is already done during `Proof` deserialization + // if Abar == 1, return INVALID + if self.A_bar.is_identity().unwrap_u8() == 1 { + return Err(Error::PointIsIdentity); + } + + // Check the signature proof + // if e(Abar, W) * e(Abar, -P2) != 1, return INVALID + // else return VALID + let P2 = C::p2().to_affine(); + Ok(Bls12::multi_miller_loop(&[ + (&self.A_bar.to_affine(), &G2Prepared::from(PK.0.to_affine())), + (&self.B_bar.to_affine(), &G2Prepared::from(-P2)), + ]) + .final_exponentiation() + .is_identity() + .unwrap_u8() + == 1) + } + + /// Initialize the Proof Generation operation. + pub fn proof_init( + PK: &PublicKey, + signature: &Signature, + generators: &G, + random_scalars: &RandomScalars, + header: Option, + message_scalars: Vec, + undisclosed_indexes: Vec, + ) -> Result + where + T: AsRef<[u8]>, + G: Generators, + C: BbsCiphersuiteParameters, + { + let total_no_of_messages = message_scalars.len(); + + // Check input sizes. + // Number of message generators == number of messages is checked in + // compute_domain. Checking that all the indexes are in the [0, + // length(messages)) range is done before get_message_generator + // bellow. Checking here that number of random scalars == number + // of messages + 3. + if undisclosed_indexes.len() != random_scalars.m_tilde_scalars_len() { + return Err(Error::UndisclosedIndexesRandomScalarsLengthMismatch { + random_scalars: random_scalars.m_tilde_scalars_len(), + undisclosed_indexes: undisclosed_indexes.len(), + }); + } + + // Checking that number of undisclosed messages (/indexes) <= number of + // messages + if undisclosed_indexes.len() > message_scalars.len() { + return Err(Error::BadParams { + cause: format!( + "Not disclosed messages number is invalid. Maximum \ + allowed value is {}", + total_no_of_messages + ), + }); + } + + // domain + // = hash_to_scalar((PK||L||generators||Ciphersuite_ID||header), 1) + let domain = compute_domain::<_, _, C>( + PK, + header, + message_scalars.len(), + generators, + )?; // Abar = A * r1 - let A_bar = signature.A * r1; + let A_bar = signature.A * random_scalars.r1; // B = P1 + Q * domain + H_1 * msg_1 + ... + H_L * msg_L - let B = compute_B::<_, C>(&domain, msg.as_ref(), generators)?; + let B = compute_B::<_, C>(&domain, &message_scalars, generators)?; // Bbar = B * r1 - Abar * e - let B_bar = G1Projective::multi_exp(&[B, A_bar], &[r1, -signature.e]); + let B_bar = G1Projective::multi_exp( + &[B, A_bar], + &[random_scalars.r1, -signature.e], + ); + + // T = Abar * r2~ + Bbar * z~ + H_j1 * m~_j1 + ... + H_jU * m~_jU + let mut H_Points = Vec::new(); + for idx in undisclosed_indexes { + if idx >= total_no_of_messages { + return Err(Error::BadParams { + cause: format!( + "Undisclosed message index is invalid. Maximum \ + allowed value is {}", + total_no_of_messages - 1 + ), + }); + } + // unwrap is safe here since we check the idx value above. + let generator = generators.get_message_generator(idx).unwrap(); + H_Points.push(generator); + } + + let T = G1Projective::multi_exp( + &[[A_bar, B_bar].to_vec(), H_Points].concat(), + &[ + [random_scalars.r2_tilde, random_scalars.z_tilde].to_vec(), + random_scalars.m_tilde_scalars.to_vec(), + ] + .concat(), + ); + + Ok(ProofInitResult { + A_bar, + B_bar, + T, + domain, + }) + } + + /// Finalize the Proof Generation operation. + pub fn proof_finalize( + challenge: Challenge, + e_value: Scalar, + random_scalars: RandomScalars, + init_res: ProofInitResult, + undisclosed_message_scalars: Vec, + ) -> Result { + // Check that number of random scalars == number of messages + 3 + if undisclosed_message_scalars.len() + != random_scalars.m_tilde_scalars_len() + { + return Err(Error::UndisclosedIndexesRandomScalarsLengthMismatch { + random_scalars: random_scalars.m_tilde_scalars_len(), + undisclosed_indexes: undisclosed_message_scalars.len(), + }); + } // r2 = -r1 ^ -1 mod r - let r2 = r1.invert(); + let r2 = random_scalars.r1.invert(); if r2.is_none().unwrap_u8() == 1u8 { return Err(Error::CryptoOps { @@ -174,89 +401,53 @@ impl Proof { }; let r2 = -r2.unwrap(); - // C = Abar * r2~ + Bbar * z~ + H_j1 * m~_j1 + ... + H_jU * m~_jU - let mut H_points = Vec::new(); - let mut m_tilde_scalars = Vec::new(); - let mut hidden_messages = Vec::new(); - let mut disclosed_messages = BTreeMap::new(); - for (i, generator) in generators.message_generators_iter().enumerate() { - match messages[i] { - ProofMessage::Revealed(m) => { - disclosed_messages.insert(i, m); - } - ProofMessage::Hidden(m) => { - H_points.push(generator); - m_tilde_scalars.push(create_random_scalar(&mut rng)?); - hidden_messages.push(m.0); - } - } - } - - let C = G1Projective::multi_exp( - &[[A_bar, B_bar].to_vec(), H_points].concat(), - &[[r2_tilde, z_tilde].to_vec(), m_tilde_scalars.clone()].concat(), - ); - - // c_array = (A_bar, B_bar, C, R, i1, ..., iR, msg_i1, ..., msg_iR, - // domain, ph) - // c_octs = serialize(c_array) - // if c_octs is INVALID, return INVALID - // c = hash_to_scalar(c_octs, 1) - let c = compute_challenge::<_, C>( - &A_bar, - &B_bar, - &C, - &disclosed_messages, - &domain, - ph, - )?; - // r2^ = r2~ + c * r2 - let r2_hat = FiatShamirProof(r2_tilde + c.0 * signature.e * r2); + let r2_hat = FiatShamirProof( + random_scalars.r2_tilde + challenge.0 * e_value * r2, + ); // z^ = z~ + c * e * r2 - let z_hat = FiatShamirProof(z_tilde + c.0 * r2); + let z_hat = FiatShamirProof(random_scalars.z_tilde + challenge.0 * r2); // for j in (j1, j2,..., jU): m^_j = m~_j + c * msg_j - let m_hat_list = m_tilde_scalars + let m_hat_list = random_scalars + .m_tilde_scalars .iter() - .zip(hidden_messages.iter()) + .zip(undisclosed_message_scalars.iter()) .map(|(m_tilde, msg)| { - let m_hat = *m_tilde + c.0 * (*msg); + let m_hat = *m_tilde + challenge.0 * (*msg); FiatShamirProof(m_hat) }) .collect::>(); Ok(Proof { - A_bar, - B_bar, + A_bar: init_res.A_bar, + B_bar: init_res.B_bar, r2_hat, z_hat, m_hat_list, - c, + c: challenge, }) } - /// Verify the zero-knowledge proof-of-knowledge of a signature with - /// optionally selectively disclosed messages from the original set of signed messages as defined in `ProofGen` API in BBS Signature specification . - pub fn verify( + /// Initialize the Proof Verification operation. + pub fn proof_verify_init( &self, PK: &PublicKey, header: Option, - ph: Option, generators: &G, disclosed_messages: &BTreeMap, - total_no_of_messages: Option, - ) -> Result + ) -> Result where T: AsRef<[u8]>, G: Generators, C: BbsCiphersuiteParameters, { - // If total number of messages is not provided, it defaults to - // disclosed_messages number + m_hat number - let total_no_of_messages = total_no_of_messages - .unwrap_or(self.m_hat_list.len() + disclosed_messages.len()); + // The total number of messages equals disclosed_messages number + m_hat + // number Note that this operation is necessarily repeated at + // the proof verify api. + let total_no_of_messages = + self.m_hat_list.len() + disclosed_messages.len(); // Input parameter checks // Error out if there is no `header` and not any `ProofMessage` @@ -265,6 +456,7 @@ impl Proof { cause: "nothing to verify".to_owned(), }); } + // Check if input proof data commitments matches no. of hidden messages if total_no_of_messages != generators.message_generators_length() { return Err(Error::BadParams { @@ -278,6 +470,7 @@ impl Proof { ), }); } + if disclosed_messages .keys() .any(|r| *r >= total_no_of_messages) @@ -290,20 +483,6 @@ impl Proof { ), }); } - // if KeyValidate(PK) is INVALID, return INVALID - // `PK` should not be an identity and should belong to subgroup G2 - if PK.is_valid().unwrap_u8() == 0u8 { - return Err(Error::InvalidPublicKey); - } - - // The following steps from the `ProofVerify` operation defined in https://identity.foundation/bbs-signature/draft-bbs-signatures.html#name-proofverify are implicit in this - // implementation - // (i1, i2, ..., iR) = RevealedIndexes - // (j1, j2, ..., jU) = [L]\RevealedIndexes - // proof_value = octets_to_proof(proof) - // if proof_value is INVALID, return INVALID - // (A', Abar, D, c, e^, r2^, z^, (m^_j1,...,m^_jU)) = proof_value - // generators = (Q || H_1 || ... || H_L) // domain // = hash_to_scalar((PK||L||generators||Ciphersuite_ID||header), 1) @@ -314,27 +493,27 @@ impl Proof { generators, )?; - // T = P1 + Q * domain + H_i1 * msg_i1 + ... H_iR * msg_iR - let T_len = 1 + 1 + disclosed_messages.len(); - let mut T_points = Vec::with_capacity(T_len); - let mut T_scalars = Vec::with_capacity(T_len); + // D = P1 + Q * domain + H_i1 * msg_i1 + ... H_iR * msg_iR + let D_len = 1 + 1 + disclosed_messages.len(); + let mut D_points = Vec::with_capacity(D_len); + let mut D_scalars = Vec::with_capacity(D_len); let P1 = C::p1()?; // P1 - T_points.push(P1); - T_scalars.push(Scalar::one()); + D_points.push(P1); + D_scalars.push(Scalar::one()); // Q * domain - T_points.push(generators.Q()); - T_scalars.push(domain); + D_points.push(generators.Q()); + D_scalars.push(domain); let mut C_points_temp = Vec::with_capacity(self.m_hat_list.len()); let mut C_scalars_temp = Vec::with_capacity(self.m_hat_list.len()); let mut j = 0; for (i, generator) in generators.message_generators_iter().enumerate() { if disclosed_messages.contains_key(&i) { - T_points.push(generator); + D_points.push(generator); // unwrap() is safe here since we already have checked for // existence of key - T_scalars.push(disclosed_messages.get(&i).unwrap().0); + D_scalars.push(disclosed_messages.get(&i).unwrap().0); } else { C_points_temp.push(generator); C_scalars_temp.push(self.m_hat_list[j].0); @@ -342,67 +521,35 @@ impl Proof { } } - // Calculate T = H_i1 * msg_i1 + ... H_iR * msg_iR - let T = G1Projective::multi_exp(&T_points, &T_scalars); + // Calculate D = H_i1 * msg_i1 + ... H_iR * msg_iR + let D = G1Projective::multi_exp(&D_points, &D_scalars); - // C = T * c + Abar * r2^ + Bbar * z^ + + // T = D * c + Abar * r2^ + Bbar * z^ + // + H_j1 * m^_j1 + ... + H_jU * m^_jU - let C_len = 1 + 1 + 1 + self.m_hat_list.len(); - let mut C_points = Vec::with_capacity(C_len); - let mut C_scalars = Vec::with_capacity(C_len); + let T_len = 1 + 1 + 1 + self.m_hat_list.len(); + let mut T_points = Vec::with_capacity(T_len); + let mut T_scalars = Vec::with_capacity(T_len); // T * (-c) - C_points.push(T); - C_scalars.push(self.c.0); + T_points.push(D); + T_scalars.push(self.c.0); // Abar * r2^ - C_points.push(self.A_bar); - C_scalars.push(self.r2_hat.0); + T_points.push(self.A_bar); + T_scalars.push(self.r2_hat.0); // Bbar * z^ - C_points.push(self.B_bar); - C_scalars.push(self.z_hat.0); + T_points.push(self.B_bar); + T_scalars.push(self.z_hat.0); // H_j1 * m^_j1 + ... + H_jU * m^_jU - C_points.append(&mut C_points_temp); - C_scalars.append(&mut C_scalars_temp); - - let C = G1Projective::multi_exp(&C_points, &C_scalars); - - // cv_array = (A', Abar, D, C1, C2, R, i1, ..., iR, msg_i1, ..., - // msg_iR, domain, ph) - // cv_for_hash = encode_for_hash(cv_array) - // if cv_for_hash is INVALID, return INVALID - // cv = hash_to_scalar(cv_for_hash, 1) - let cv = compute_challenge::<_, C>( - &self.A_bar, - &self.B_bar, - &C, - disclosed_messages, - &domain, - ph, - )?; - - // Check the selective disclosure proof - // if c != cv, return INVALID - if self.c != cv { - return Ok(false); - } + T_points.append(&mut C_points_temp); + T_scalars.append(&mut C_scalars_temp); - // This check is already done during `Proof` deserialization - // if Abar == 1, return INVALID - if self.A_bar.is_identity().unwrap_u8() == 1 { - return Err(Error::PointIsIdentity); - } + let T = G1Projective::multi_exp(&T_points, &T_scalars); - // Check the signature proof - // if e(Abar, W) * e(Abar, -P2) != 1, return INVALID - // else return VALID - let P2 = C::p2().to_affine(); - Ok(Bls12::multi_miller_loop(&[ - (&self.A_bar.to_affine(), &G2Prepared::from(PK.0.to_affine())), - (&self.B_bar.to_affine(), &G2Prepared::from(-P2)), - ]) - .final_exponentiation() - .is_identity() - .unwrap_u8() - == 1) + Ok(ProofInitResult { + A_bar: self.A_bar, + B_bar: self.B_bar, + T, + domain, + }) } /// Return the size of proof in bytes for `num_undisclosed_messages`. diff --git a/src/schemes/bbs/core/signature.rs b/src/schemes/bbs/core/signature.rs index 609c53b4..0dc6c795 100644 --- a/src/schemes/bbs/core/signature.rs +++ b/src/schemes/bbs/core/signature.rs @@ -190,7 +190,9 @@ impl Signature { let e = C::hash_to_e(&data_to_hash)?; // B = P1 + Q * domain + H_1 * msg_1 + ... + H_L * msg_L - let B = compute_B::<_, C>(&domain, messages, generators)?; + let message_scalars: Vec = + messages.iter().map(|m| m.0).collect(); + let B = compute_B::<_, C>(&domain, &message_scalars, generators)?; let exp = (e + SK.as_scalar()).invert(); let exp = if exp.is_some().unwrap_u8() == 1u8 { exp.unwrap() @@ -336,7 +338,9 @@ impl Signature { compute_domain::<_, _, C>(PK, header, messages.len(), generators)?; // B = P1 + Q * domain + H_1 * msg_1 + ... + H_L * msg_L - let B = compute_B::<_, C>(&domain, messages, generators)?; + let message_scalars: Vec = + messages.iter().map(|m| m.0).collect(); + let B = compute_B::<_, C>(&domain, &message_scalars, generators)?; let P2 = C::p2(); // C1 = (A, W + P2 * e) diff --git a/src/schemes/bbs/core/types.rs b/src/schemes/bbs/core/types.rs index fe761cbb..da6e2d20 100644 --- a/src/schemes/bbs/core/types.rs +++ b/src/schemes/bbs/core/types.rs @@ -6,6 +6,7 @@ use crate::{ }, error::Error, }; +use blstrs::G1Projective; use serde::{Deserialize, Serialize}; use subtle::CtOption; @@ -62,3 +63,13 @@ impl ProofMessage { } } } + +/// Result of proof generation and +/// verification initialization. +#[allow(non_snake_case)] +pub(crate) struct ProofInitResult { + pub A_bar: G1Projective, + pub B_bar: G1Projective, + pub T: G1Projective, + pub domain: Scalar, +} diff --git a/src/schemes/bbs/core/utils.rs b/src/schemes/bbs/core/utils.rs index 7d0215dd..e5a0256b 100644 --- a/src/schemes/bbs/core/utils.rs +++ b/src/schemes/bbs/core/utils.rs @@ -3,7 +3,7 @@ use super::{ generator::Generators, key_pair::PublicKey, - types::{Challenge, Message}, + types::{Challenge, Message, ProofInitResult}, }; use crate::{ bbs::ciphersuites::BbsCiphersuiteParameters, @@ -78,7 +78,7 @@ where /// B = P1 + Q * domain + H_1 * msg_1 + ... + H_L * msg_L pub(crate) fn compute_B( domain: &Scalar, - messages: &[Message], + messages: &[Scalar], generators: &G, ) -> Result where @@ -96,11 +96,7 @@ where let mut points: Vec<_> = vec![C::p1()?, generators.Q()]; points.extend(generators.message_generators_iter()); - let scalars: Vec<_> = [Scalar::one(), *domain] - .iter() - .copied() - .chain(messages.iter().map(|c| c.0)) - .collect(); + let scalars = [&[Scalar::one(), *domain], messages].concat(); Ok(G1Projective::multi_exp(&points, &scalars)) } @@ -108,11 +104,8 @@ where /// Compute Fiat Shamir heuristic challenge. #[allow(clippy::too_many_arguments)] pub(crate) fn compute_challenge( - A_bar: &G1Projective, - B_bar: &G1Projective, - C: &G1Projective, + proof_init_res: &ProofInitResult, disclosed_messages: &BTreeMap, - domain: &Scalar, ph: Option, ) -> Result where @@ -124,9 +117,9 @@ where // c_octs = serialize(c_array) // if c_octs is INVALID, return INVALID let mut data_to_hash = vec![]; - data_to_hash.extend(point_to_octets_g1(A_bar).as_ref()); - data_to_hash.extend(point_to_octets_g1(B_bar).as_ref()); - data_to_hash.extend(point_to_octets_g1(C)); + data_to_hash.extend(point_to_octets_g1(&proof_init_res.A_bar).as_ref()); + data_to_hash.extend(point_to_octets_g1(&proof_init_res.B_bar).as_ref()); + data_to_hash.extend(point_to_octets_g1(&proof_init_res.T)); data_to_hash.extend(i2osp( disclosed_messages.len() as u64, @@ -139,7 +132,7 @@ where for &msg in disclosed_messages.values() { data_to_hash.extend(msg.to_bytes()); } - data_to_hash.extend(domain.to_bytes_be()); + data_to_hash.extend(proof_init_res.domain.to_bytes_be()); let _ph_bytes = ph.as_ref().map_or(&[] as &[u8], |v| v.as_ref()); data_to_hash.extend(i2osp_with_data( diff --git a/src/schemes/bbs_bound/api/proof.rs b/src/schemes/bbs_bound/api/proof.rs index 04bd3209..21b7567c 100644 --- a/src/schemes/bbs_bound/api/proof.rs +++ b/src/schemes/bbs_bound/api/proof.rs @@ -125,6 +125,5 @@ where request.presentation_header.as_ref(), &generators, &messages, - Some(total_message_count), ) } diff --git a/src/tests/bbs/generators.rs b/src/tests/bbs/generators.rs index 8400d216..1abe49b4 100644 --- a/src/tests/bbs/generators.rs +++ b/src/tests/bbs/generators.rs @@ -18,13 +18,13 @@ fn creation_nominal() { #[test] fn equality() { const GENERATORS_COUNT: usize = 1000; - let mut generators_1 = MemoryCachedGenerators::< + let generators_1 = MemoryCachedGenerators::< Bls12381Shake256CipherSuiteParameter, >::new(GENERATORS_COUNT, None) .expect("generators creation failed"); assert_eq!(generators_1.message_generators_length(), GENERATORS_COUNT); - let mut generators_2 = MemoryCachedGenerators::< + let generators_2 = MemoryCachedGenerators::< Bls12381Shake256CipherSuiteParameter, >::new(GENERATORS_COUNT, None) .expect("generators creation failed"); @@ -41,7 +41,7 @@ fn equality() { #[test] fn get_point_out_of_bound_index() { // Create 32 message generators - let mut generators = MemoryCachedGenerators::< + let generators = MemoryCachedGenerators::< Bls12381Shake256CipherSuiteParameter, >::new(32, None) .expect("generators creation failed"); diff --git a/src/tests/bbs/proof.rs b/src/tests/bbs/proof.rs index 515c9a00..92cfbbe2 100644 --- a/src/tests/bbs/proof.rs +++ b/src/tests/bbs/proof.rs @@ -198,8 +198,7 @@ fn gen_verify_serde_nominal() { header, ph, &mut generators, - &revealed_messages, - None + &revealed_messages ) .expect("proof verification failed")); @@ -217,8 +216,7 @@ fn gen_verify_serde_nominal() { header, ph, &mut generators, - &revealed_messages, - None + &revealed_messages ) .expect("roundtrip deserialized proof verification failed")); } @@ -307,8 +305,7 @@ fn gen_verify_different_key_pairs() { header, ph, &mut generators, - &revealed_msgs, - None + &revealed_msgs ) .expect("proof verification failed")); proof_msgs[j] = ProofMessage::Revealed(messages[j]); @@ -393,8 +390,7 @@ fn no_presentation_header_proof() { Some(TEST_HEADER), None, &mut generators, - &revealed_messages, - None + &revealed_messages ) .unwrap()); @@ -426,8 +422,7 @@ fn no_presentation_header_proof() { None::<&[u8]>, None, &mut generators, - &revealed_messages, - None + &revealed_messages ) .unwrap()); @@ -520,8 +515,7 @@ fn proof_gen_verify_valid_cases() { header, ph, &mut generators, - &revealed_messages, - None + &revealed_messages ) .unwrap_or_else(|_| panic!( "proof verification failed - {failure_debug_message}" @@ -550,8 +544,7 @@ fn proof_gen_verify_valid_cases() { header, ph, &mut generators, - &revealed_messages, - None + &revealed_messages ) .unwrap_or_else(|_| panic!( "proof verification failed - {failure_debug_message}, \ @@ -620,8 +613,7 @@ fn proof_gen_verify_all_revealed_shuffled_indices() { header, ph, &mut generators, - &revealed_messages_same_but_shuffled_indices, - None + &revealed_messages_same_but_shuffled_indices ) .expect("proof-verification should not fail")); } @@ -668,8 +660,7 @@ fn proof_gen_with_invalid_public_key() { header, ph, &mut generators, - &revealed_messages, - None + &revealed_messages ) .unwrap_or_else(|_| panic!("proof verification failed "))); @@ -681,8 +672,7 @@ fn proof_gen_with_invalid_public_key() { header, ph, &mut generators, - &revealed_messages, - None + &revealed_messages ), Err(Error::InvalidPublicKey) ); @@ -736,8 +726,7 @@ fn proof_verify_invalid_parameters() { header, ph, &mut generators, - &revealed_messages, - None + &revealed_messages ), Err(error), "proof-verification should return error - {}", @@ -771,8 +760,7 @@ fn verify_proof_helper( header, ph, &mut generators, - &revealed_messages, - None + &revealed_messages ) .unwrap_or_else(|_| panic!( "proof-verification should not return error - {}", diff --git a/src/tests/bbs/test_data/proof.rs b/src/tests/bbs/test_data/proof.rs index 86d8b908..fa2928d1 100644 --- a/src/tests/bbs/test_data/proof.rs +++ b/src/tests/bbs/test_data/proof.rs @@ -950,8 +950,7 @@ pub(crate) fn test_data_verify_tampered_proof() -> [( header, ph, &mut generators, - &revealed_messages, - None + &revealed_messages ) .expect("proof verification failed"), true @@ -1149,8 +1148,7 @@ pub(crate) fn test_data_verify_tampered_parameters() -> [( header, ph, &mut generators, - &no_revealed_messages, - None + &no_revealed_messages ) .expect("proof verification failed"), true @@ -1188,8 +1186,7 @@ pub(crate) fn test_data_verify_tampered_parameters() -> [( None, ph, &mut generators, - &revealed_messages, - None + &revealed_messages ) .expect("proof verification failed"), true @@ -1217,8 +1214,7 @@ pub(crate) fn test_data_verify_tampered_parameters() -> [( header, None, &mut generators, - &revealed_messages, - None + &revealed_messages ) .expect("proof verification failed"), true @@ -1247,8 +1243,7 @@ pub(crate) fn test_data_verify_tampered_parameters() -> [( None, None, &mut generators, - &revealed_messages, - None + &revealed_messages ) .expect("proof verification failed"), true @@ -1297,8 +1292,7 @@ pub(crate) fn test_data_verify_tampered_parameters() -> [( header, ph, &mut generators, - &all_revealed_messages, - None + &all_revealed_messages ) .expect("proof verification failed"), true diff --git a/tools/bbs-fixtures-generator/Cargo.toml b/tools/bbs-fixtures-generator/Cargo.toml index 657f127e..ddb4e6ce 100644 --- a/tools/bbs-fixtures-generator/Cargo.toml +++ b/tools/bbs-fixtures-generator/Cargo.toml @@ -14,7 +14,7 @@ path = "../../" features = ["__private_bbs_fixtures_generator_api"] [dependencies] -clap = { version = "3.2.12", features = ["derive"] } +clap = { version = "4", features = ["derive"] } serde = "1.0.139" serde_derive = "1.0.139" serde_json = "1.0.82" diff --git a/tools/bbs-fixtures-generator/src/main.rs b/tools/bbs-fixtures-generator/src/main.rs index 6ccb06d6..131e7f22 100644 --- a/tools/bbs-fixtures-generator/src/main.rs +++ b/tools/bbs-fixtures-generator/src/main.rs @@ -4,10 +4,10 @@ use clap::Parser; #[derive(Parser)] struct Cli { // The path to the file to read the test assets - #[clap(short = 'i', parse(from_os_str), value_hint = clap::ValueHint::DirPath)] + #[clap(short = 'i', value_parser = clap::value_parser!(std::path::PathBuf), value_hint = clap::ValueHint::DirPath)] test_asset_file: std::path::PathBuf, // The path to the directory to write the fixture files - #[clap(short = 'o', parse(from_os_str), value_hint = clap::ValueHint::DirPath)] + #[clap(short = 'o', value_parser = clap::value_parser!(std::path::PathBuf), value_hint = clap::ValueHint::DirPath)] fixture_output_dir: std::path::PathBuf, }