Skip to content

Commit

Permalink
BBS#
Browse files Browse the repository at this point in the history
Signed-off-by: lovesh <[email protected]>
  • Loading branch information
lovesh committed Jul 15, 2024
1 parent 82f19ab commit 8cae48f
Show file tree
Hide file tree
Showing 17 changed files with 1,236 additions and 6 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Library providing privacy enhancing cryptographic primitives.
9. [LegoGroth16](./legogroth16/). LegoGroth16, the [LegoSNARK](https://eprint.iacr.org/2019/142) variant of [Groth16](https://eprint.iacr.org/2016/260) zkSNARK proof system
10. [Oblivious Transfer (OT) and Oblivious Transfer Extensions (OTE)](./oblivious_transfer).
11. [Short group signatures](./short_group_sig/). BB signature and weak-BB signature and their proofs of knowledge based on the papers [Short Signatures Without Random Oracles](https://eprint.iacr.org/2004/171) and [Scalable Revocation Scheme for Anonymous Credentials Based on n-times Unlinkable Proofs](http://library.usc.edu.ph/ACM/SIGSAC%202017/wpes/p123.pdf).
12. [Keyed-Verification Anonymous Credentials (KVAC)](./kvac). Implements Keyed-Verification Anonymous Credentials (KVAC) schemes.

## Composite proof system

Expand Down
2 changes: 2 additions & 0 deletions kvac/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ ark-bls12-381.workspace = true
ark-ed25519 = { version = "^0.4.0", default-features = false }
ark-curve25519 = { version = "^0.4.0", default-features = false }
ark-secp256k1 = { version = "^0.4.0", default-features = false }
ark-secp256r1 = { version = "^0.4.0", default-features = false }
sha2 = {version = "0.10.8", default-features = false}

[features]
default = [ "parallel"]
Expand Down
3 changes: 2 additions & 1 deletion kvac/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ KVACs are supposed to be verified by the issuer only (or anyone who shares the i
Both implementations support additional verification methods that allow joint verification of proof of possession of credentials where one
of the verifier is the issuer who knows the secret key and another verifier does not know secret key but learns the revealed attributes which
are not shared with the issuer. This lets us build for a use-case where issuer wants to allow anytime its issued credential is used
(eg. to get paid by the verifier) while still not harming the user's privacy as it doesn't learn any revealed attributes.
(eg. to get paid by the verifier) while still not harming the user's privacy as it doesn't learn any revealed attributes. The first
verifier, i.e. the issuer can also provide a proof of validity or invalidity to the second verifier.

<!-- cargo-rdme end -->
7 changes: 7 additions & 0 deletions kvac/src/bbdt_2016/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Implements KVAC from [Improved Algebraic MACs and Practical Keyed-Verification Anonymous Credentials](https://link.springer.com/chapter/10.1007/978-3-319-69453-5_20)

An alternate implementation of proof of knowledge of MAC is added which is adapted from the protocol to prove knowledge of
BBS+ signatures described in section 4.5 of the paper [Anonymous Attestation Using the Strong Diffie Hellman Assumption Revisited](https://eprint.iacr.org/2016/663)

In addition, it supports generating proof of validity or invalidity of keyed-proofs, i.e. the proof verifying which requires the knowledge of
secret key.
14 changes: 12 additions & 2 deletions kvac/src/bbdt_2016/keyed_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,16 @@ impl<G: AffineRepr> ProofOfValidityOfKeyedProof<G> {
proof: &KeyedProof<G>,
pk: impl Into<&'a G>,
g_0: impl Into<&'a G>,
) -> Result<(), KVACError> {
self.verify_given_destructured::<D>(&proof.B_0, &proof.C, pk, g_0)
}

pub fn verify_given_destructured<'a, D: Digest>(
&self,
B_0: &G,
C: &G,
pk: impl Into<&'a G>,
g_0: impl Into<&'a G>,
) -> Result<(), KVACError> {
if self.sc_proof.response != self.sc_pk.response {
return Err(KVACError::InvalidKeyedProof);
Expand All @@ -185,13 +195,13 @@ impl<G: AffineRepr> ProofOfValidityOfKeyedProof<G> {
.challenge_contribution(g_0, pk, &mut challenge_bytes)
.unwrap();
self.sc_proof
.challenge_contribution(&proof.B_0, &proof.C, &mut challenge_bytes)
.challenge_contribution(B_0, C, &mut challenge_bytes)
.unwrap();
let challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
if !self.sc_pk.verify(pk, g_0, &challenge) {
return Err(KVACError::InvalidKeyedProof);
}
if !self.sc_proof.verify(&proof.C, &proof.B_0, &challenge) {
if !self.sc_proof.verify(C, B_0, &challenge) {
return Err(KVACError::InvalidKeyedProof);
}
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion kvac/src/bbdt_2016/mac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ impl<G: AffineRepr> ProofOfValidityOfMAC<G> {
return Err(KVACError::InvalidMACProof);
}
let params = params.as_ref();
// B = h + g * s + g_1 * m_1 + g_2 * m_2 + ... g_n * m_n
// B = h + g * s + g_1 * m_1 + g_2 * m_2 + ... g_n * m_n - A * e
let B =
(params.b(messages.iter().enumerate(), &mac.s)? + mac.A * mac.e.neg()).into_affine();

Expand Down
2 changes: 2 additions & 0 deletions kvac/src/bbdt_2016/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Implements KVAC from [Improved Algebraic MACs and Practical Keyed-Verification Anonymous Credentials](https://link.springer.com/chapter/10.1007/978-3-319-69453-5_20)
//!
//! An alternate implementation of proof of knowledge of MAC is added which is adapted from the protocol to prove knowledge of
//! BBS+ signatures described in section 4.5 of the paper [Anonymous Attestation Using the Strong Diffie Hellman Assumption Revisited](https://eprint.iacr.org/2016/663)
//!
//! In addition it supports generating proof of validity or invalidity of keyed-proofs, i.e. the proof verifying which requires the knowledge of
//! secret key.
Expand Down
4 changes: 2 additions & 2 deletions kvac/src/bbdt_2016/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,12 @@ impl<G: AffineRepr> MACParams<G> {
affine_group_element_from_byte_slices!(label, b" : g"),
affine_group_element_from_byte_slices!(label, b" : h"),
{
let h: Vec<_> = n_projective_group_elements::<G, D>(
let g: Vec<_> = n_projective_group_elements::<G, D>(
1..message_count + 1,
&concat_slices!(label, b" : g_"),
)
.collect();
G::Group::normalize_batch(&h)
G::Group::normalize_batch(&g)
}
);

Expand Down
13 changes: 13 additions & 0 deletions kvac/src/bbs_sharp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
BBS# as described [here](https://github.com/user-attachments/files/15905230/BBS_Sharp_Short_TR.pdf)

This assumes that the messages/attributes have already been prepared before signing, i.e. attributes are hashed
with public salts, etc and whats called `H_i` in the paper is already created.

Assumes that a Schnorr Signature will be generated by the user's secure hardware.

Implements both the offline and half-offline (HOL) mode.
In the former, the verifier is either the signer (has the secret key) or can ask the signer to verify the proof without revealing any user-specific info.
In the latter, the user needs to communicate with the signer before creating a proof and get "some helper data"
to create a proof which the verifier can check without needing the secret key or interacting with the issuer.
For efficiency and avoiding correlation (when signer and verifier collude), the user gets a batch of
"helper data" to let him create several proofs.
210 changes: 210 additions & 0 deletions kvac/src/bbs_sharp/mac.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
use crate::{
bbs_sharp::setup::{MACParams, PublicKey, SecretKey},
error::KVACError,
};
use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::{Field, Zero};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::{ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand};
use digest::Digest;
use dock_crypto_utils::{
expect_equality, serde_utils::ArkObjectBytes, signature::MultiMessageSignatureParams,
};
use schnorr_pok::{
compute_random_oracle_challenge,
discrete_log::{PokDiscreteLog, PokDiscreteLogProtocol},
};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use zeroize::{Zeroize, ZeroizeOnDrop};

#[serde_as]
#[derive(
Clone,
PartialEq,
Eq,
Debug,
CanonicalSerialize,
CanonicalDeserialize,
Serialize,
Deserialize,
Zeroize,
ZeroizeOnDrop,
)]
pub struct MAC<G: AffineRepr> {
#[serde_as(as = "ArkObjectBytes")]
pub A: G,
#[serde_as(as = "ArkObjectBytes")]
pub e: G::ScalarField,
}

/// A proof corresponding to a MAC that it is correctly created, i.e. can be verified successfully by someone possessing
/// the secret key. Verifying the proof does not require the secret key.
/// Consists of 2 protocols for discrete log relations, and both have the same discrete log
///
#[serde_as]
#[derive(
Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize,
)]
pub struct ProofOfValidityOfMAC<G: AffineRepr> {
/// For proving `B = A * sk` where `sk` is the secret key and `B = g_0 + signer_pk + g_1 * m_1 + g_2 * m_2 + ... g_n * m_n`
pub sc_B: PokDiscreteLog<G>,
/// For proving knowledge of secret key, i.e. `pk = g * sk`
pub sc_pk: PokDiscreteLog<G>,
}

impl<G: AffineRepr> MAC<G> {
pub fn new<R: RngCore>(
rng: &mut R,
messages: &[G::ScalarField],
user_public_key: &PublicKey<G>,
signer_secret_key: &SecretKey<G::ScalarField>,
params: impl AsRef<MACParams<G>>,
) -> Result<Self, KVACError> {
if messages.is_empty() {
return Err(KVACError::NoMessageGiven);
}
let params = params.as_ref();
expect_equality!(
messages.len(),
params.supported_message_count(),
KVACError::MessageCountIncompatibleWithMACParams
);
let mut e = G::ScalarField::rand(rng);
while (e + signer_secret_key.0).is_zero() {
e = G::ScalarField::rand(rng)
}
// 1/(e+x)
let e_plus_x_inv = (e + signer_secret_key.0).inverse().unwrap();
let A = params.b(messages.iter().enumerate(), user_public_key)? * e_plus_x_inv;
Ok(Self {
A: A.into_affine(),
e,
})
}

pub fn verify(
&self,
messages: &[G::ScalarField],
user_public_key: &PublicKey<G>,
sk: impl AsRef<G::ScalarField>,
params: impl AsRef<MACParams<G>>,
) -> Result<(), KVACError> {
if messages.is_empty() {
return Err(KVACError::NoMessageGiven);
}
let params = params.as_ref();
expect_equality!(
messages.len(),
params.supported_message_count(),
KVACError::MessageCountIncompatibleWithMACParams
);
let b = params.b(messages.iter().enumerate(), user_public_key)?;
let e_plus_x_inv = (self.e + sk.as_ref())
.inverse()
.ok_or(KVACError::CannotInvert0)?;
if (b * e_plus_x_inv).into_affine() != self.A {
return Err(KVACError::InvalidMAC);
}
Ok(())
}
}

impl<G: AffineRepr> ProofOfValidityOfMAC<G> {
pub fn new<R: RngCore, D: Digest>(
rng: &mut R,
mac: &MAC<G>,
secret_key: &SecretKey<G::ScalarField>,
public_key: &PublicKey<G>,
params: impl AsRef<MACParams<G>>,
) -> Self {
let witness = secret_key.0;
let blinding = G::ScalarField::rand(rng);
let B = (mac.A * witness).into_affine();
let params = params.as_ref();
let mut challenge_bytes = vec![];
// As witness has to be proven same in both protocols.
let p1 = PokDiscreteLogProtocol::init(witness, blinding, &mac.A);
let p2 = PokDiscreteLogProtocol::init(witness, blinding, &params.g);
p1.challenge_contribution(&mac.A, &B, &mut challenge_bytes)
.unwrap();
p2.challenge_contribution(&params.g, &public_key.0, &mut challenge_bytes)
.unwrap();
let challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
Self {
sc_B: p1.gen_proof(&challenge),
sc_pk: p2.gen_proof(&challenge),
}
}

pub fn verify<D: Digest>(
&self,
mac: &MAC<G>,
messages: &[G::ScalarField],
user_public_key: &PublicKey<G>,
signer_public_key: &PublicKey<G>,
params: impl AsRef<MACParams<G>>,
) -> Result<(), KVACError> {
if self.sc_B.response != self.sc_pk.response {
return Err(KVACError::InvalidMACProof);
}
let params = params.as_ref();
// B = g_0 + user_pk + g_1 * m_1 + g_2 * m_2 + ... g_n * m_n - A * e
let B = (params.b(messages.iter().enumerate(), user_public_key)? + mac.A * mac.e.neg())
.into_affine();

let mut challenge_bytes = vec![];
self.sc_B
.challenge_contribution(&mac.A, &B, &mut challenge_bytes)
.unwrap();
self.sc_pk
.challenge_contribution(&params.g, &signer_public_key.0, &mut challenge_bytes)
.unwrap();
let challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
if !self.sc_B.verify(&B, &mac.A, &challenge) {
return Err(KVACError::InvalidMACProof);
}
if !self
.sc_pk
.verify(&signer_public_key.0, &params.g, &challenge)
{
return Err(KVACError::InvalidMACProof);
}
Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
use ark_secp256r1::{Affine, Fr};
use ark_std::rand::{prelude::StdRng, SeedableRng};
use sha2::Sha256;

#[test]
fn mac_verification() {
let mut rng = StdRng::seed_from_u64(0u64);
let message_count = 10;
let messages = (0..message_count)
.map(|_| Fr::rand(&mut rng))
.collect::<Vec<_>>();
let params = MACParams::<Affine>::new::<Sha256>(b"test", message_count);
let signer_sk = SecretKey::new(&mut rng);
let signer_pk = PublicKey::new(&signer_sk, &params.g);

let user_sk = SecretKey::new(&mut rng);
let user_pk = PublicKey::new(&user_sk, &params.g);

// Signer sends the following 2 items to the user
let mac = MAC::new(&mut rng, &messages, &user_pk, &signer_sk, &params).unwrap();
let proof =
ProofOfValidityOfMAC::new::<_, Sha256>(&mut rng, &mac, &signer_sk, &signer_pk, &params);

// User verifies both
mac.verify(&messages, &user_pk, &signer_sk, &params)
.unwrap();
proof
.verify::<Sha256>(&mac, &messages, &user_pk, &signer_pk, params)
.unwrap();
}
}
17 changes: 17 additions & 0 deletions kvac/src/bbs_sharp/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! BBS# as described [here](https://github.com/user-attachments/files/15905230/BBS_Sharp_Short_TR.pdf)
//!
//! This assumes that the messages/attributes have already been prepared before signing, i.e. attributes are hashed
//! with public salts, etc and whats called `H_i` in the paper is already created.
//!
//! Assumes that a Schnorr Signature will be generated by the user's secure hardware.
//!
//! Implements both the offline and half-offline (HOL) mode. In the former, the verifier is either the
//! signer (has the secret key) or can ask the signer to verify the proof without revealing any user-specific info
//! In the latter, the user needs to communicate with the signer before creating a proof and get "some helper data"
//! to create a proof which the verifier can check without needing the secret key or interacting with the issuer.
//! For efficiency and avoiding correlation (when signer and verifier collude), the user gets a batch of
//! "helper data" to let him create several proofs.
pub mod mac;
pub mod proof;
pub mod setup;
Loading

0 comments on commit 8cae48f

Please sign in to comment.