From 1214c6bc160934a74e99828e04cddb3576003360 Mon Sep 17 00:00:00 2001 From: Vasilis Kalos Date: Thu, 14 Sep 2023 20:19:16 +0300 Subject: [PATCH 1/3] feat: add pseudonyms --- src/common/interface.rs | 2 + src/curves/bls12_381.rs | 2 +- src/schemes.rs | 3 + src/schemes/bbs/api/utils.rs | 2 +- src/schemes/bbs/core/proof.rs | 32 +- src/schemes/bbs/core/types.rs | 8 + src/schemes/bbs/core/utils.rs | 10 +- src/schemes/pseudonym.rs | 6 + src/schemes/pseudonym/api.rs | 5 + src/schemes/pseudonym/api/dtos.rs | 150 +++++++++ src/schemes/pseudonym/api/proof.rs | 111 +++++++ src/schemes/pseudonym/api/pseudonym.rs | 24 ++ src/schemes/pseudonym/api/signature.rs | 67 ++++ src/schemes/pseudonym/ciphersuites.rs | 2 + .../ciphersuites/bls12_381_g1_sha_256.rs | 106 ++++++ src/schemes/pseudonym/core.rs | 2 + src/schemes/pseudonym/core/proof.rs | 303 ++++++++++++++++++ src/schemes/pseudonym/core/pseudonym.rs | 85 +++++ src/tests/bbs/mod.rs | 13 +- src/tests/bbs/proof.rs | 2 +- src/tests/mod.rs | 1 + src/tests/pseudonym/mod.rs | 1 + src/tests/pseudonym/proof.rs | 134 ++++++++ tests/nym_api.rs | 215 +++++++++++++ 24 files changed, 1273 insertions(+), 13 deletions(-) create mode 100644 src/schemes/pseudonym.rs create mode 100644 src/schemes/pseudonym/api.rs create mode 100644 src/schemes/pseudonym/api/dtos.rs create mode 100644 src/schemes/pseudonym/api/proof.rs create mode 100644 src/schemes/pseudonym/api/pseudonym.rs create mode 100644 src/schemes/pseudonym/api/signature.rs create mode 100644 src/schemes/pseudonym/ciphersuites.rs create mode 100644 src/schemes/pseudonym/ciphersuites/bls12_381_g1_sha_256.rs create mode 100644 src/schemes/pseudonym/core.rs create mode 100644 src/schemes/pseudonym/core/proof.rs create mode 100644 src/schemes/pseudonym/core/pseudonym.rs create mode 100644 src/tests/pseudonym/mod.rs create mode 100644 src/tests/pseudonym/proof.rs create mode 100644 tests/nym_api.rs diff --git a/src/common/interface.rs b/src/common/interface.rs index 7c52edab..4f057252 100644 --- a/src/common/interface.rs +++ b/src/common/interface.rs @@ -6,12 +6,14 @@ pub(crate) trait InterfaceParameter: Debug + Clone { pub(crate) enum InterfaceId { BbsH2gHm2s, + BbsH2gHm2sNym, } impl InterfaceId { pub(crate) fn as_octets(&self) -> &[u8] { match &self { InterfaceId::BbsH2gHm2s => b"H2G_HM2S_", + InterfaceId::BbsH2gHm2sNym => b"H2G_HM2S_PSEUDONYM_", } } } diff --git a/src/curves/bls12_381.rs b/src/curves/bls12_381.rs index 6c423515..2c155589 100644 --- a/src/curves/bls12_381.rs +++ b/src/curves/bls12_381.rs @@ -4,7 +4,7 @@ pub use blstrs::*; pub(crate) const OCTET_SCALAR_LENGTH: usize = 32; /// Number of bytes to store an element of G1 in affine and compressed form. -pub(crate) const OCTET_POINT_G1_LENGTH: usize = 48; +pub const OCTET_POINT_G1_LENGTH: usize = 48; /// Number of bytes to store an element of G2 in affine and compressed form. pub(crate) const OCTET_POINT_G2_LENGTH: usize = 96; diff --git a/src/schemes.rs b/src/schemes.rs index 9ca27ee8..d97a1d24 100644 --- a/src/schemes.rs +++ b/src/schemes.rs @@ -6,3 +6,6 @@ pub mod bbs_bound; /// The BLS signature scheme pub mod bls; + +/// The BBS signature scheme using per verifier linkable pseudonyms. +pub mod pseudonym; diff --git a/src/schemes/bbs/api/utils.rs b/src/schemes/bbs/api/utils.rs index addf2297..5f5c0913 100644 --- a/src/schemes/bbs/api/utils.rs +++ b/src/schemes/bbs/api/utils.rs @@ -47,7 +47,7 @@ where } /// Digests a set of supplied proof messages -pub(super) fn digest_proof_messages( +pub(crate) fn digest_proof_messages( messages: Option<&[BbsProofGenRevealMessageRequest]>, ) -> Result<(Vec, Vec), Error> where diff --git a/src/schemes/bbs/core/proof.rs b/src/schemes/bbs/core/proof.rs index 58f3e08b..df4cdaed 100644 --- a/src/schemes/bbs/core/proof.rs +++ b/src/schemes/bbs/core/proof.rs @@ -61,7 +61,7 @@ pub(crate) struct RandomScalars { } impl RandomScalars { - fn insert_m_tilde(&mut self, m_tilde: Scalar) { + pub fn insert_m_tilde(&mut self, m_tilde: Scalar) { self.m_tilde_scalars.push(m_tilde); } @@ -206,8 +206,12 @@ impl Proof { )?; // calculate the challenge - let c = - compute_challenge::<_, I>(&init_result, &disclosed_messages, ph)?; + let c = compute_challenge::<_, I>( + &init_result, + &disclosed_messages, + ph, + None, + )?; // finalize the proof Self::proof_finalize( @@ -253,7 +257,29 @@ impl Proof { // cv_for_hash = encode_for_hash(cv_array) // if cv_for_hash is INVALID, return INVALID // cv = hash_to_scalar(cv_for_hash, 1) +<<<<<<< HEAD + let cv = compute_challenge::<_, I>(&init_res, disclosed_messages, ph)?; +======= +<<<<<<< HEAD + let cv = compute_challenge::<_, I>( + &init_res, + disclosed_messages, + ph, + )?; +======= +<<<<<<< HEAD let cv = compute_challenge::<_, I>(&init_res, disclosed_messages, ph)?; +======= + let cv = compute_challenge::<_, C>( + &init_res, + disclosed_messages, + ph, + api_id, + None, + )?; +>>>>>>> 8a6d8d2 (feat: add pseudonyms) +>>>>>>> bd611bc (feat: add pseudonyms) +>>>>>>> d8735e3 (feat: add pseudonyms) // Check the selective disclosure proof // if c != cv, return INVALID diff --git a/src/schemes/bbs/core/types.rs b/src/schemes/bbs/core/types.rs index f3174c7a..3322ae15 100644 --- a/src/schemes/bbs/core/types.rs +++ b/src/schemes/bbs/core/types.rs @@ -73,3 +73,11 @@ pub(crate) struct ProofInitResult { pub T: G1Projective, pub domain: Scalar, } + +/// Result of commit correctness proof +/// generation initialization. +pub(crate) struct CommitProofInitResult { + pub commit: G1Projective, + pub commit_base: G1Projective, + pub blind_commit: G1Projective, +} diff --git a/src/schemes/bbs/core/utils.rs b/src/schemes/bbs/core/utils.rs index 9f7a5e65..25fcf4bb 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, ProofInitResult}, + types::{Challenge, CommitProofInitResult, Message, ProofInitResult}, }; use crate::{ bbs::{ @@ -125,6 +125,7 @@ pub(crate) fn compute_challenge( proof_init_res: &ProofInitResult, disclosed_messages: &BTreeMap, ph: Option, + commit_init_res: Option, ) -> Result where T: AsRef<[u8]>, @@ -139,6 +140,13 @@ where 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)); + // Add the commit proof init result elements if supplied + if let Some(commit_init) = commit_init_res { + data_to_hash.extend(point_to_octets_g1(&commit_init.commit)); + data_to_hash.extend(point_to_octets_g1(&commit_init.commit_base)); + data_to_hash.extend(point_to_octets_g1(&commit_init.blind_commit)); + }; + data_to_hash.extend(i2osp( disclosed_messages.len() as u64, NON_NEGATIVE_INTEGER_ENCODING_LENGTH, diff --git a/src/schemes/pseudonym.rs b/src/schemes/pseudonym.rs new file mode 100644 index 00000000..5de68201 --- /dev/null +++ b/src/schemes/pseudonym.rs @@ -0,0 +1,6 @@ +/// Interface for using the BBS operations with a pseudonym. +pub mod api; +/// Ciphersuite abstraction over the defined api. Each ciphersuite includes +/// concrete instantiations of the api operations. +pub mod ciphersuites; +pub(crate) mod core; diff --git a/src/schemes/pseudonym/api.rs b/src/schemes/pseudonym/api.rs new file mode 100644 index 00000000..94ff0580 --- /dev/null +++ b/src/schemes/pseudonym/api.rs @@ -0,0 +1,5 @@ +/// Types for Signature and Proof generation and verification requests. +pub mod dtos; +pub(crate) mod proof; +pub(crate) mod pseudonym; +pub(crate) mod signature; diff --git a/src/schemes/pseudonym/api/dtos.rs b/src/schemes/pseudonym/api/dtos.rs new file mode 100644 index 00000000..3f4bb574 --- /dev/null +++ b/src/schemes/pseudonym/api/dtos.rs @@ -0,0 +1,150 @@ +use crate::bbs::{ + api::dtos::BbsProofGenRevealMessageRequest, + ciphersuites::bls12_381::{ + BBS_BLS12381G1_PUBLIC_KEY_LENGTH, + BBS_BLS12381G1_SECRET_KEY_LENGTH, + BBS_BLS12381G1_SIGNATURE_LENGTH, + }, +}; + +use crate::curves::bls12_381::OCTET_POINT_G1_LENGTH; + +/// Sign request for a BBS signature with a Pseudonym. +#[derive(Clone, Debug)] +pub struct BbsSignRequest<'a, T: AsRef<[u8]> + Default> { + /// Secret key + pub secret_key: &'a [u8; BBS_BLS12381G1_SECRET_KEY_LENGTH], + /// Public key + pub public_key: &'a [u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], + /// Prover unique identifier + pub pid: T, + /// Header containing context and application specific information + pub header: Option, + /// Vector of messages to sign + pub messages: Option<&'a [T]>, +} + +impl<'a, T: AsRef<[u8]> + Default> Default for BbsSignRequest<'a, T> { + fn default() -> Self { + Self { + secret_key: &[0u8; BBS_BLS12381G1_SECRET_KEY_LENGTH], + public_key: &[0u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], + pid: Default::default(), + header: Default::default(), + messages: Default::default(), + } + } +} + +/// Verify request for a BBS signature. +#[derive(Clone, Debug)] +pub struct BbsVerifyRequest<'a, T: AsRef<[u8]> + Default> { + /// Public key + pub public_key: &'a [u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], + /// Prover unique identifier + pub pid: T, + /// Header containing context and application specific information + pub header: Option, + /// Vector of messages to verify against a signature + pub messages: Option<&'a [T]>, + /// Signature to verify + pub signature: &'a [u8; BBS_BLS12381G1_SIGNATURE_LENGTH], +} + +impl<'a, T: AsRef<[u8]> + Default> Default for BbsVerifyRequest<'a, T> { + fn default() -> Self { + Self { + public_key: &[0u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], + pid: Default::default(), + header: Default::default(), + messages: Default::default(), + signature: &[0u8; BBS_BLS12381G1_SIGNATURE_LENGTH], + } + } +} + +/// Derive proof request for computing a signature proof of knowledge for a +/// supplied BBS signature. +#[derive(Clone, Debug)] +pub struct BbsProofGenRequest<'a, T: AsRef<[u8]> + Default> { + /// Public key associated to the BBS signature + pub public_key: &'a [u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], + /// The Prover's unique identifier + pub pid: T, + /// The Verifier's unique Identifier + pub verifier_id: T, + /// Point of G1 used by a Verifier to link multiple proof presentations + /// by the same Prover. + pub pseudonym: &'a [u8; OCTET_POINT_G1_LENGTH], + /// Header containing context and application specific information + pub header: Option, + /// Vector of messages protected by the signature, including a flag + /// indicating which to reveal in the derived proof + pub messages: Option<&'a [BbsProofGenRevealMessageRequest]>, + /// Signature to derive the signature proof of knowledge from + pub signature: &'a [u8; BBS_BLS12381G1_SIGNATURE_LENGTH], + /// Presentation header to be bound to the signature proof of knowledge + pub presentation_header: Option, + /// Flag which indicates if the signature verification should be done + /// before actual proof computation. + pub verify_signature: Option, +} + +impl<'a, T: AsRef<[u8]> + Default> Default for BbsProofGenRequest<'a, T> { + fn default() -> Self { + Self { + public_key: &[0u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], + pid: Default::default(), + verifier_id: Default::default(), + pseudonym: &[0u8; OCTET_POINT_G1_LENGTH], + header: Default::default(), + messages: Default::default(), + signature: &[0u8; BBS_BLS12381G1_SIGNATURE_LENGTH], + presentation_header: Default::default(), + verify_signature: None, + } + } +} + +/// Verify proof request for verifying a supplied signature proof of knowledge. +#[derive(Clone, Debug)] +pub struct BbsProofVerifyRequest<'a, T: AsRef<[u8]> + Default> { + /// Public key associated to the signature proof of knowledge (who signed + /// the original BBS signature the proof is derived from) + pub public_key: &'a [u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], + /// The Verifier's unique Identifier + pub verifier_id: T, + /// Point of G1 used by a Verifier to link multiple proof presentations + /// by the same Prover. + pub pseudonym: &'a [u8; OCTET_POINT_G1_LENGTH], + /// Header containing context and application specific information + pub header: Option, + /// Presentation header associated to the signature proof of knowledge + pub presentation_header: Option, + /// Proof to verify + pub proof: &'a [u8], + /// Revealed messages to validate against the signature proof of knowledge + pub messages: Option<&'a [(usize, T)]>, +} + +impl<'a, T: AsRef<[u8]> + Default> Default for BbsProofVerifyRequest<'a, T> { + fn default() -> Self { + Self { + public_key: &[0u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], + verifier_id: Default::default(), + pseudonym: &[0u8; OCTET_POINT_G1_LENGTH], + header: Default::default(), + messages: Default::default(), + presentation_header: Default::default(), + proof: &[0u8; 0], + } + } +} + +/// Generate a pseudonym request +pub struct BbsPseudonymGenRequest> { + /// The Verifier's unique Identifier + pub verifier_id: T, + /// The Prover's unique identifier + pub prover_id: T, +} diff --git a/src/schemes/pseudonym/api/proof.rs b/src/schemes/pseudonym/api/proof.rs new file mode 100644 index 00000000..d584d58a --- /dev/null +++ b/src/schemes/pseudonym/api/proof.rs @@ -0,0 +1,111 @@ +use super::dtos::{BbsProofGenRequest, BbsProofVerifyRequest}; + +use crate::{ + bbs::{ + api::utils::{digest_proof_messages, digest_revealed_proof_messages}, + core::{ + generator::memory_cached_generator::MemoryCachedGenerators, + key_pair::PublicKey, + signature::Signature, + types::Message, + }, + interface::BbsInterfaceParameter, + }, + error::Error, + pseudonym::core::{proof::ProofWithNym, pseudonym::Pseudonym}, +}; + +fn pid_to_message(pid: &T) -> Result +where + T: AsRef<[u8]> + Default, + I: BbsInterfaceParameter, +{ + Message::from_arbitrary_data::(pid.as_ref(), None) +} + +pub(crate) fn proof_gen( + request: &BbsProofGenRequest<'_, T>, +) -> Result, Error> +where + T: AsRef<[u8]> + Default, + I: BbsInterfaceParameter, +{ + let pk = PublicKey::from_octets(request.public_key)?; + + let (mut digested_messages, proof_messages) = + digest_proof_messages::<_, I>(request.messages)?; + + let generators = + MemoryCachedGenerators::::new(digested_messages.len() + 1, None)?; + + let signature = Signature::from_octets(request.signature)?; + let pseudonym = Pseudonym::from_octets(request.pseudonym)?; + + // digest the pid message + let pid = pid_to_message::<_, I>(&request.pid)?; + digested_messages.push(pid); + // proof_messages.push(ProofMessage::Hidden(pid)); + + let verify_signature = request.verify_signature.unwrap_or(true); + if verify_signature + && !(signature.verify::<_, _, _, I::Ciphersuite>( + &pk, + request.header.as_ref(), + &generators, + &digested_messages, + Some(I::api_id()), + )?) + { + return Err(Error::SignatureVerification); + }; + + let proof = ProofWithNym::new::<_, _, I::Ciphersuite>( + &pk, + &signature, + &pseudonym, + &request.verifier_id, + pid, + request.header.as_ref(), + request.presentation_header.as_ref(), + &generators, + &proof_messages, + Some(I::api_id()), + )?; + + Ok(proof.to_octets()) +} + +pub(crate) fn proof_verify( + request: &BbsProofVerifyRequest<'_, T>, +) -> Result +where + T: AsRef<[u8]> + Default, + I: BbsInterfaceParameter, +{ + let pk = PublicKey::from_octets(request.public_key)?; + let proof = ProofWithNym::from_octets(request.proof)?; + + let pseudonym = Pseudonym::from_octets(request.pseudonym)?; + + let messages = request.messages.unwrap_or(&[] as &[(usize, T)]); + let total_message_count = proof.0.m_hat_list.len() + messages.len(); + + let messages = digest_revealed_proof_messages::<_, I>( + messages, + total_message_count, // The last message should not be disclosed + )?; + + let generators = + MemoryCachedGenerators::::new(total_message_count, None)?; + + proof.verify::<_, _, I::Ciphersuite>( + &pk, + &pseudonym, + &request.verifier_id, + request.header.as_ref(), + request.presentation_header.as_ref(), + &generators, + &messages, + Some(I::api_id()), + ) +} diff --git a/src/schemes/pseudonym/api/pseudonym.rs b/src/schemes/pseudonym/api/pseudonym.rs new file mode 100644 index 00000000..d521726d --- /dev/null +++ b/src/schemes/pseudonym/api/pseudonym.rs @@ -0,0 +1,24 @@ +use super::dtos::BbsPseudonymGenRequest; + +use crate::{ + bbs::interface::BbsInterfaceParameter, + curves::bls12_381::OCTET_POINT_G1_LENGTH, + error::Error, + pseudonym::core::pseudonym::Pseudonym, +}; + +pub(crate) fn generate( + request: &BbsPseudonymGenRequest, +) -> Result<[u8; OCTET_POINT_G1_LENGTH], Error> +where + T: AsRef<[u8]>, + I: BbsInterfaceParameter, +{ + let pseudonym = Pseudonym::new::<_, I>( + &request.verifier_id, + &request.prover_id, + Some(I::api_id()), + )?; + + Ok(pseudonym.to_octets()) +} diff --git a/src/schemes/pseudonym/api/signature.rs b/src/schemes/pseudonym/api/signature.rs new file mode 100644 index 00000000..d14fcd5e --- /dev/null +++ b/src/schemes/pseudonym/api/signature.rs @@ -0,0 +1,67 @@ +use super::dtos::{BbsSignRequest, BbsVerifyRequest}; + +use crate::{ + bbs::{ + api::utils::digest_messages, + ciphersuites::bls12_381::BBS_BLS12381G1_SIGNATURE_LENGTH, + core::{ + generator::memory_cached_generator::MemoryCachedGenerators, + key_pair::{PublicKey, SecretKey}, + signature::Signature, + }, + interface::BbsInterfaceParameter, + }, + error::Error, +}; + +pub(crate) fn sign( + request: &BbsSignRequest<'_, T>, +) -> Result<[u8; BBS_BLS12381G1_SIGNATURE_LENGTH], Error> +where + T: AsRef<[u8]> + Default, + I: BbsInterfaceParameter, +{ + let sk = SecretKey::from_bytes(request.secret_key)?; + let pk = PublicKey::from_octets(request.public_key)?; + + let mut messages = digest_messages::<_, I>(request.messages)?; + let pid_msg = digest_messages::<_, I>(Some(&[&request.pid]))?; + messages.push(pid_msg[0]); + + let generators = MemoryCachedGenerators::::new(messages.len(), None)?; + + Signature::new::<_, _, _, I::Ciphersuite>( + &sk, + &pk, + request.header.as_ref(), + &generators, + &messages, + Some(I::api_id()), + ) + .map(|sig| sig.to_octets()) +} + +pub(crate) fn verify( + request: &BbsVerifyRequest<'_, T>, +) -> Result +where + T: AsRef<[u8]> + Default, + I: BbsInterfaceParameter, +{ + let pk = PublicKey::from_octets(request.public_key)?; + + let mut messages = digest_messages::<_, I>(request.messages)?; + let pid_msg = digest_messages::<_, I>(Some(&[&request.pid]))?; + messages.push(pid_msg[0]); + + let generators = MemoryCachedGenerators::::new(messages.len(), None)?; + let signature = Signature::from_octets(request.signature)?; + + signature.verify::<_, _, _, I::Ciphersuite>( + &pk, + request.header.as_ref(), + &generators, + &messages, + Some(I::api_id()), + ) +} diff --git a/src/schemes/pseudonym/ciphersuites.rs b/src/schemes/pseudonym/ciphersuites.rs new file mode 100644 index 00000000..b38c6d79 --- /dev/null +++ b/src/schemes/pseudonym/ciphersuites.rs @@ -0,0 +1,2 @@ +/// BBS BLS12-381-Sha-256 ciphersuites. +pub mod bls12_381_g1_sha_256; diff --git a/src/schemes/pseudonym/ciphersuites/bls12_381_g1_sha_256.rs b/src/schemes/pseudonym/ciphersuites/bls12_381_g1_sha_256.rs new file mode 100644 index 00000000..6fcc03f6 --- /dev/null +++ b/src/schemes/pseudonym/ciphersuites/bls12_381_g1_sha_256.rs @@ -0,0 +1,106 @@ +#![allow(dead_code)] +#![allow(unused)] +#![allow(non_snake_case)] + +// use crate::common::ciphersuite::{ +// CipherSuiteParameter, +// CipherSuiteId, +// }; + +use crate::{ + bbs::{ + ciphersuites::{ + bls12_381::BBS_BLS12381G1_SIGNATURE_LENGTH, + bls12_381_g1_sha_256::Bls12381Sha256CipherSuiteParameter, + }, + interface::BbsInterfaceParameter, + }, + common::interface::{InterfaceId, InterfaceParameter}, + curves::bls12_381::OCTET_POINT_G1_LENGTH, + error::Error, + pseudonym::api::dtos::{ + BbsProofGenRequest, + BbsProofVerifyRequest, + BbsPseudonymGenRequest, + BbsSignRequest, + BbsVerifyRequest, + }, +}; + +// BBS Interface for use with pseudonyms +#[derive(Debug, Clone)] +pub(crate) struct Bls12381Sha256InterfaceParameter; + +impl InterfaceParameter for Bls12381Sha256InterfaceParameter { + const ID: InterfaceId = InterfaceId::BbsH2gHm2sNym; +} + +impl BbsInterfaceParameter for Bls12381Sha256InterfaceParameter { + type Ciphersuite = Bls12381Sha256CipherSuiteParameter; +} + +/// Create a BLS12-381-G1-Sha-256 BBS signature including a unique +/// Prover identifier. +pub fn sign( + request: &BbsSignRequest<'_, T>, +) -> Result<[u8; BBS_BLS12381G1_SIGNATURE_LENGTH], Error> +where + T: AsRef<[u8]> + Default, +{ + crate::pseudonym::api::signature::sign::<_, Bls12381Sha256InterfaceParameter>( + request, + ) +} + +/// Verify a BLS12-381-G1-Sha-256 BBS signature including a unique +/// Prover identifier. +pub fn verify(request: &BbsVerifyRequest<'_, T>) -> Result +where + T: AsRef<[u8]> + Default, +{ + crate::pseudonym::api::signature::verify::< + _, + Bls12381Sha256InterfaceParameter, + >(request) +} + +/// Generate a BLS12-381-G1-Sha-256 BBS signature proof of knowledge including +/// a pseudonym (point of G1 used by the Proof Verifier to link multiple +/// proof presentations by the same Prover). +pub fn proof_gen( + request: &BbsProofGenRequest<'_, T>, +) -> Result, Error> +where + T: AsRef<[u8]> + Default, +{ + crate::pseudonym::api::proof::proof_gen::<_, Bls12381Sha256InterfaceParameter>( + request, + ) +} + +/// Verify a BLS12-381-G1-Sha-256 BBS signature proof of knowledge including +/// a pseudonym. +pub fn proof_verify( + request: &BbsProofVerifyRequest<'_, T>, +) -> Result +where + T: AsRef<[u8]> + Default, +{ + crate::pseudonym::api::proof::proof_verify::< + _, + Bls12381Sha256InterfaceParameter, + >(request) +} + +/// Generate a BLS12-381-G1-Sha-256 BBS pseudonym. +pub fn pseudonym_gen( + request: &BbsPseudonymGenRequest, +) -> Result<[u8; OCTET_POINT_G1_LENGTH], Error> +where + T: AsRef<[u8]>, +{ + crate::pseudonym::api::pseudonym::generate::< + _, + Bls12381Sha256InterfaceParameter, + >(request) +} diff --git a/src/schemes/pseudonym/core.rs b/src/schemes/pseudonym/core.rs new file mode 100644 index 00000000..2581dba5 --- /dev/null +++ b/src/schemes/pseudonym/core.rs @@ -0,0 +1,2 @@ +pub(crate) mod proof; +pub(crate) mod pseudonym; diff --git a/src/schemes/pseudonym/core/proof.rs b/src/schemes/pseudonym/core/proof.rs new file mode 100644 index 00000000..4d1b4710 --- /dev/null +++ b/src/schemes/pseudonym/core/proof.rs @@ -0,0 +1,303 @@ +#![allow(dead_code)] +#![allow(unused)] +#![allow(non_snake_case)] +use std::collections::BTreeMap; + +use blstrs::{G1Projective, Scalar}; + +use super::pseudonym::Pseudonym; +use crate::{ + bbs::{ + ciphersuites::BbsCiphersuiteParameters, + core::{ + generator::Generators, + key_pair::PublicKey, + proof::{Proof, RandomScalars}, + signature::Signature, + types::{CommitProofInitResult, Message, ProofMessage}, + utils::compute_challenge, + }, + }, + common::util::create_random_scalar, + curves::bls12_381::{Bls12, G2Prepared}, + error::Error, +}; +use group::{Curve, Group}; +use pairing::{MillerLoopResult as _, MultiMillerLoop}; +use rand::{CryptoRng, RngCore}; +use rand_core::OsRng; + +pub(crate) struct ProofWithNym(pub(crate) Proof); + +impl core::fmt::Display for ProofWithNym { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.0.fmt(f) + } +} + +impl ProofWithNym { + // TODO: remove the clippy warning de-activation + #[allow(clippy::too_many_arguments)] + pub fn new( + PK: &PublicKey, + signature: &Signature, + nym: &Pseudonym, + verifier_id: T, + pid: Message, + header: Option, + ph: Option, + generators: &G, + messages: &[ProofMessage], + api_id: Option>, + ) -> Result + where + T: AsRef<[u8]>, + G: Generators, + C: BbsCiphersuiteParameters, + { + Self::new_with_rng::<_, _, _, C>( + PK, + signature, + nym, + verifier_id, + pid, + header, + ph, + generators, + messages, + api_id, + OsRng, + ) + } + + // TODO: remove the clippy warning de-activation + #[allow(clippy::too_many_arguments)] + pub fn new_with_rng( + PK: &PublicKey, + signature: &Signature, + nym: &Pseudonym, + verifier_id: T, + pid: Message, + header: Option, + ph: Option, + generators: &G, + messages: &[ProofMessage], + api_id: Option>, + mut rng: R, + ) -> Result + where + T: AsRef<[u8]>, + R: RngCore + CryptoRng, + G: Generators, + C: BbsCiphersuiteParameters, + { + if header.is_none() && messages.is_empty() { + return Err(Error::BadParams { + cause: "nothing to prove".to_owned(), + }); + } + // Error out if length of messages and generators are not equal + if messages.len() + 1 != generators.message_generators_length() { + println!("messages.len() + 1 = {:?}", messages.len() + 1); + println!( + "generators.message_generators_length() = {:?}", + generators.message_generators_length() + ); + + return Err(Error::MessageGeneratorsLengthMismatch { + generators: generators.message_generators_length(), + messages: messages.len(), + }); + } + let api_id = api_id.unwrap_or([].to_vec()); + + // (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() + }; + + // Deserialization steps of the `ProofGen` operation defined in https://identity.foundation/bbs-signature/draft-bbs-signatures.html#name-proofgen + // TODO: Update reference + // + // Deserialization: + // ...(implicit steps)... + // 4. messages.push(pid) + // ...(implicit steps)... + // 10. undisclosed_indexes = range(1, L) \ disclosed_indexes + // 11. disclosed_messages = (messages[i1], ..., messages[iR]) + let mut messages_vec = messages.to_vec(); + messages_vec.push(ProofMessage::Hidden(pid)); + + let message_scalars: Vec = + messages_vec.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_vec.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)?); + } + } + } + + let init_result = Proof::proof_init::( + PK, + signature, + generators, + &random_scalars, + header, + message_scalars, + undisclosed_indexes, + &api_id, + )?; + + // Pseudonym correctness proof init + let OP = C::hash_to_curve(verifier_id.as_ref(), &api_id)?; + + let pid_tilde = random_scalars.m_tilde_scalars.last().unwrap(); + let pseudonym_proof_init = CommitProofInitResult { + commit: nym.as_point(), + commit_base: OP, + blind_commit: OP * pid_tilde, + }; + + // challenge calculation + let challenge = compute_challenge::<_, C>( + &init_result, + &disclosed_messages, + ph, + api_id, + Some(pseudonym_proof_init), + )?; + + // finalize proof + let proof = Proof::proof_finalize( + challenge, + signature.e, + random_scalars, + init_result, + undisclosed_message_scalars, + ); + + match proof { + Ok(proof_val) => Ok(ProofWithNym(proof_val)), + Err(e) => Err(e), + } + } + + // TODO: Remove this clippy warning de-activation + #[allow(clippy::too_many_arguments)] + pub fn verify( + &self, + PK: &PublicKey, + pseudonym: &Pseudonym, + verifier_id: T, + header: Option, + ph: Option, + generators: &G, + disclosed_messages: &BTreeMap, + api_id: Option>, + ) -> 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); + } + let api_id = api_id.unwrap_or([].to_vec()); + + // initialize the proof verification procedure + // TODO: Check that the last message is not revealed + // TODO: Check that the m_hat_list is not empty. + let init_res = self.0.proof_verify_init::( + PK, + header, + generators, + disclosed_messages, + &api_id, + )?; + + // initialize the pseudonym correctness proof verification procedure + let OP = C::hash_to_curve(verifier_id.as_ref(), &api_id)?; + + // unwrap() is safe here is we check that m_hat_list is non empty (TODO) + let pid_hat = self.0.m_hat_list.last().unwrap(); + let pseudonym_point = pseudonym.as_point(); + let proof_challenge = self.0.c; + let Uv = G1Projective::multi_exp( + &[OP, pseudonym_point], + &[pid_hat.0, -proof_challenge.0], + ); + + let pseudonym_proof_verify_init = CommitProofInitResult { + commit: pseudonym_point, + commit_base: OP, + blind_commit: Uv, + }; + + let challenge = compute_challenge::<_, C>( + &init_res, + disclosed_messages, + ph, + api_id, + Some(pseudonym_proof_verify_init), + )?; + + // Check the selective disclosure proof + // if c != cv, return INVALID + if proof_challenge != challenge { + return Ok(false); + } + + // This check is already done during `Proof` deserialization + // if Abar == 1, return INVALID + if self.0.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.0.A_bar.to_affine(), + &G2Prepared::from(PK.0.to_affine()), + ), + (&self.0.B_bar.to_affine(), &G2Prepared::from(-P2)), + ]) + .final_exponentiation() + .is_identity() + .unwrap_u8() + == 1) + } + + pub fn to_octets(&self) -> Vec { + self.0.to_octets() + } + + pub fn from_octets>(bytes: B) -> Result { + let proof = Proof::from_octets(bytes); + match proof { + Ok(proof_val) => Ok(ProofWithNym(proof_val)), + Err(e) => Err(e), + } + } +} diff --git a/src/schemes/pseudonym/core/pseudonym.rs b/src/schemes/pseudonym/core/pseudonym.rs new file mode 100644 index 00000000..4ff1a933 --- /dev/null +++ b/src/schemes/pseudonym/core/pseudonym.rs @@ -0,0 +1,85 @@ +#![allow(dead_code)] +#![allow(unused)] +#![allow(non_snake_case)] +use crate::{ + bbs::interface::BbsInterfaceParameter, + curves::{ + bls12_381::{G1Projective, Scalar, OCTET_POINT_G1_LENGTH}, + point_serde::{octets_to_point_g1, point_to_octets_g1}, + }, + error::Error, + schemes::bbs::ciphersuites::BbsCiphersuiteParameters, +}; +use ff::Field; +use group::Group; +pub(crate) struct Pseudonym(G1Projective); + +// TODO: Use ct to check equalities bellow +impl Pseudonym { + pub fn new( + verifier_id: &T, + prover_id: &T, + api_id: Option>, + ) -> Result + where + T: AsRef<[u8]>, + I: BbsInterfaceParameter, + { + // Check that Verifier ID and Prover ID are not empty. + let verifier_id = verifier_id.as_ref(); + let prover_id = prover_id.as_ref(); + + if verifier_id.is_empty() || prover_id.is_empty() { + return Err(Error::BadParams { + cause: "Both the Verifier and the Prover Identifiers must be \ + non empty" + .to_owned(), + }); + } + + let api_id = api_id.as_ref().map_or(&[] as &[u8], |v| v.as_ref()); + let OP = I::Ciphersuite::hash_to_curve(verifier_id, api_id)?; + + // Check that OP is not the identity, the base point of G1 or P1. + if OP.is_identity().unwrap_u8() == 1u8 + || OP == G1Projective::generator() + || OP == I::Ciphersuite::p1().unwrap() + { + return Err(Error::CryptoOps { + cause: "Origin defined point of G1 must not be the Identity, \ + the Base point or the P1 point of the ciphersuite" + .to_owned(), + }); + } + + let pid_scalar = I::map_message_to_scalar_as_hash( + prover_id, None, // Use the default dst + )?; + + if pid_scalar.is_zero().unwrap_u8() == 1u8 + || pid_scalar == Scalar::one() + { + return Err(Error::CryptoOps { + cause: "Invalid Prover ID after is mapped to a scalar" + .to_owned(), + }); + }; + + Ok(Self(OP * pid_scalar)) + } + + pub fn as_point(&self) -> G1Projective { + self.0 + } + + pub fn to_octets(&self) -> [u8; OCTET_POINT_G1_LENGTH] { + point_to_octets_g1(&self.0) + } + + pub fn from_octets( + bytes: &[u8; OCTET_POINT_G1_LENGTH], + ) -> Result { + let point = octets_to_point_g1(bytes)?; + Ok(Self(point)) + } +} diff --git a/src/tests/bbs/mod.rs b/src/tests/bbs/mod.rs index f2343f84..ab0b15d6 100644 --- a/src/tests/bbs/mod.rs +++ b/src/tests/bbs/mod.rs @@ -19,15 +19,16 @@ mod test_data; mod generators; mod key_pair; -mod proof; +pub(crate) mod proof; mod signature; -const TEST_KEY_GEN_IKM: &[u8; 32] = b"not_A_random_seed_at_Allllllllll"; +pub(crate) const TEST_KEY_GEN_IKM: &[u8; 32] = + b"not_A_random_seed_at_Allllllllll"; const TEST_KEY_INFO: &[u8; 52] = b"this-IS-some-key-metadata-to-be-used-in-test-key-gen"; -const TEST_KEY_INFOS: [&[u8]; 7] = [ +pub(crate) const TEST_KEY_INFOS: [&[u8]; 7] = [ b"", b"abc", b"abcdefgh", @@ -71,7 +72,7 @@ const EXPECTED_SIGNATURES: [&str; 7] = [ const TEST_PRESENTATION_HEADER_1: &[u8; 26] = b"test_presentation-header-1"; const TEST_PRESENTATION_HEADER_2: &[u8; 26] = b"test_presentation-header-2"; -fn create_generators_helper( +pub(crate) fn create_generators_helper( num_of_messages: usize, ) -> MemoryCachedGenerators { MemoryCachedGenerators::::new( @@ -97,7 +98,7 @@ fn test_generators_random_message_generators( generators } -fn get_test_messages() -> Vec { +pub(crate) fn get_test_messages() -> Vec { TEST_CLAIMS .iter() .map(|b| { @@ -116,7 +117,7 @@ fn get_random_test_messages(num_messages: usize) -> Vec { vec![Message::random(&mut OsRng); num_messages] } -fn get_random_test_key_pair() -> KeyPair { +pub(crate) fn get_random_test_key_pair() -> KeyPair { KeyPair::random(&mut OsRng, TEST_KEY_INFO) .expect("key pair generation failed") } diff --git a/src/tests/bbs/proof.rs b/src/tests/bbs/proof.rs index 7da20ed1..ef81848a 100644 --- a/src/tests/bbs/proof.rs +++ b/src/tests/bbs/proof.rs @@ -58,7 +58,7 @@ pub(crate) mod test_helper { use super::*; use rand::{CryptoRng, RngCore}; - pub(super) fn to_proof_revealed_messages( + pub(crate) fn to_proof_revealed_messages( messages: &Vec, revealed_indices: &BTreeSet, ) -> (Vec, BTreeMap) { diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 8d40eac7..94d83efb 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,3 +1,4 @@ mod bbs; mod common; pub(crate) mod mock_rng; +mod pseudonym; diff --git a/src/tests/pseudonym/mod.rs b/src/tests/pseudonym/mod.rs new file mode 100644 index 00000000..5e3cb98b --- /dev/null +++ b/src/tests/pseudonym/mod.rs @@ -0,0 +1 @@ +mod proof; diff --git a/src/tests/pseudonym/proof.rs b/src/tests/pseudonym/proof.rs new file mode 100644 index 00000000..6bc31010 --- /dev/null +++ b/src/tests/pseudonym/proof.rs @@ -0,0 +1,134 @@ +use crate::{ + bbs::{ + ciphersuites::bls12_381_g1_shake_256::{ + Bls12381Shake256CipherSuiteParameter, + Bls12381Shake256InterfaceParameter, + }, + core::{key_pair::KeyPair, signature::Signature, types::ProofMessage}, + }, + pseudonym::core::{proof::ProofWithNym, pseudonym::Pseudonym}, +}; +use std::collections::BTreeMap; + +use crate::tests::bbs::{ + create_generators_helper, + get_test_messages, + TEST_KEY_GEN_IKM, + TEST_KEY_INFOS, +}; + +const TEST_PID: &[u8; 14] = b"TEST_PROVER_ID"; +const TEST_VID: &[u8; 16] = b"TEST_VERIFIER_ID"; + +const TEST_API_ID: &[u8; 28] = b"TEST_APPLICATION_IDENTIFIER_"; +const TEST_HEADER: &[u8; 16] = b"some_app_context"; +const TEST_PRESENTATION_HEADER: &[u8; 15] = b"some_randomness"; + +mod test_helper { + use crate::bbs::{ + ciphersuites::bls12_381_g1_shake_256::Bls12381Shake256InterfaceParameter, + core::types::Message, + interface::BbsInterfaceParameter, + }; + + pub(super) fn pid_to_message>(pid: T) -> Message { + Message::from_arbitrary_data::< + Bls12381Shake256InterfaceParameter + >(pid.as_ref(), + Some(&Bls12381Shake256InterfaceParameter::default_map_message_to_scalar_as_hash_dst()) + ).unwrap() + } +} + +#[test] +fn gen_verify_different_key_pairs() { + let header = Some(TEST_HEADER.as_ref()); + let ph = Some(TEST_PRESENTATION_HEADER.as_ref()); + let messages = get_test_messages(); + let pid_msg = test_helper::pid_to_message(&TEST_PID); + + let mut messages_with_pid = messages.clone(); + messages_with_pid.push(pid_msg); + + let generators = create_generators_helper(messages.len() + 1); + + for i in 0..TEST_KEY_INFOS.len() { + let key_pair = KeyPair::new(TEST_KEY_GEN_IKM, TEST_KEY_INFOS[i]) + .expect("key pair generation failed"); + + let signature = + Signature::new::<_, _, _, Bls12381Shake256CipherSuiteParameter>( + &key_pair.secret_key, + &key_pair.public_key, + header, + &generators, + &messages_with_pid, + Some(TEST_API_ID.to_vec()), + ) + .expect("signature generation failed"); + + let verify_res = signature + .verify::<_, _, _, Bls12381Shake256CipherSuiteParameter>( + &key_pair.public_key, + header, + &generators, + &messages_with_pid, + Some(TEST_API_ID.to_vec()), + ) + .expect("signature verification failed"); + + assert!(verify_res); + + let mut proof_msgs: Vec = + messages.iter().map(|a| ProofMessage::Hidden(*a)).collect(); + + let pseudonym = + Pseudonym::new::<_, Bls12381Shake256InterfaceParameter>( + &TEST_VID.as_ref(), + &TEST_PID.as_ref(), + Some(TEST_API_ID.to_vec()), + ) + .expect("failed to calculate pseudonym"); + + for j in 0..proof_msgs.len() { + let proof_with_nym = ProofWithNym::new::< + _, + _, + Bls12381Shake256CipherSuiteParameter, + >( + &key_pair.public_key, + &signature, + &pseudonym, + TEST_VID.as_ref(), + pid_msg, + header, + ph, + &generators, + &proof_msgs, + Some(TEST_API_ID.to_vec()), + ) + .expect("failed to generate proof with pseudonym"); + + let mut revealed_msgs = BTreeMap::new(); + for k in 0..j { + revealed_msgs.insert(k, proof_msgs[k].get_message()); + } + + let proof_verify_res = proof_with_nym + .verify::<_, _, Bls12381Shake256CipherSuiteParameter>( + &key_pair.public_key, + &pseudonym, + TEST_VID.as_ref(), + header, + ph, + &generators, + &revealed_msgs, + Some(TEST_API_ID.to_vec()), + ) + .expect("Proof with nym verification failed unexpectedly"); + assert!(proof_verify_res); + + proof_msgs[j] = ProofMessage::Revealed(messages[j]); + } + } +} diff --git a/tests/nym_api.rs b/tests/nym_api.rs new file mode 100644 index 00000000..1405bce0 --- /dev/null +++ b/tests/nym_api.rs @@ -0,0 +1,215 @@ +#![allow(dead_code)] +#![allow(unused)] +#![allow(non_snake_case)] + +use pairing_crypto::{ + bbs::{ciphersuites::bls12_381::KeyPair, BbsProofGenRevealMessageRequest}, + pseudonym::{ + api::dtos::{ + BbsProofGenRequest, + BbsProofVerifyRequest, + BbsPseudonymGenRequest, + BbsSignRequest, + BbsVerifyRequest, + }, + ciphersuites::bls12_381_g1_sha_256::{ + proof_gen as bls12_381_g1_sha_256_proof_gen, + proof_verify as bls12_381_g1_sha_256_proof_verify, + pseudonym_gen as bls12_381_g1_sha_256_pseudonym_gen, + sign as bls12_381_g1_sha_256_sign, + verify as bls12_381_g1_sha_256_verify, + }, + }, +}; + +const TEST_KEY_GEN_SEED: &[u8; 35] = b"trust_me_im_a_totally_random_scalar"; +const TEST_KEY_INFOS: [&[u8]; 7] = [ + b"", + b"abc", + b"abcdefgh", + b"abcdefghijklmnopqrstuvwxyz", + b"qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + b"12345678901234567890123456789012345678901234567890", + b"1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik,9ol.0p;/", +]; + +const TEST_CLAIMS: [&[u8]; 7] = [ + b"", + b"first_name", + b"surname", + b"date_of_birth", + b"father", + b"mother", + b"credential_id", +]; + +const TEST_HEADER: &[u8; 11] = b"test-header"; + +const TEST_PRESENTATION_HEADER: &[u8; 24] = b"test-presentation-header"; + +const TEST_VERIFIER_ID: &[u8; 24] = b"test-verifier-identifier"; +const TEST_PROVER_ID: &[u8; 22] = b"test-prover-identifier"; + +const EXPECTED_SIGNATURE_SHA_256: [&str; 7] = [ + "b00a353780b1cd8fe46842d5714d79272e53125cf0c315a2a87b74ca46ef06027a30625044c071bf62d6fb0c0245a6bd2210ef2cd149cb7e0010739b9064aa666f70cf4a189182412fa4569f1f3bc6a3", + "93f92920bd6d40b091481e6f73bd105e3445c2ac1e76751bcb4aae14569954754212ca69a1604497fe9218c7972e2a9b03de12d03bbe3ce07635d2b4e2d3a0dac65a4f80cb6e2b4a4a3addf0253a045e", + "902a1ac4894d31f7baf974781e2c02c61d3b9d59db49ae3fcffddfebe03b877c7fb407316c2202e67d94463c59f5b49619a5371dcad985db400e920ca33be87aaa5a797010304c8c58b1a6f220aaee93", + "81a3f5ff87c7ae53a4aa6cc8e2f4a43c498ca32fe6b15e7b4d3dd904bfa73ea6a4c7dde1e3930f112ff9441e5cc443e46495463be0df3291818e9f8718a745f422ec73e6fd395157d60a79a21eb20bbe", + "ab2b4919a7fe9939f8989b2d874d754756e731b99b3d5423b9d2535842ed0f9e542e821e942e9ddf25242a9cbd69d86b58486c7aa6ef9cfbb4df206e9b66186ccc8684688c535405bde0ba29fe5b2135", + "924c38d441d8b021bf47adcd555622c372b012ef616b18f61b356701835a0371825ee5d7a03417f5d6fd6a15d7e7b8a038e5a01ea493df76211b8d1a174eb81c8525ca86a3c0ab129052a85ea7832b2c", + "927b8609124d5ae90f2f9333233aeecb9e85408b9285eb5b58c1f04969c46cbf01977d229e6186878d46115d734847510afefc4f9ea17bd865446926a09f1fcb1968c1562a3b3ad138a53d484e82c865", +]; + +macro_rules! nym_sign_verify_e2e_nominal { + ($sign_fn:ident, $verify_fn:ident, $signature_test_vector:ident) => { + let header: Vec = TEST_HEADER.to_vec(); + let messages = TEST_CLAIMS.map(|e| e.to_vec()); + + for i in 0..TEST_KEY_INFOS.len() { + let (secret_key, public_key) = + KeyPair::new(TEST_KEY_GEN_SEED, TEST_KEY_INFOS[i]) + .map(|key_pair| { + ( + key_pair.secret_key.to_bytes(), + key_pair.public_key.to_octets(), + ) + }) + .expect("key generation failed"); + + let signature = $sign_fn(&BbsSignRequest { + secret_key: &secret_key, + public_key: &public_key, + pid: TEST_PROVER_ID.to_vec(), + header: Some(header.clone()), + messages: Some(&messages), + }) + .expect("signature generation failed"); + + let expected_signature = hex::decode($signature_test_vector[i]) + .expect("signature hex decoding failed"); + assert_eq!(signature.to_vec(), expected_signature); + // println!("{:?},", hex::encode(signature)); + + assert!($verify_fn(&BbsVerifyRequest { + public_key: &public_key, + pid: TEST_PROVER_ID.to_vec(), + header: Some(header.clone()), + messages: Some(&messages), + signature: &signature + }) + .expect("signature verification failed")); + } + }; +} + +#[test] +fn sign_verify_e2e_nominal() { + nym_sign_verify_e2e_nominal!( + bls12_381_g1_sha_256_sign, + bls12_381_g1_sha_256_verify, + EXPECTED_SIGNATURE_SHA_256 + ); +} + +macro_rules! proof_gen_verify_e2e_nominal { + ( + $sign_fn:ident, + $verify_fn:ident, + $proof_gen_fn:ident, + $proof_verify_fn:ident, + $pseudonym_gen:ident, + ) => { + let header: Vec = TEST_HEADER.to_vec(); + let presentation_header = TEST_PRESENTATION_HEADER.as_ref(); + let messages = TEST_CLAIMS.map(|e| e.to_vec()); + + for i in 0..TEST_KEY_INFOS.len() { + let (secret_key, public_key) = + KeyPair::new(TEST_KEY_GEN_SEED, TEST_KEY_INFOS[i]) + .map(|key_pair| { + ( + key_pair.secret_key.to_bytes(), + key_pair.public_key.to_octets(), + ) + }) + .expect("key generation failed"); + + let signature = $sign_fn(&BbsSignRequest { + secret_key: &secret_key, + public_key: &public_key, + pid: TEST_PROVER_ID.to_vec(), + header: Some(header.clone()), + messages: Some(&messages), + }) + .expect("signature generation failed"); + + assert!($verify_fn(&BbsVerifyRequest { + public_key: &public_key, + pid: TEST_PROVER_ID.to_vec(), + header: Some(header.clone()), + messages: Some(&messages), + signature: &signature + }) + .expect("signature verification failed")); + + let mut proof_messages: Vec> = + messages + .iter() + .map(|value| BbsProofGenRevealMessageRequest { + reveal: false, + value: value.clone(), + }) + .collect(); + + let pseudonym = $pseudonym_gen(&BbsPseudonymGenRequest { + verifier_id: TEST_VERIFIER_ID.to_vec(), + prover_id: TEST_PROVER_ID.to_vec(), + }) + .expect("pseudonym generation failed"); + + for j in 0..proof_messages.len() { + let proof = &$proof_gen_fn(&BbsProofGenRequest { + public_key: &public_key, + pid: TEST_PROVER_ID.to_vec(), + verifier_id: TEST_VERIFIER_ID.to_vec(), + pseudonym: &pseudonym, + header: Some(header.clone()), + messages: Some(&proof_messages), + signature: &signature, + presentation_header: Some(presentation_header.to_vec()), + verify_signature: Some(true), + }) + .expect("proof generation failed"); + + let mut revealed_msgs = Vec::new(); + for k in 0..j { + revealed_msgs.push((k as usize, TEST_CLAIMS[k])); + } + + assert!($proof_verify_fn(&BbsProofVerifyRequest { + public_key: &public_key, + verifier_id: TEST_VERIFIER_ID.as_slice(), + pseudonym: &pseudonym, + header: Some(&header.clone()), + presentation_header: Some(presentation_header), + proof: &proof, + messages: Some(&revealed_msgs), + }) + .expect("proof verification failed")); + + proof_messages[j].reveal = true; + } + } + }; +} + +#[test] +fn proof_gen_verify_e2e_nominal() { + proof_gen_verify_e2e_nominal!( + bls12_381_g1_sha_256_sign, + bls12_381_g1_sha_256_verify, + bls12_381_g1_sha_256_proof_gen, + bls12_381_g1_sha_256_proof_verify, + bls12_381_g1_sha_256_pseudonym_gen, + ); +} From fa2c1dcfce1a71b77df146a3f727f227d34b2cc1 Mon Sep 17 00:00:00 2001 From: Vasilis Kalos Date: Sun, 17 Sep 2023 01:19:40 +0300 Subject: [PATCH 2/3] refactor: updates and cleanups --- src/error.rs | 6 ++ src/schemes/bbs/core/proof.rs | 63 +++++++--------- src/schemes/pseudonym/api/dtos.rs | 12 +-- src/schemes/pseudonym/api/proof.rs | 3 +- src/schemes/pseudonym/api/signature.rs | 4 +- .../ciphersuites/bls12_381_g1_sha_256.rs | 9 --- src/schemes/pseudonym/core/proof.rs | 74 ++++++++++--------- src/schemes/pseudonym/core/pseudonym.rs | 33 ++++++--- src/tests/pseudonym/proof.rs | 4 +- tests/nym_api.rs | 14 ++-- 10 files changed, 111 insertions(+), 111 deletions(-) diff --git a/src/error.rs b/src/error.rs index 95f33723..e5faec05 100644 --- a/src/error.rs +++ b/src/error.rs @@ -47,6 +47,9 @@ pub enum Error { /// Public key is not valid. InvalidPublicKey, + /// Pseudonym is not valid + InvalidPseudonym, + /// Signature is malformed. MalformedSignature { /// Detailed cause. @@ -126,6 +129,9 @@ impl core::fmt::Debug for Error { Error::InvalidPublicKey => { write!(f, "public key is invalid.") } + Error::InvalidPseudonym => { + write!(f, "pseudonym is invalid") + } Error::MalformedSignature { ref cause } => { write!(f, "signature is malformed: cause: {cause}") } diff --git a/src/schemes/bbs/core/proof.rs b/src/schemes/bbs/core/proof.rs index df4cdaed..712f0846 100644 --- a/src/schemes/bbs/core/proof.rs +++ b/src/schemes/bbs/core/proof.rs @@ -52,7 +52,6 @@ macro_rules! slicer { }; } -#[derive(Default)] pub(crate) struct RandomScalars { pub r1: Scalar, pub r2_tilde: Scalar, @@ -60,6 +59,17 @@ pub(crate) struct RandomScalars { pub m_tilde_scalars: Vec, } +impl Default for RandomScalars { + fn default() -> Self { + Self { + r1: Default::default(), + r2_tilde: Default::default(), + z_tilde: Default::default(), + m_tilde_scalars: Vec::new() as Vec, + } + } +} + impl RandomScalars { pub fn insert_m_tilde(&mut self, m_tilde: Scalar) { self.m_tilde_scalars.push(m_tilde); @@ -144,21 +154,6 @@ impl Proof { G: Generators, I: BbsInterfaceParameter, { - // Input parameter checks - // Error out if there is no `header` and not any `ProofMessage` - if header.is_none() && messages.is_empty() { - return Err(Error::BadParams { - cause: "nothing to prove".to_owned(), - }); - } - // Error out if length of messages and generators are not equal - if messages.len() != generators.message_generators_length() { - return Err(Error::MessageGeneratorsLengthMismatch { - generators: generators.message_generators_length(), - messages: messages.len(), - }); - } - // (r1, r2, r3, m~_j1, ..., m~_jU) = calculate_random_scalars(3+U) let mut random_scalars = RandomScalars { r1: create_random_scalar(&mut rng)?, @@ -257,29 +252,12 @@ impl Proof { // cv_for_hash = encode_for_hash(cv_array) // if cv_for_hash is INVALID, return INVALID // cv = hash_to_scalar(cv_for_hash, 1) -<<<<<<< HEAD - let cv = compute_challenge::<_, I>(&init_res, disclosed_messages, ph)?; -======= -<<<<<<< HEAD let cv = compute_challenge::<_, I>( &init_res, disclosed_messages, ph, + None )?; -======= -<<<<<<< HEAD - let cv = compute_challenge::<_, I>(&init_res, disclosed_messages, ph)?; -======= - let cv = compute_challenge::<_, C>( - &init_res, - disclosed_messages, - ph, - api_id, - None, - )?; ->>>>>>> 8a6d8d2 (feat: add pseudonyms) ->>>>>>> bd611bc (feat: add pseudonyms) ->>>>>>> d8735e3 (feat: add pseudonyms) // Check the selective disclosure proof // if c != cv, return INVALID @@ -327,6 +305,19 @@ impl Proof { let total_no_of_messages = message_scalars.len(); // Check input sizes. + // Error out if there is no `header` and not any `ProofMessage` + if header.is_none() && message_scalars.is_empty() { + return Err(Error::BadParams { + cause: "nothing to prove".to_owned(), + }); + } + // Error out if length of messages and generators are not equal + if total_no_of_messages != generators.message_generators_length() { + return Err(Error::MessageGeneratorsLengthMismatch { + generators: generators.message_generators_length(), + messages: total_no_of_messages, + }); + } // 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 @@ -341,7 +332,7 @@ impl Proof { // Checking that number of undisclosed messages (/indexes) <= number of // messages - if undisclosed_indexes.len() > message_scalars.len() { + if undisclosed_indexes.len() > total_no_of_messages { return Err(Error::BadParams { cause: format!( "Not disclosed messages number is invalid. Maximum \ @@ -356,7 +347,7 @@ impl Proof { let domain = compute_domain::<_, _, I>( PK, header, - message_scalars.len(), + total_no_of_messages, generators, )?; diff --git a/src/schemes/pseudonym/api/dtos.rs b/src/schemes/pseudonym/api/dtos.rs index 3f4bb574..15089e5e 100644 --- a/src/schemes/pseudonym/api/dtos.rs +++ b/src/schemes/pseudonym/api/dtos.rs @@ -17,7 +17,7 @@ pub struct BbsSignRequest<'a, T: AsRef<[u8]> + Default> { /// Public key pub public_key: &'a [u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], /// Prover unique identifier - pub pid: T, + pub prover_id: T, /// Header containing context and application specific information pub header: Option, /// Vector of messages to sign @@ -29,7 +29,7 @@ impl<'a, T: AsRef<[u8]> + Default> Default for BbsSignRequest<'a, T> { Self { secret_key: &[0u8; BBS_BLS12381G1_SECRET_KEY_LENGTH], public_key: &[0u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], - pid: Default::default(), + prover_id: Default::default(), header: Default::default(), messages: Default::default(), } @@ -42,7 +42,7 @@ pub struct BbsVerifyRequest<'a, T: AsRef<[u8]> + Default> { /// Public key pub public_key: &'a [u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], /// Prover unique identifier - pub pid: T, + pub prover_id: T, /// Header containing context and application specific information pub header: Option, /// Vector of messages to verify against a signature @@ -55,7 +55,7 @@ impl<'a, T: AsRef<[u8]> + Default> Default for BbsVerifyRequest<'a, T> { fn default() -> Self { Self { public_key: &[0u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], - pid: Default::default(), + prover_id: Default::default(), header: Default::default(), messages: Default::default(), signature: &[0u8; BBS_BLS12381G1_SIGNATURE_LENGTH], @@ -70,7 +70,7 @@ pub struct BbsProofGenRequest<'a, T: AsRef<[u8]> + Default> { /// Public key associated to the BBS signature pub public_key: &'a [u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], /// The Prover's unique identifier - pub pid: T, + pub prover_id: T, /// The Verifier's unique Identifier pub verifier_id: T, /// Point of G1 used by a Verifier to link multiple proof presentations @@ -94,7 +94,7 @@ impl<'a, T: AsRef<[u8]> + Default> Default for BbsProofGenRequest<'a, T> { fn default() -> Self { Self { public_key: &[0u8; BBS_BLS12381G1_PUBLIC_KEY_LENGTH], - pid: Default::default(), + prover_id: Default::default(), verifier_id: Default::default(), pseudonym: &[0u8; OCTET_POINT_G1_LENGTH], header: Default::default(), diff --git a/src/schemes/pseudonym/api/proof.rs b/src/schemes/pseudonym/api/proof.rs index d584d58a..1258f9c5 100644 --- a/src/schemes/pseudonym/api/proof.rs +++ b/src/schemes/pseudonym/api/proof.rs @@ -42,9 +42,8 @@ where let pseudonym = Pseudonym::from_octets(request.pseudonym)?; // digest the pid message - let pid = pid_to_message::<_, I>(&request.pid)?; + let pid = pid_to_message::<_, I>(&request.prover_id)?; digested_messages.push(pid); - // proof_messages.push(ProofMessage::Hidden(pid)); let verify_signature = request.verify_signature.unwrap_or(true); if verify_signature diff --git a/src/schemes/pseudonym/api/signature.rs b/src/schemes/pseudonym/api/signature.rs index d14fcd5e..dd4540f3 100644 --- a/src/schemes/pseudonym/api/signature.rs +++ b/src/schemes/pseudonym/api/signature.rs @@ -25,7 +25,7 @@ where let pk = PublicKey::from_octets(request.public_key)?; let mut messages = digest_messages::<_, I>(request.messages)?; - let pid_msg = digest_messages::<_, I>(Some(&[&request.pid]))?; + let pid_msg = digest_messages::<_, I>(Some(&[&request.prover_id]))?; messages.push(pid_msg[0]); let generators = MemoryCachedGenerators::::new(messages.len(), None)?; @@ -51,7 +51,7 @@ where let pk = PublicKey::from_octets(request.public_key)?; let mut messages = digest_messages::<_, I>(request.messages)?; - let pid_msg = digest_messages::<_, I>(Some(&[&request.pid]))?; + let pid_msg = digest_messages::<_, I>(Some(&[&request.prover_id]))?; messages.push(pid_msg[0]); let generators = MemoryCachedGenerators::::new(messages.len(), None)?; diff --git a/src/schemes/pseudonym/ciphersuites/bls12_381_g1_sha_256.rs b/src/schemes/pseudonym/ciphersuites/bls12_381_g1_sha_256.rs index 6fcc03f6..d5236237 100644 --- a/src/schemes/pseudonym/ciphersuites/bls12_381_g1_sha_256.rs +++ b/src/schemes/pseudonym/ciphersuites/bls12_381_g1_sha_256.rs @@ -1,12 +1,3 @@ -#![allow(dead_code)] -#![allow(unused)] -#![allow(non_snake_case)] - -// use crate::common::ciphersuite::{ -// CipherSuiteParameter, -// CipherSuiteId, -// }; - use crate::{ bbs::{ ciphersuites::{ diff --git a/src/schemes/pseudonym/core/proof.rs b/src/schemes/pseudonym/core/proof.rs index 4d1b4710..83a474a1 100644 --- a/src/schemes/pseudonym/core/proof.rs +++ b/src/schemes/pseudonym/core/proof.rs @@ -1,6 +1,5 @@ -#![allow(dead_code)] -#![allow(unused)] #![allow(non_snake_case)] + use std::collections::BTreeMap; use blstrs::{G1Projective, Scalar}; @@ -41,9 +40,9 @@ impl ProofWithNym { pub fn new( PK: &PublicKey, signature: &Signature, - nym: &Pseudonym, + pseudonym: &Pseudonym, verifier_id: T, - pid: Message, + prover_id: Message, header: Option, ph: Option, generators: &G, @@ -58,9 +57,9 @@ impl ProofWithNym { Self::new_with_rng::<_, _, _, C>( PK, signature, - nym, + pseudonym, verifier_id, - pid, + prover_id, header, ph, generators, @@ -75,9 +74,9 @@ impl ProofWithNym { pub fn new_with_rng( PK: &PublicKey, signature: &Signature, - nym: &Pseudonym, + pseudonym: &Pseudonym, verifier_id: T, - pid: Message, + prover_id: Message, header: Option, ph: Option, generators: &G, @@ -91,24 +90,6 @@ impl ProofWithNym { G: Generators, C: BbsCiphersuiteParameters, { - if header.is_none() && messages.is_empty() { - return Err(Error::BadParams { - cause: "nothing to prove".to_owned(), - }); - } - // Error out if length of messages and generators are not equal - if messages.len() + 1 != generators.message_generators_length() { - println!("messages.len() + 1 = {:?}", messages.len() + 1); - println!( - "generators.message_generators_length() = {:?}", - generators.message_generators_length() - ); - - return Err(Error::MessageGeneratorsLengthMismatch { - generators: generators.message_generators_length(), - messages: messages.len(), - }); - } let api_id = api_id.unwrap_or([].to_vec()); // (r1, r2, r3, m~_j1, ..., m~_jU) = calculate_random_scalars(3+U) @@ -124,12 +105,12 @@ impl ProofWithNym { // // Deserialization: // ...(implicit steps)... - // 4. messages.push(pid) + // 4. messages.push(prover_id) // ...(implicit steps)... // 10. undisclosed_indexes = range(1, L) \ disclosed_indexes // 11. disclosed_messages = (messages[i1], ..., messages[iR]) let mut messages_vec = messages.to_vec(); - messages_vec.push(ProofMessage::Hidden(pid)); + messages_vec.push(ProofMessage::Hidden(prover_id)); let message_scalars: Vec = messages_vec.iter().map(|m| m.get_message().0).collect(); @@ -169,7 +150,7 @@ impl ProofWithNym { let pid_tilde = random_scalars.m_tilde_scalars.last().unwrap(); let pseudonym_proof_init = CommitProofInitResult { - commit: nym.as_point(), + commit: pseudonym.as_point(), commit_base: OP, blind_commit: OP * pid_tilde, }; @@ -221,11 +202,35 @@ impl ProofWithNym { if PK.is_valid().unwrap_u8() == 0u8 { return Err(Error::InvalidPublicKey); } + + // the pseudonym should be a point of G1 but not any of the constant + // "reserved" points (i.e., the identity of G1 or the base + // generator and the base point of G1). + if pseudonym.is_valid::().unwrap_u8() == 0u8 { + return Err(Error::InvalidPseudonym); + } + + // Check that the m_hat_list is not empty (the prover_id + // should always be undisclosed). + if self.0.m_hat_list.is_empty() { + return Err(Error::BadParams { + cause: "At least on message must be undisclosed".to_owned(), + }); + } + + // Check that the last message (the prover_id) is not revealed + if let Some(val) = disclosed_messages.last_key_value() { + if *val.0 == self.0.m_hat_list.len() + disclosed_messages.len() { + return Err(Error::BadParams { + cause: "The last signed message should not be revealed" + .to_owned(), + }); + } + } + let api_id = api_id.unwrap_or([].to_vec()); // initialize the proof verification procedure - // TODO: Check that the last message is not revealed - // TODO: Check that the m_hat_list is not empty. let init_res = self.0.proof_verify_init::( PK, header, @@ -236,14 +241,13 @@ impl ProofWithNym { // initialize the pseudonym correctness proof verification procedure let OP = C::hash_to_curve(verifier_id.as_ref(), &api_id)?; - - // unwrap() is safe here is we check that m_hat_list is non empty (TODO) - let pid_hat = self.0.m_hat_list.last().unwrap(); let pseudonym_point = pseudonym.as_point(); let proof_challenge = self.0.c; + + // unwrap() is safe here since we check that m_hat_list is non empty let Uv = G1Projective::multi_exp( &[OP, pseudonym_point], - &[pid_hat.0, -proof_challenge.0], + &[self.0.m_hat_list.last().unwrap().0, -proof_challenge.0], ); let pseudonym_proof_verify_init = CommitProofInitResult { diff --git a/src/schemes/pseudonym/core/pseudonym.rs b/src/schemes/pseudonym/core/pseudonym.rs index 4ff1a933..429f9975 100644 --- a/src/schemes/pseudonym/core/pseudonym.rs +++ b/src/schemes/pseudonym/core/pseudonym.rs @@ -1,17 +1,20 @@ -#![allow(dead_code)] -#![allow(unused)] #![allow(non_snake_case)] + use crate::{ - bbs::interface::BbsInterfaceParameter, + bbs::{ + ciphersuites::BbsCiphersuiteParameters, + interface::BbsInterfaceParameter, + }, curves::{ bls12_381::{G1Projective, Scalar, OCTET_POINT_G1_LENGTH}, point_serde::{octets_to_point_g1, point_to_octets_g1}, }, error::Error, - schemes::bbs::ciphersuites::BbsCiphersuiteParameters, }; use ff::Field; -use group::Group; +use group::{Curve, Group}; +use subtle::{Choice, ConstantTimeEq}; + pub(crate) struct Pseudonym(G1Projective); // TODO: Use ct to check equalities bellow @@ -42,8 +45,8 @@ impl Pseudonym { // Check that OP is not the identity, the base point of G1 or P1. if OP.is_identity().unwrap_u8() == 1u8 - || OP == G1Projective::generator() - || OP == I::Ciphersuite::p1().unwrap() + || OP.ct_eq(&G1Projective::generator()).unwrap_u8() == 1u8 + || OP.ct_eq(&I::Ciphersuite::p1().unwrap()).unwrap_u8() == 1u8 { return Err(Error::CryptoOps { cause: "Origin defined point of G1 must not be the Identity, \ @@ -57,11 +60,10 @@ impl Pseudonym { )?; if pid_scalar.is_zero().unwrap_u8() == 1u8 - || pid_scalar == Scalar::one() + || pid_scalar.ct_eq(&Scalar::one()).unwrap_u8() == 1u8 { return Err(Error::CryptoOps { - cause: "Invalid Prover ID after is mapped to a scalar" - .to_owned(), + cause: "Invalid Prover ID".to_owned(), }); }; @@ -82,4 +84,15 @@ impl Pseudonym { let point = octets_to_point_g1(bytes)?; Ok(Self(point)) } + + pub fn is_valid(&self) -> Choice + where + C: BbsCiphersuiteParameters, + { + (!self.0.is_identity()) + & self.0.is_on_curve() + & self.0.to_affine().is_torsion_free() + & !(Choice::from((self.0 == G1Projective::generator()) as u8)) + & !(Choice::from((self.0 == C::p1().unwrap()) as u8)) + } } diff --git a/src/tests/pseudonym/proof.rs b/src/tests/pseudonym/proof.rs index 6bc31010..6a599e8d 100644 --- a/src/tests/pseudonym/proof.rs +++ b/src/tests/pseudonym/proof.rs @@ -17,8 +17,8 @@ use crate::tests::bbs::{ TEST_KEY_INFOS, }; -const TEST_PID: &[u8; 14] = b"TEST_PROVER_ID"; -const TEST_VID: &[u8; 16] = b"TEST_VERIFIER_ID"; +const TEST_PID: &[u8; 14] = b"TEST_PROVER_ID"; // Test prover id +const TEST_VID: &[u8; 16] = b"TEST_VERIFIER_ID"; // Test verifier id const TEST_API_ID: &[u8; 28] = b"TEST_APPLICATION_IDENTIFIER_"; const TEST_HEADER: &[u8; 16] = b"some_app_context"; diff --git a/tests/nym_api.rs b/tests/nym_api.rs index 1405bce0..619d08d1 100644 --- a/tests/nym_api.rs +++ b/tests/nym_api.rs @@ -1,7 +1,3 @@ -#![allow(dead_code)] -#![allow(unused)] -#![allow(non_snake_case)] - use pairing_crypto::{ bbs::{ciphersuites::bls12_381::KeyPair, BbsProofGenRevealMessageRequest}, pseudonym::{ @@ -79,7 +75,7 @@ macro_rules! nym_sign_verify_e2e_nominal { let signature = $sign_fn(&BbsSignRequest { secret_key: &secret_key, public_key: &public_key, - pid: TEST_PROVER_ID.to_vec(), + prover_id: TEST_PROVER_ID.to_vec(), header: Some(header.clone()), messages: Some(&messages), }) @@ -92,7 +88,7 @@ macro_rules! nym_sign_verify_e2e_nominal { assert!($verify_fn(&BbsVerifyRequest { public_key: &public_key, - pid: TEST_PROVER_ID.to_vec(), + prover_id: TEST_PROVER_ID.to_vec(), header: Some(header.clone()), messages: Some(&messages), signature: &signature @@ -137,7 +133,7 @@ macro_rules! proof_gen_verify_e2e_nominal { let signature = $sign_fn(&BbsSignRequest { secret_key: &secret_key, public_key: &public_key, - pid: TEST_PROVER_ID.to_vec(), + prover_id: TEST_PROVER_ID.to_vec(), header: Some(header.clone()), messages: Some(&messages), }) @@ -145,7 +141,7 @@ macro_rules! proof_gen_verify_e2e_nominal { assert!($verify_fn(&BbsVerifyRequest { public_key: &public_key, - pid: TEST_PROVER_ID.to_vec(), + prover_id: TEST_PROVER_ID.to_vec(), header: Some(header.clone()), messages: Some(&messages), signature: &signature @@ -170,7 +166,7 @@ macro_rules! proof_gen_verify_e2e_nominal { for j in 0..proof_messages.len() { let proof = &$proof_gen_fn(&BbsProofGenRequest { public_key: &public_key, - pid: TEST_PROVER_ID.to_vec(), + prover_id: TEST_PROVER_ID.to_vec(), verifier_id: TEST_VERIFIER_ID.to_vec(), pseudonym: &pseudonym, header: Some(header.clone()), From 6f59c4fdc0057fb7be39a7e09762ff06c1d27303 Mon Sep 17 00:00:00 2001 From: Vasilis Kalos Date: Wed, 17 Jan 2024 12:22:32 +0200 Subject: [PATCH 3/3] refacor: fix failing test --- src/schemes/bbs/core/proof.rs | 8 ++--- src/schemes/bbs/interface.rs | 5 +++ src/schemes/pseudonym/api/proof.rs | 9 ++--- src/schemes/pseudonym/api/pseudonym.rs | 7 ++-- src/schemes/pseudonym/api/signature.rs | 6 ++-- src/schemes/pseudonym/core/proof.rs | 43 +++++++++------------- src/schemes/pseudonym/core/pseudonym.rs | 9 ++--- src/tests/pseudonym/proof.rs | 47 ++++++++++--------------- 8 files changed, 50 insertions(+), 84 deletions(-) diff --git a/src/schemes/bbs/core/proof.rs b/src/schemes/bbs/core/proof.rs index 712f0846..7e6686af 100644 --- a/src/schemes/bbs/core/proof.rs +++ b/src/schemes/bbs/core/proof.rs @@ -252,12 +252,8 @@ impl Proof { // 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::<_, I>( - &init_res, - disclosed_messages, - ph, - None - )?; + let cv = + compute_challenge::<_, I>(&init_res, disclosed_messages, ph, None)?; // Check the selective disclosure proof // if c != cv, return INVALID diff --git a/src/schemes/bbs/interface.rs b/src/schemes/bbs/interface.rs index 562ac4a0..bd9d6b33 100644 --- a/src/schemes/bbs/interface.rs +++ b/src/schemes/bbs/interface.rs @@ -103,4 +103,9 @@ pub(crate) trait BbsInterfaceParameter: InterfaceParameter { [&Self::api_id(), DEFAULT_DST_SUFFIX_H2S.as_bytes()].concat(); Self::Ciphersuite::hash_to_scalar(data_to_hash, &e_dst) } + + // Hash to curve function, using the Interface's identifier as a dst + fn hash_to_curve(message: &[u8]) -> Result { + Self::Ciphersuite::hash_to_curve(message, &Self::api_id()) + } } diff --git a/src/schemes/pseudonym/api/proof.rs b/src/schemes/pseudonym/api/proof.rs index 1258f9c5..e6ab104a 100644 --- a/src/schemes/pseudonym/api/proof.rs +++ b/src/schemes/pseudonym/api/proof.rs @@ -47,18 +47,17 @@ where let verify_signature = request.verify_signature.unwrap_or(true); if verify_signature - && !(signature.verify::<_, _, _, I::Ciphersuite>( + && !(signature.verify::<_, _, _, I>( &pk, request.header.as_ref(), &generators, &digested_messages, - Some(I::api_id()), )?) { return Err(Error::SignatureVerification); }; - let proof = ProofWithNym::new::<_, _, I::Ciphersuite>( + let proof = ProofWithNym::new::<_, _, I>( &pk, &signature, &pseudonym, @@ -68,7 +67,6 @@ where request.presentation_header.as_ref(), &generators, &proof_messages, - Some(I::api_id()), )?; Ok(proof.to_octets()) @@ -97,7 +95,7 @@ where let generators = MemoryCachedGenerators::::new(total_message_count, None)?; - proof.verify::<_, _, I::Ciphersuite>( + proof.verify::<_, _, I>( &pk, &pseudonym, &request.verifier_id, @@ -105,6 +103,5 @@ where request.presentation_header.as_ref(), &generators, &messages, - Some(I::api_id()), ) } diff --git a/src/schemes/pseudonym/api/pseudonym.rs b/src/schemes/pseudonym/api/pseudonym.rs index d521726d..62ec4cf5 100644 --- a/src/schemes/pseudonym/api/pseudonym.rs +++ b/src/schemes/pseudonym/api/pseudonym.rs @@ -14,11 +14,8 @@ where T: AsRef<[u8]>, I: BbsInterfaceParameter, { - let pseudonym = Pseudonym::new::<_, I>( - &request.verifier_id, - &request.prover_id, - Some(I::api_id()), - )?; + let pseudonym = + Pseudonym::new::<_, I>(&request.verifier_id, &request.prover_id)?; Ok(pseudonym.to_octets()) } diff --git a/src/schemes/pseudonym/api/signature.rs b/src/schemes/pseudonym/api/signature.rs index dd4540f3..31a10e94 100644 --- a/src/schemes/pseudonym/api/signature.rs +++ b/src/schemes/pseudonym/api/signature.rs @@ -30,13 +30,12 @@ where let generators = MemoryCachedGenerators::::new(messages.len(), None)?; - Signature::new::<_, _, _, I::Ciphersuite>( + Signature::new::<_, _, _, I>( &sk, &pk, request.header.as_ref(), &generators, &messages, - Some(I::api_id()), ) .map(|sig| sig.to_octets()) } @@ -57,11 +56,10 @@ where let generators = MemoryCachedGenerators::::new(messages.len(), None)?; let signature = Signature::from_octets(request.signature)?; - signature.verify::<_, _, _, I::Ciphersuite>( + signature.verify::<_, _, _, I>( &pk, request.header.as_ref(), &generators, &messages, - Some(I::api_id()), ) } diff --git a/src/schemes/pseudonym/core/proof.rs b/src/schemes/pseudonym/core/proof.rs index 83a474a1..24bc5ed9 100644 --- a/src/schemes/pseudonym/core/proof.rs +++ b/src/schemes/pseudonym/core/proof.rs @@ -16,6 +16,7 @@ use crate::{ types::{CommitProofInitResult, Message, ProofMessage}, utils::compute_challenge, }, + interface::BbsInterfaceParameter, }, common::util::create_random_scalar, curves::bls12_381::{Bls12, G2Prepared}, @@ -37,7 +38,7 @@ impl core::fmt::Display for ProofWithNym { impl ProofWithNym { // TODO: remove the clippy warning de-activation #[allow(clippy::too_many_arguments)] - pub fn new( + pub fn new( PK: &PublicKey, signature: &Signature, pseudonym: &Pseudonym, @@ -47,14 +48,13 @@ impl ProofWithNym { ph: Option, generators: &G, messages: &[ProofMessage], - api_id: Option>, ) -> Result where T: AsRef<[u8]>, G: Generators, - C: BbsCiphersuiteParameters, + I: BbsInterfaceParameter, { - Self::new_with_rng::<_, _, _, C>( + Self::new_with_rng::<_, _, _, I>( PK, signature, pseudonym, @@ -64,14 +64,13 @@ impl ProofWithNym { ph, generators, messages, - api_id, OsRng, ) } // TODO: remove the clippy warning de-activation #[allow(clippy::too_many_arguments)] - pub fn new_with_rng( + pub fn new_with_rng( PK: &PublicKey, signature: &Signature, pseudonym: &Pseudonym, @@ -81,17 +80,14 @@ impl ProofWithNym { ph: Option, generators: &G, messages: &[ProofMessage], - api_id: Option>, mut rng: R, ) -> Result where T: AsRef<[u8]>, R: RngCore + CryptoRng, G: Generators, - C: BbsCiphersuiteParameters, + I: BbsInterfaceParameter, { - let api_id = api_id.unwrap_or([].to_vec()); - // (r1, r2, r3, m~_j1, ..., m~_jU) = calculate_random_scalars(3+U) let mut random_scalars = RandomScalars { r1: create_random_scalar(&mut rng)?, @@ -134,7 +130,7 @@ impl ProofWithNym { } } - let init_result = Proof::proof_init::( + let init_result = Proof::proof_init::( PK, signature, generators, @@ -142,11 +138,10 @@ impl ProofWithNym { header, message_scalars, undisclosed_indexes, - &api_id, )?; // Pseudonym correctness proof init - let OP = C::hash_to_curve(verifier_id.as_ref(), &api_id)?; + let OP = I::hash_to_curve(verifier_id.as_ref())?; let pid_tilde = random_scalars.m_tilde_scalars.last().unwrap(); let pseudonym_proof_init = CommitProofInitResult { @@ -156,11 +151,10 @@ impl ProofWithNym { }; // challenge calculation - let challenge = compute_challenge::<_, C>( + let challenge = compute_challenge::<_, I>( &init_result, &disclosed_messages, ph, - api_id, Some(pseudonym_proof_init), )?; @@ -181,7 +175,7 @@ impl ProofWithNym { // TODO: Remove this clippy warning de-activation #[allow(clippy::too_many_arguments)] - pub fn verify( + pub fn verify( &self, PK: &PublicKey, pseudonym: &Pseudonym, @@ -190,12 +184,11 @@ impl ProofWithNym { ph: Option, generators: &G, disclosed_messages: &BTreeMap, - api_id: Option>, ) -> Result where T: AsRef<[u8]>, G: Generators, - C: BbsCiphersuiteParameters, + I: BbsInterfaceParameter, { // if KeyValidate(PK) is INVALID, return INVALID // `PK` should not be an identity and should belong to subgroup G2 @@ -206,7 +199,7 @@ impl ProofWithNym { // the pseudonym should be a point of G1 but not any of the constant // "reserved" points (i.e., the identity of G1 or the base // generator and the base point of G1). - if pseudonym.is_valid::().unwrap_u8() == 0u8 { + if pseudonym.is_valid::().unwrap_u8() == 0u8 { return Err(Error::InvalidPseudonym); } @@ -228,19 +221,16 @@ impl ProofWithNym { } } - let api_id = api_id.unwrap_or([].to_vec()); - // initialize the proof verification procedure - let init_res = self.0.proof_verify_init::( + let init_res = self.0.proof_verify_init::( PK, header, generators, disclosed_messages, - &api_id, )?; // initialize the pseudonym correctness proof verification procedure - let OP = C::hash_to_curve(verifier_id.as_ref(), &api_id)?; + let OP = I::hash_to_curve(verifier_id.as_ref())?; let pseudonym_point = pseudonym.as_point(); let proof_challenge = self.0.c; @@ -256,11 +246,10 @@ impl ProofWithNym { blind_commit: Uv, }; - let challenge = compute_challenge::<_, C>( + let challenge = compute_challenge::<_, I>( &init_res, disclosed_messages, ph, - api_id, Some(pseudonym_proof_verify_init), )?; @@ -279,7 +268,7 @@ impl ProofWithNym { // Check the signature proof // if e(Abar, W) * e(Abar, -P2) != 1, return INVALID // else return VALID - let P2 = C::p2().to_affine(); + let P2 = I::Ciphersuite::p2().to_affine(); Ok(Bls12::multi_miller_loop(&[ ( &self.0.A_bar.to_affine(), diff --git a/src/schemes/pseudonym/core/pseudonym.rs b/src/schemes/pseudonym/core/pseudonym.rs index 429f9975..cbe49dcd 100644 --- a/src/schemes/pseudonym/core/pseudonym.rs +++ b/src/schemes/pseudonym/core/pseudonym.rs @@ -19,11 +19,7 @@ pub(crate) struct Pseudonym(G1Projective); // TODO: Use ct to check equalities bellow impl Pseudonym { - pub fn new( - verifier_id: &T, - prover_id: &T, - api_id: Option>, - ) -> Result + pub fn new(verifier_id: &T, prover_id: &T) -> Result where T: AsRef<[u8]>, I: BbsInterfaceParameter, @@ -40,8 +36,7 @@ impl Pseudonym { }); } - let api_id = api_id.as_ref().map_or(&[] as &[u8], |v| v.as_ref()); - let OP = I::Ciphersuite::hash_to_curve(verifier_id, api_id)?; + let OP = I::hash_to_curve(verifier_id)?; // Check that OP is not the identity, the base point of G1 or P1. if OP.is_identity().unwrap_u8() == 1u8 diff --git a/src/tests/pseudonym/proof.rs b/src/tests/pseudonym/proof.rs index 6a599e8d..a3edb3e8 100644 --- a/src/tests/pseudonym/proof.rs +++ b/src/tests/pseudonym/proof.rs @@ -1,9 +1,6 @@ use crate::{ bbs::{ - ciphersuites::bls12_381_g1_shake_256::{ - Bls12381Shake256CipherSuiteParameter, - Bls12381Shake256InterfaceParameter, - }, + ciphersuites::bls12_381_g1_shake_256::Bls12381Shake256InterfaceParameter, core::{key_pair::KeyPair, signature::Signature, types::ProofMessage}, }, pseudonym::core::{proof::ProofWithNym, pseudonym::Pseudonym}, @@ -20,7 +17,6 @@ use crate::tests::bbs::{ const TEST_PID: &[u8; 14] = b"TEST_PROVER_ID"; // Test prover id const TEST_VID: &[u8; 16] = b"TEST_VERIFIER_ID"; // Test verifier id -const TEST_API_ID: &[u8; 28] = b"TEST_APPLICATION_IDENTIFIER_"; const TEST_HEADER: &[u8; 16] = b"some_app_context"; const TEST_PRESENTATION_HEADER: &[u8; 15] = b"some_randomness"; @@ -57,23 +53,21 @@ fn gen_verify_different_key_pairs() { .expect("key pair generation failed"); let signature = - Signature::new::<_, _, _, Bls12381Shake256CipherSuiteParameter>( + Signature::new::<_, _, _, Bls12381Shake256InterfaceParameter>( &key_pair.secret_key, &key_pair.public_key, header, &generators, &messages_with_pid, - Some(TEST_API_ID.to_vec()), ) .expect("signature generation failed"); let verify_res = signature - .verify::<_, _, _, Bls12381Shake256CipherSuiteParameter>( + .verify::<_, _, _, Bls12381Shake256InterfaceParameter>( &key_pair.public_key, header, &generators, &messages_with_pid, - Some(TEST_API_ID.to_vec()), ) .expect("signature verification failed"); @@ -86,28 +80,23 @@ fn gen_verify_different_key_pairs() { Pseudonym::new::<_, Bls12381Shake256InterfaceParameter>( &TEST_VID.as_ref(), &TEST_PID.as_ref(), - Some(TEST_API_ID.to_vec()), ) .expect("failed to calculate pseudonym"); for j in 0..proof_msgs.len() { - let proof_with_nym = ProofWithNym::new::< - _, - _, - Bls12381Shake256CipherSuiteParameter, - >( - &key_pair.public_key, - &signature, - &pseudonym, - TEST_VID.as_ref(), - pid_msg, - header, - ph, - &generators, - &proof_msgs, - Some(TEST_API_ID.to_vec()), - ) - .expect("failed to generate proof with pseudonym"); + let proof_with_nym = + ProofWithNym::new::<_, _, Bls12381Shake256InterfaceParameter>( + &key_pair.public_key, + &signature, + &pseudonym, + TEST_VID.as_ref(), + pid_msg, + header, + ph, + &generators, + &proof_msgs, + ) + .expect("failed to generate proof with pseudonym"); let mut revealed_msgs = BTreeMap::new(); for k in 0..j { @@ -115,7 +104,7 @@ fn gen_verify_different_key_pairs() { } let proof_verify_res = proof_with_nym - .verify::<_, _, Bls12381Shake256CipherSuiteParameter>( + .verify::<_, _, Bls12381Shake256InterfaceParameter>( &key_pair.public_key, &pseudonym, TEST_VID.as_ref(), @@ -123,9 +112,9 @@ fn gen_verify_different_key_pairs() { ph, &generators, &revealed_msgs, - Some(TEST_API_ID.to_vec()), ) .expect("Proof with nym verification failed unexpectedly"); + assert!(proof_verify_res); proof_msgs[j] = ProofMessage::Revealed(messages[j]);