Skip to content

Commit 8cae48f

Browse files
committed
BBS#
Signed-off-by: lovesh <lovesh.bond@gmail.com>
1 parent 82f19ab commit 8cae48f

17 files changed

+1236
-6
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Library providing privacy enhancing cryptographic primitives.
2828
9. [LegoGroth16](./legogroth16/). LegoGroth16, the [LegoSNARK](https://eprint.iacr.org/2019/142) variant of [Groth16](https://eprint.iacr.org/2016/260) zkSNARK proof system
2929
10. [Oblivious Transfer (OT) and Oblivious Transfer Extensions (OTE)](./oblivious_transfer).
3030
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).
31+
12. [Keyed-Verification Anonymous Credentials (KVAC)](./kvac). Implements Keyed-Verification Anonymous Credentials (KVAC) schemes.
3132

3233
## Composite proof system
3334

kvac/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ ark-bls12-381.workspace = true
2727
ark-ed25519 = { version = "^0.4.0", default-features = false }
2828
ark-curve25519 = { version = "^0.4.0", default-features = false }
2929
ark-secp256k1 = { version = "^0.4.0", default-features = false }
30+
ark-secp256r1 = { version = "^0.4.0", default-features = false }
31+
sha2 = {version = "0.10.8", default-features = false}
3032

3133
[features]
3234
default = [ "parallel"]

kvac/README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ KVACs are supposed to be verified by the issuer only (or anyone who shares the i
1111
Both implementations support additional verification methods that allow joint verification of proof of possession of credentials where one
1212
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
1313
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
14-
(eg. to get paid by the verifier) while still not harming the user's privacy as it doesn't learn any revealed attributes.
14+
(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
15+
verifier, i.e. the issuer can also provide a proof of validity or invalidity to the second verifier.
1516

1617
<!-- cargo-rdme end -->

kvac/src/bbdt_2016/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
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)
2+
3+
An alternate implementation of proof of knowledge of MAC is added which is adapted from the protocol to prove knowledge of
4+
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)
5+
6+
In addition, it supports generating proof of validity or invalidity of keyed-proofs, i.e. the proof verifying which requires the knowledge of
7+
secret key.

kvac/src/bbdt_2016/keyed_proof.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,16 @@ impl<G: AffineRepr> ProofOfValidityOfKeyedProof<G> {
174174
proof: &KeyedProof<G>,
175175
pk: impl Into<&'a G>,
176176
g_0: impl Into<&'a G>,
177+
) -> Result<(), KVACError> {
178+
self.verify_given_destructured::<D>(&proof.B_0, &proof.C, pk, g_0)
179+
}
180+
181+
pub fn verify_given_destructured<'a, D: Digest>(
182+
&self,
183+
B_0: &G,
184+
C: &G,
185+
pk: impl Into<&'a G>,
186+
g_0: impl Into<&'a G>,
177187
) -> Result<(), KVACError> {
178188
if self.sc_proof.response != self.sc_pk.response {
179189
return Err(KVACError::InvalidKeyedProof);
@@ -185,13 +195,13 @@ impl<G: AffineRepr> ProofOfValidityOfKeyedProof<G> {
185195
.challenge_contribution(g_0, pk, &mut challenge_bytes)
186196
.unwrap();
187197
self.sc_proof
188-
.challenge_contribution(&proof.B_0, &proof.C, &mut challenge_bytes)
198+
.challenge_contribution(B_0, C, &mut challenge_bytes)
189199
.unwrap();
190200
let challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
191201
if !self.sc_pk.verify(pk, g_0, &challenge) {
192202
return Err(KVACError::InvalidKeyedProof);
193203
}
194-
if !self.sc_proof.verify(&proof.C, &proof.B_0, &challenge) {
204+
if !self.sc_proof.verify(C, B_0, &challenge) {
195205
return Err(KVACError::InvalidKeyedProof);
196206
}
197207
Ok(())

kvac/src/bbdt_2016/mac.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ impl<G: AffineRepr> ProofOfValidityOfMAC<G> {
208208
return Err(KVACError::InvalidMACProof);
209209
}
210210
let params = params.as_ref();
211-
// B = h + g * s + g_1 * m_1 + g_2 * m_2 + ... g_n * m_n
211+
// B = h + g * s + g_1 * m_1 + g_2 * m_2 + ... g_n * m_n - A * e
212212
let B =
213213
(params.b(messages.iter().enumerate(), &mac.s)? + mac.A * mac.e.neg()).into_affine();
214214

kvac/src/bbdt_2016/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! 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)
2+
//!
23
//! An alternate implementation of proof of knowledge of MAC is added which is adapted from the protocol to prove knowledge of
34
//! 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)
5+
//!
46
//! In addition it supports generating proof of validity or invalidity of keyed-proofs, i.e. the proof verifying which requires the knowledge of
57
//! secret key.
68

kvac/src/bbdt_2016/setup.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,12 @@ impl<G: AffineRepr> MACParams<G> {
7373
affine_group_element_from_byte_slices!(label, b" : g"),
7474
affine_group_element_from_byte_slices!(label, b" : h"),
7575
{
76-
let h: Vec<_> = n_projective_group_elements::<G, D>(
76+
let g: Vec<_> = n_projective_group_elements::<G, D>(
7777
1..message_count + 1,
7878
&concat_slices!(label, b" : g_"),
7979
)
8080
.collect();
81-
G::Group::normalize_batch(&h)
81+
G::Group::normalize_batch(&g)
8282
}
8383
);
8484

kvac/src/bbs_sharp/README.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
BBS# as described [here](https://github.com/user-attachments/files/15905230/BBS_Sharp_Short_TR.pdf)
2+
3+
This assumes that the messages/attributes have already been prepared before signing, i.e. attributes are hashed
4+
with public salts, etc and whats called `H_i` in the paper is already created.
5+
6+
Assumes that a Schnorr Signature will be generated by the user's secure hardware.
7+
8+
Implements both the offline and half-offline (HOL) mode.
9+
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.
10+
In the latter, the user needs to communicate with the signer before creating a proof and get "some helper data"
11+
to create a proof which the verifier can check without needing the secret key or interacting with the issuer.
12+
For efficiency and avoiding correlation (when signer and verifier collude), the user gets a batch of
13+
"helper data" to let him create several proofs.

kvac/src/bbs_sharp/mac.rs

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
use crate::{
2+
bbs_sharp::setup::{MACParams, PublicKey, SecretKey},
3+
error::KVACError,
4+
};
5+
use ark_ec::{AffineRepr, CurveGroup};
6+
use ark_ff::{Field, Zero};
7+
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
8+
use ark_std::{ops::Neg, rand::RngCore, vec, vec::Vec, UniformRand};
9+
use digest::Digest;
10+
use dock_crypto_utils::{
11+
expect_equality, serde_utils::ArkObjectBytes, signature::MultiMessageSignatureParams,
12+
};
13+
use schnorr_pok::{
14+
compute_random_oracle_challenge,
15+
discrete_log::{PokDiscreteLog, PokDiscreteLogProtocol},
16+
};
17+
use serde::{Deserialize, Serialize};
18+
use serde_with::serde_as;
19+
use zeroize::{Zeroize, ZeroizeOnDrop};
20+
21+
#[serde_as]
22+
#[derive(
23+
Clone,
24+
PartialEq,
25+
Eq,
26+
Debug,
27+
CanonicalSerialize,
28+
CanonicalDeserialize,
29+
Serialize,
30+
Deserialize,
31+
Zeroize,
32+
ZeroizeOnDrop,
33+
)]
34+
pub struct MAC<G: AffineRepr> {
35+
#[serde_as(as = "ArkObjectBytes")]
36+
pub A: G,
37+
#[serde_as(as = "ArkObjectBytes")]
38+
pub e: G::ScalarField,
39+
}
40+
41+
/// A proof corresponding to a MAC that it is correctly created, i.e. can be verified successfully by someone possessing
42+
/// the secret key. Verifying the proof does not require the secret key.
43+
/// Consists of 2 protocols for discrete log relations, and both have the same discrete log
44+
///
45+
#[serde_as]
46+
#[derive(
47+
Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize,
48+
)]
49+
pub struct ProofOfValidityOfMAC<G: AffineRepr> {
50+
/// 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`
51+
pub sc_B: PokDiscreteLog<G>,
52+
/// For proving knowledge of secret key, i.e. `pk = g * sk`
53+
pub sc_pk: PokDiscreteLog<G>,
54+
}
55+
56+
impl<G: AffineRepr> MAC<G> {
57+
pub fn new<R: RngCore>(
58+
rng: &mut R,
59+
messages: &[G::ScalarField],
60+
user_public_key: &PublicKey<G>,
61+
signer_secret_key: &SecretKey<G::ScalarField>,
62+
params: impl AsRef<MACParams<G>>,
63+
) -> Result<Self, KVACError> {
64+
if messages.is_empty() {
65+
return Err(KVACError::NoMessageGiven);
66+
}
67+
let params = params.as_ref();
68+
expect_equality!(
69+
messages.len(),
70+
params.supported_message_count(),
71+
KVACError::MessageCountIncompatibleWithMACParams
72+
);
73+
let mut e = G::ScalarField::rand(rng);
74+
while (e + signer_secret_key.0).is_zero() {
75+
e = G::ScalarField::rand(rng)
76+
}
77+
// 1/(e+x)
78+
let e_plus_x_inv = (e + signer_secret_key.0).inverse().unwrap();
79+
let A = params.b(messages.iter().enumerate(), user_public_key)? * e_plus_x_inv;
80+
Ok(Self {
81+
A: A.into_affine(),
82+
e,
83+
})
84+
}
85+
86+
pub fn verify(
87+
&self,
88+
messages: &[G::ScalarField],
89+
user_public_key: &PublicKey<G>,
90+
sk: impl AsRef<G::ScalarField>,
91+
params: impl AsRef<MACParams<G>>,
92+
) -> Result<(), KVACError> {
93+
if messages.is_empty() {
94+
return Err(KVACError::NoMessageGiven);
95+
}
96+
let params = params.as_ref();
97+
expect_equality!(
98+
messages.len(),
99+
params.supported_message_count(),
100+
KVACError::MessageCountIncompatibleWithMACParams
101+
);
102+
let b = params.b(messages.iter().enumerate(), user_public_key)?;
103+
let e_plus_x_inv = (self.e + sk.as_ref())
104+
.inverse()
105+
.ok_or(KVACError::CannotInvert0)?;
106+
if (b * e_plus_x_inv).into_affine() != self.A {
107+
return Err(KVACError::InvalidMAC);
108+
}
109+
Ok(())
110+
}
111+
}
112+
113+
impl<G: AffineRepr> ProofOfValidityOfMAC<G> {
114+
pub fn new<R: RngCore, D: Digest>(
115+
rng: &mut R,
116+
mac: &MAC<G>,
117+
secret_key: &SecretKey<G::ScalarField>,
118+
public_key: &PublicKey<G>,
119+
params: impl AsRef<MACParams<G>>,
120+
) -> Self {
121+
let witness = secret_key.0;
122+
let blinding = G::ScalarField::rand(rng);
123+
let B = (mac.A * witness).into_affine();
124+
let params = params.as_ref();
125+
let mut challenge_bytes = vec![];
126+
// As witness has to be proven same in both protocols.
127+
let p1 = PokDiscreteLogProtocol::init(witness, blinding, &mac.A);
128+
let p2 = PokDiscreteLogProtocol::init(witness, blinding, &params.g);
129+
p1.challenge_contribution(&mac.A, &B, &mut challenge_bytes)
130+
.unwrap();
131+
p2.challenge_contribution(&params.g, &public_key.0, &mut challenge_bytes)
132+
.unwrap();
133+
let challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
134+
Self {
135+
sc_B: p1.gen_proof(&challenge),
136+
sc_pk: p2.gen_proof(&challenge),
137+
}
138+
}
139+
140+
pub fn verify<D: Digest>(
141+
&self,
142+
mac: &MAC<G>,
143+
messages: &[G::ScalarField],
144+
user_public_key: &PublicKey<G>,
145+
signer_public_key: &PublicKey<G>,
146+
params: impl AsRef<MACParams<G>>,
147+
) -> Result<(), KVACError> {
148+
if self.sc_B.response != self.sc_pk.response {
149+
return Err(KVACError::InvalidMACProof);
150+
}
151+
let params = params.as_ref();
152+
// B = g_0 + user_pk + g_1 * m_1 + g_2 * m_2 + ... g_n * m_n - A * e
153+
let B = (params.b(messages.iter().enumerate(), user_public_key)? + mac.A * mac.e.neg())
154+
.into_affine();
155+
156+
let mut challenge_bytes = vec![];
157+
self.sc_B
158+
.challenge_contribution(&mac.A, &B, &mut challenge_bytes)
159+
.unwrap();
160+
self.sc_pk
161+
.challenge_contribution(&params.g, &signer_public_key.0, &mut challenge_bytes)
162+
.unwrap();
163+
let challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
164+
if !self.sc_B.verify(&B, &mac.A, &challenge) {
165+
return Err(KVACError::InvalidMACProof);
166+
}
167+
if !self
168+
.sc_pk
169+
.verify(&signer_public_key.0, &params.g, &challenge)
170+
{
171+
return Err(KVACError::InvalidMACProof);
172+
}
173+
Ok(())
174+
}
175+
}
176+
177+
#[cfg(test)]
178+
mod tests {
179+
use super::*;
180+
use ark_secp256r1::{Affine, Fr};
181+
use ark_std::rand::{prelude::StdRng, SeedableRng};
182+
use sha2::Sha256;
183+
184+
#[test]
185+
fn mac_verification() {
186+
let mut rng = StdRng::seed_from_u64(0u64);
187+
let message_count = 10;
188+
let messages = (0..message_count)
189+
.map(|_| Fr::rand(&mut rng))
190+
.collect::<Vec<_>>();
191+
let params = MACParams::<Affine>::new::<Sha256>(b"test", message_count);
192+
let signer_sk = SecretKey::new(&mut rng);
193+
let signer_pk = PublicKey::new(&signer_sk, &params.g);
194+
195+
let user_sk = SecretKey::new(&mut rng);
196+
let user_pk = PublicKey::new(&user_sk, &params.g);
197+
198+
// Signer sends the following 2 items to the user
199+
let mac = MAC::new(&mut rng, &messages, &user_pk, &signer_sk, &params).unwrap();
200+
let proof =
201+
ProofOfValidityOfMAC::new::<_, Sha256>(&mut rng, &mac, &signer_sk, &signer_pk, &params);
202+
203+
// User verifies both
204+
mac.verify(&messages, &user_pk, &signer_sk, &params)
205+
.unwrap();
206+
proof
207+
.verify::<Sha256>(&mac, &messages, &user_pk, &signer_pk, params)
208+
.unwrap();
209+
}
210+
}

kvac/src/bbs_sharp/mod.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//! BBS# as described [here](https://github.com/user-attachments/files/15905230/BBS_Sharp_Short_TR.pdf)
2+
//!
3+
//! This assumes that the messages/attributes have already been prepared before signing, i.e. attributes are hashed
4+
//! with public salts, etc and whats called `H_i` in the paper is already created.
5+
//!
6+
//! Assumes that a Schnorr Signature will be generated by the user's secure hardware.
7+
//!
8+
//! Implements both the offline and half-offline (HOL) mode. In the former, the verifier is either the
9+
//! signer (has the secret key) or can ask the signer to verify the proof without revealing any user-specific info
10+
//! In the latter, the user needs to communicate with the signer before creating a proof and get "some helper data"
11+
//! to create a proof which the verifier can check without needing the secret key or interacting with the issuer.
12+
//! For efficiency and avoiding correlation (when signer and verifier collude), the user gets a batch of
13+
//! "helper data" to let him create several proofs.
14+
15+
pub mod mac;
16+
pub mod proof;
17+
pub mod setup;

0 commit comments

Comments
 (0)