Compare commits

...

1 Commits

Author SHA1 Message Date
aniampio ffe9084566 Add public attributes into the commitment hash - security patch 2023-01-11 17:20:07 +00:00
3 changed files with 999 additions and 796 deletions
Generated
+932 -757
View File
File diff suppressed because it is too large Load Diff
+54 -35
View File
@@ -7,17 +7,18 @@ use std::borrow::Borrow;
use std::convert::TryInto; use std::convert::TryInto;
use bls12_381::{G1Projective, G2Projective, Scalar}; use bls12_381::{G1Projective, G2Projective, Scalar};
use digest::generic_array::typenum::Unsigned;
use digest::Digest; use digest::Digest;
use digest::generic_array::typenum::Unsigned;
use group::GroupEncoding; use group::GroupEncoding;
use itertools::izip; use itertools::izip;
use sha2::Sha256; use sha2::Sha256;
use crate::Attribute;
use crate::error::{CoconutError, Result}; use crate::error::{CoconutError, Result};
use crate::scheme::issuance::compute_commitment_hash;
use crate::scheme::setup::Parameters; use crate::scheme::setup::Parameters;
use crate::scheme::VerificationKey; use crate::scheme::VerificationKey;
use crate::utils::{hash_g1, try_deserialize_scalar, try_deserialize_scalar_vec}; use crate::utils::{hash_g1, try_deserialize_scalar, try_deserialize_scalar_vec};
use crate::Attribute;
// as per the reference python implementation // as per the reference python implementation
type ChallengeDigest = Sha256; type ChallengeDigest = Sha256;
@@ -38,10 +39,10 @@ pub struct ProofCmCs {
// and as per the bls12-381 library all elements are using big-endian form // and as per the bls12-381 library all elements are using big-endian form
/// Generates a Scalar [or Fp] challenge by hashing a number of elliptic curve points. /// Generates a Scalar [or Fp] challenge by hashing a number of elliptic curve points.
fn compute_challenge<D, I, B>(iter: I) -> Scalar fn compute_challenge<D, I, B>(iter: I) -> Scalar
where where
D: Digest, D: Digest,
I: Iterator<Item = B>, I: Iterator<Item=B>,
B: AsRef<[u8]>, B: AsRef<[u8]>,
{ {
let mut h = D::new(); let mut h = D::new();
for point_representation in iter { for point_representation in iter {
@@ -69,8 +70,8 @@ fn produce_response(witness: &Scalar, challenge: &Scalar, secret: &Scalar) -> Sc
// note: it's caller's responsibility to ensure witnesses.len() = secrets.len() // note: it's caller's responsibility to ensure witnesses.len() = secrets.len()
fn produce_responses<S>(witnesses: &[Scalar], challenge: &Scalar, secrets: &[S]) -> Vec<Scalar> fn produce_responses<S>(witnesses: &[Scalar], challenge: &Scalar, secrets: &[S]) -> Vec<Scalar>
where where
S: Borrow<Scalar>, S: Borrow<Scalar>,
{ {
debug_assert_eq!(witnesses.len(), secrets.len()); debug_assert_eq!(witnesses.len(), secrets.len());
@@ -91,6 +92,7 @@ impl ProofCmCs {
commitments: &[G1Projective], commitments: &[G1Projective],
pedersen_commitments_openings: &[Scalar], pedersen_commitments_openings: &[Scalar],
private_attributes: &[Attribute], private_attributes: &[Attribute],
public_attributes: &[Attribute],
) -> Self { ) -> Self {
// note: this is only called from `prepare_blind_sign` that already checks // note: this is only called from `prepare_blind_sign` that already checks
// whether private attributes are non-empty and whether we don't have too many // whether private attributes are non-empty and whether we don't have too many
@@ -104,7 +106,7 @@ impl ProofCmCs {
let witness_attributes = params.n_random_scalars(private_attributes.len()); let witness_attributes = params.n_random_scalars(private_attributes.len());
// recompute h // recompute h
let h = hash_g1(commitment.to_bytes()); let h = compute_commitment_hash(*commitment, public_attributes);
let hs_bytes = params let hs_bytes = params
.gen_hs() .gen_hs()
.iter() .iter()
@@ -119,10 +121,10 @@ impl ProofCmCs {
// Ccm = (wr * g1) + (wm[0] * hs[0]) + ... + (wm[i] * hs[i]) // Ccm = (wr * g1) + (wm[0] * hs[0]) + ... + (wm[i] * hs[i])
let commitment_attributes = g1 * witness_commitment_opening let commitment_attributes = g1 * witness_commitment_opening
+ witness_attributes + witness_attributes
.iter() .iter()
.zip(params.gen_hs().iter()) .zip(params.gen_hs().iter())
.map(|(wm_i, hs_i)| hs_i * wm_i) .map(|(wm_i, hs_i)| hs_i * wm_i)
.sum::<G1Projective>(); .sum::<G1Projective>();
// zkp commitments for the individual attributes // zkp commitments for the individual attributes
let commitments_attributes = witness_pedersen_commitments_openings let commitments_attributes = witness_pedersen_commitments_openings
@@ -186,7 +188,7 @@ impl ProofCmCs {
} }
// recompute h // recompute h
let h = hash_g1(commitment.to_bytes()); let h = compute_commitment_hash(*commitment, public_attributes);
let g1 = params.gen1(); let g1 = params.gen1();
let hs_bytes = params let hs_bytes = params
@@ -199,26 +201,26 @@ impl ProofCmCs {
// Cw = (cm * c) + (rr * g1) + (rm[0] * hs[0]) + ... + (rm[n] * hs[n]) // Cw = (cm * c) + (rr * g1) + (rm[0] * hs[0]) + ... + (rm[n] * hs[n])
let commitment_attributes = (commitment let commitment_attributes = (commitment
- public_attributes - public_attributes
.iter() .iter()
.zip(params.gen_hs().iter().skip(self.response_attributes.len())) .zip(params.gen_hs().iter().skip(self.response_attributes.len()))
.map(|(pub_attr, hs)| hs * pub_attr) .map(|(pub_attr, hs)| hs * pub_attr)
.sum::<G1Projective>()) .sum::<G1Projective>())
* self.challenge * self.challenge
+ g1 * self.response_opening + g1 * self.response_opening
+ self + self
.response_attributes .response_attributes
.iter() .iter()
.zip(params.gen_hs().iter()) .zip(params.gen_hs().iter())
.map(|(res_attr, hs)| hs * res_attr) .map(|(res_attr, hs)| hs * res_attr)
.sum::<G1Projective>(); .sum::<G1Projective>();
let commitments_attributes = izip!( let commitments_attributes = izip!(
commitments.iter(), commitments.iter(),
self.response_openings.iter(), self.response_openings.iter(),
self.response_attributes.iter() self.response_attributes.iter()
) )
.map(|(cm_j, r_o_j, r_m_j)| cm_j * self.challenge + g1 * r_o_j + h * r_m_j) .map(|(cm_j, r_o_j, r_m_j)| cm_j * self.challenge + g1 * r_o_j + h * r_m_j)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let commitments_bytes = commitments let commitments_bytes = commitments
.iter() .iter()
@@ -365,10 +367,10 @@ impl ProofKappaZeta {
let commitment_kappa = params.gen2() * witness_blinder let commitment_kappa = params.gen2() * witness_blinder
+ verification_key.alpha + verification_key.alpha
+ witness_attributes + witness_attributes
.iter() .iter()
.zip(verification_key.beta_g2.iter()) .zip(verification_key.beta_g2.iter())
.map(|(wm_i, beta_i)| beta_i * wm_i) .map(|(wm_i, beta_i)| beta_i * wm_i)
.sum::<G2Projective>(); .sum::<G2Projective>();
// zeta is the public value associated with the serial number // zeta is the public value associated with the serial number
let commitment_zeta = params.gen2() * witness_serial_number; let commitment_zeta = params.gen2() * witness_serial_number;
@@ -422,10 +424,10 @@ impl ProofKappaZeta {
+ params.gen2() * self.response_blinder + params.gen2() * self.response_blinder
+ verification_key.alpha * (Scalar::one() - self.challenge) + verification_key.alpha * (Scalar::one() - self.challenge)
+ response_attributes + response_attributes
.iter() .iter()
.zip(verification_key.beta_g2.iter()) .zip(verification_key.beta_g2.iter())
.map(|(priv_attr, beta_i)| beta_i * priv_attr) .map(|(priv_attr, beta_i)| beta_i * priv_attr)
.sum::<G2Projective>(); .sum::<G2Projective>();
// zeta is the public value associated with the serial number // zeta is the public value associated with the serial number
let commitment_zeta = zeta * self.challenge + params.gen2() * self.response_serial_number; let commitment_zeta = zeta * self.challenge + params.gen2() * self.response_serial_number;
@@ -529,9 +531,18 @@ mod tests {
let cms: [G1Projective; 1] = [G1Projective::random(&mut rng)]; let cms: [G1Projective; 1] = [G1Projective::random(&mut rng)];
let rs = params.n_random_scalars(1); let rs = params.n_random_scalars(1);
let private_attributes = params.n_random_scalars(1); let private_attributes = params.n_random_scalars(1);
let public_attributes = params.n_random_scalars(1);
// 0 public 1 private // 0 public 1 private
let pi_s = ProofCmCs::construct(&params, &cm, &r, &cms, &rs, &private_attributes); let pi_s = ProofCmCs::construct(
&params,
&cm,
&r,
&cms,
&rs,
&private_attributes,
&public_attributes,
);
let bytes = pi_s.to_bytes(); let bytes = pi_s.to_bytes();
assert_eq!(ProofCmCs::from_bytes(&bytes).unwrap(), pi_s); assert_eq!(ProofCmCs::from_bytes(&bytes).unwrap(), pi_s);
@@ -547,7 +558,15 @@ mod tests {
let private_attributes = params.n_random_scalars(2); let private_attributes = params.n_random_scalars(2);
// 0 public 2 privates // 0 public 2 privates
let pi_s = ProofCmCs::construct(&params, &cm, &r, &cms, &rs, &private_attributes); let pi_s = ProofCmCs::construct(
&params,
&cm,
&r,
&cms,
&rs,
&private_attributes,
&public_attributes,
);
let bytes = pi_s.to_bytes(); let bytes = pi_s.to_bytes();
assert_eq!(ProofCmCs::from_bytes(&bytes).unwrap(), pi_s); assert_eq!(ProofCmCs::from_bytes(&bytes).unwrap(), pi_s);
+13 -4
View File
@@ -201,8 +201,16 @@ pub fn compute_pedersen_commitments_for_private_attributes(
(commitments_openings, pedersen_commitments) (commitments_openings, pedersen_commitments)
} }
pub fn compute_commitment_hash(commitment: G1Projective) -> G1Projective { pub fn compute_commitment_hash(
hash_g1(commitment.to_bytes()) commitment: G1Projective,
public_attributes: &[Attribute],
) -> G1Projective {
let mut msg_bytes = Vec::with_capacity(public_attributes.len() * 32);
msg_bytes.extend_from_slice(&commitment.to_affine().to_compressed());
for attr in public_attributes {
msg_bytes.extend_from_slice(&attr.to_bytes());
}
hash_g1(msg_bytes)
} }
/// Builds cryptographic material required for blind sign. /// Builds cryptographic material required for blind sign.
@@ -230,7 +238,7 @@ pub fn prepare_blind_sign(
compute_attributes_commitment(params, private_attributes, public_attributes, hs); compute_attributes_commitment(params, private_attributes, public_attributes, hs);
// Compute the challenge as the commitment hash // Compute the challenge as the commitment hash
let commitment_hash = compute_commitment_hash(commitment); let commitment_hash = compute_commitment_hash(commitment, public_attributes);
let (pedersen_commitments_openings, pedersen_commitments) = let (pedersen_commitments_openings, pedersen_commitments) =
compute_pedersen_commitments_for_private_attributes( compute_pedersen_commitments_for_private_attributes(
@@ -246,6 +254,7 @@ pub fn prepare_blind_sign(
&pedersen_commitments, &pedersen_commitments,
&pedersen_commitments_openings, &pedersen_commitments_openings,
private_attributes, private_attributes,
public_attributes,
); );
Ok(( Ok((
@@ -276,7 +285,7 @@ pub fn blind_sign(
} }
// Verify the commitment hash // Verify the commitment hash
let h = hash_g1(blind_sign_request.commitment.to_bytes()); let h = compute_commitment_hash(blind_sign_request.commitment, public_attributes);
if !(h == blind_sign_request.commitment_hash) { if !(h == blind_sign_request.commitment_hash) {
return Err(CoconutError::Issuance( return Err(CoconutError::Issuance(
"Failed to verify the commitment hash".to_string(), "Failed to verify the commitment hash".to_string(),