Skip to content

Commit 6c57e76

Browse files
committed
Add ECDSA and designated verifier proof support in BBS-sharp, test to show BBS-sharp works with keyed-verification accumulator and relaxing the type of accumulator from Pairing to Affine
Signed-off-by: lovesh <lovesh.bond@gmail.com>
1 parent ed791bb commit 6c57e76

34 files changed

+1817
-762
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ serde_with = { version = "1.10.0", default-features = false, features = ["macros
4646
zeroize = { version = "1.7.0", features = ["derive"] }
4747
blake2 = { version = "0.10", default-features = false }
4848
ark-bls12-381 = { version = "^0.4.0", default-features = false, features = [ "curve" ] }
49+
ark-secp256r1 = { version = "^0.4.0", default-features = false }
4950
itertools = "0.12.1"
5051

5152
[profile.release]

kvac/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ serde_with.workspace = true
2020
itertools.workspace = true
2121
dock_crypto_utils = { version = "0.20.0", default-features = false, path = "../utils" }
2222
schnorr_pok = { version = "0.20.0", default-features = false, path = "../schnorr_pok" }
23+
ark-secp256r1.workspace = true
2324

2425
[dev-dependencies]
2526
blake2.workspace = true
2627
ark-bls12-381.workspace = true
2728
ark-ed25519 = { version = "^0.4.0", default-features = false }
2829
ark-curve25519 = { version = "^0.4.0", default-features = false }
2930
ark-secp256k1 = { version = "^0.4.0", default-features = false }
30-
ark-secp256r1 = { version = "^0.4.0", default-features = false }
3131
sha2 = {version = "0.10.8", default-features = false}
3232

3333
[features]

kvac/src/bbs_sharp/README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ BBS# as described [here](https://github.com/user-attachments/files/15905230/BBS_
33
This assumes that the messages/attributes have already been prepared before signing, i.e. attributes are hashed
44
with public salts, etc and whats called `H_i` in the paper is already created.
55

6-
Assumes that a Schnorr Signature will be generated by the user's secure hardware.
6+
Implements support for both Schnorr signature and ECDSA (on secp256r1) generated by the user's secure hardware.
77

88
Implements both the offline and half-offline (HOL) mode.
99
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.
1010
In the latter, the user needs to communicate with the signer before creating a proof and get "some helper data"
1111
to create a proof which the verifier can check without needing the secret key or interacting with the issuer.
1212
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.
13+
"helper data" to let him create several proofs.
14+
15+
Implements designated verifier proof for both issuer's signature (proof of validity of MAC) and user's proof of knowledge of MAC

kvac/src/bbs_sharp/ecdsa.rs

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use ark_ec::{AffineRepr, CurveGroup};
2+
use ark_ff::{Field, PrimeField};
3+
use ark_secp256r1::{Affine, Fr, G_GENERATOR_X, G_GENERATOR_Y};
4+
use ark_std::{rand::RngCore, UniformRand, Zero};
5+
6+
/// ECDSA signature
7+
pub struct Signature {
8+
pub rand_x_coord: Fr,
9+
pub response: Fr,
10+
}
11+
12+
impl Signature {
13+
/// Create new signature given that the message has already been hashed into a scalar
14+
pub fn new_prehashed<R: RngCore>(rng: &mut R, hashed_message: Fr, secret_key: Fr) -> Self {
15+
let g = Self::generator();
16+
// r = k * g
17+
let mut r = Affine::zero();
18+
// x coordinate of r
19+
let mut rand_x_coord = Fr::zero();
20+
// response = 1/k * (message + secret_key * rand_x_coord)
21+
let mut response = Fr::zero();
22+
// response should be invertible
23+
while r.is_zero() || rand_x_coord.is_zero() || response.is_zero() {
24+
let mut k = Fr::rand(rng);
25+
// k should be invertible
26+
while k.is_zero() {
27+
k = Fr::rand(rng);
28+
}
29+
r = (g * k).into_affine();
30+
rand_x_coord = Fr::from(r.x.into_bigint());
31+
response = k.inverse().unwrap() * (hashed_message + secret_key * rand_x_coord);
32+
}
33+
Self {
34+
rand_x_coord,
35+
response,
36+
}
37+
}
38+
39+
/// Verify the signature given that the message has already been hashed into a scalar
40+
pub fn verify_prehashed(&self, hashed_message: Fr, public_key: Affine) -> bool {
41+
let g = Self::generator();
42+
let resp_inv = if let Some(inv) = self.response.inverse() {
43+
inv
44+
} else {
45+
return false;
46+
};
47+
let gc = g * (resp_inv * hashed_message);
48+
let yr = public_key * (resp_inv * self.rand_x_coord);
49+
self.rand_x_coord == Fr::from((gc + yr).into_affine().x.into_bigint())
50+
}
51+
52+
/// Chosen generator of the group
53+
pub fn generator() -> Affine {
54+
Affine::new_unchecked(G_GENERATOR_X, G_GENERATOR_Y)
55+
}
56+
}
57+
58+
#[cfg(test)]
59+
mod tests {
60+
use super::*;
61+
use ark_std::rand::{rngs::StdRng, SeedableRng};
62+
63+
#[test]
64+
fn sig_verify() {
65+
let mut rng = StdRng::seed_from_u64(0u64);
66+
let message = Fr::rand(&mut rng);
67+
let g = Signature::generator();
68+
let sk = Fr::rand(&mut rng);
69+
let pk = (g * sk).into_affine();
70+
let sig = Signature::new_prehashed(&mut rng, message, sk);
71+
assert!(sig.verify_prehashed(message, pk));
72+
}
73+
}

kvac/src/bbs_sharp/hol.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Protocol between the user and signer to create blinded tokens in half-offline mode. Follows protocol in
1+
//! Protocol between the user and signer to create blinded tokens in half-offline (HOL) mode. Follows protocol in
22
//! Fig. 8 but this supports batching
33
44
use super::setup::{MACParams, SecretKey, SignerPublicKey, UserPublicKey};

kvac/src/bbs_sharp/mac.rs

+59-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::{
2-
bbs_sharp::setup::{MACParams, SecretKey, SignerPublicKey, UserPublicKey},
2+
bbs_sharp::setup::{
3+
DesignatedVerifierPoKOfPublicKey, MACParams, SecretKey, SignerPublicKey, UserPublicKey,
4+
},
35
error::KVACError,
46
};
57
use ark_ec::{AffineRepr, CurveGroup};
@@ -51,6 +53,8 @@ pub struct ProofOfValidityOfMAC<G: AffineRepr> {
5153
pub sc_B: PokDiscreteLog<G>,
5254
/// For proving knowledge of secret key, i.e. `pk = g_tilde * sk`
5355
pub sc_pk: PokDiscreteLog<G>,
56+
/// If set, then its a designated verifier proof which only the user can verify
57+
pub designated_verifier_pk_proof: Option<DesignatedVerifierPoKOfPublicKey<G>>,
5458
}
5559

5660
impl<G: AffineRepr> MAC<G> {
@@ -111,12 +115,14 @@ impl<G: AffineRepr> MAC<G> {
111115
}
112116

113117
impl<G: AffineRepr> ProofOfValidityOfMAC<G> {
118+
/// If `user_public_key` is provided, then create a designated verifier proof which only the user can verify
114119
pub fn new<R: RngCore, D: Digest>(
115120
rng: &mut R,
116121
mac: &MAC<G>,
117122
secret_key: &SecretKey<G::ScalarField>,
118123
public_key: &SignerPublicKey<G>,
119124
params: impl AsRef<MACParams<G>>,
125+
user_public_key: Option<&UserPublicKey<G>>,
120126
) -> Self {
121127
let witness = secret_key.0;
122128
let blinding = G::ScalarField::rand(rng);
@@ -130,10 +136,17 @@ impl<G: AffineRepr> ProofOfValidityOfMAC<G> {
130136
.unwrap();
131137
p2.challenge_contribution(&params.g_tilde, &public_key.0, &mut challenge_bytes)
132138
.unwrap();
133-
let challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
139+
// Adjust challenge if creating designated verifier proof
140+
let mut challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
141+
let designated_verifier_pk_proof =
142+
user_public_key.map(|pk| DesignatedVerifierPoKOfPublicKey::new(rng, &pk.0, &params.g));
143+
if let Some(dvp) = &designated_verifier_pk_proof {
144+
challenge = challenge - dvp.challenge;
145+
}
134146
Self {
135147
sc_B: p1.gen_proof(&challenge),
136148
sc_pk: p2.gen_proof(&challenge),
149+
designated_verifier_pk_proof,
137150
}
138151
}
139152

@@ -160,7 +173,12 @@ impl<G: AffineRepr> ProofOfValidityOfMAC<G> {
160173
self.sc_pk
161174
.challenge_contribution(&params.g_tilde, &signer_public_key.0, &mut challenge_bytes)
162175
.unwrap();
163-
let challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
176+
let mut challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
177+
// Adjust challenge if received designated verifier proof
178+
if let Some(dvp) = &self.designated_verifier_pk_proof {
179+
dvp.verify(&user_public_key.0, &params.g)?;
180+
challenge = challenge - dvp.challenge
181+
}
164182
if !self.sc_B.verify(&B, &mac.A, &challenge) {
165183
return Err(KVACError::InvalidMACProof);
166184
}
@@ -197,8 +215,44 @@ mod tests {
197215

198216
// Signer sends the following 2 items to the user
199217
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);
218+
let proof = ProofOfValidityOfMAC::new::<_, Sha256>(
219+
&mut rng, &mac, &signer_sk, &signer_pk, &params, None,
220+
);
221+
assert!(proof.designated_verifier_pk_proof.is_none());
222+
223+
// User verifies both
224+
mac.verify(&messages, &user_pk, &signer_sk, &params)
225+
.unwrap();
226+
proof
227+
.verify::<Sha256>(&mac, &messages, &user_pk, &signer_pk, params)
228+
.unwrap();
229+
}
230+
231+
#[test]
232+
fn mac_verification_with_designated_verifier_proof_of_validity() {
233+
let mut rng = StdRng::seed_from_u64(0u64);
234+
let message_count = 10;
235+
let messages = (0..message_count)
236+
.map(|_| Fr::rand(&mut rng))
237+
.collect::<Vec<_>>();
238+
let params = MACParams::<Affine>::new::<Sha256>(b"test", message_count);
239+
let signer_sk = SecretKey::new(&mut rng);
240+
let signer_pk = SignerPublicKey::new_from_params(&signer_sk, &params);
241+
242+
let user_sk = SecretKey::new(&mut rng);
243+
let user_pk = UserPublicKey::new_from_params(&user_sk, &params);
244+
245+
// Signer sends the following 2 items to the user
246+
let mac = MAC::new(&mut rng, &messages, &user_pk, &signer_sk, &params).unwrap();
247+
let proof = ProofOfValidityOfMAC::new::<_, Sha256>(
248+
&mut rng,
249+
&mac,
250+
&signer_sk,
251+
&signer_pk,
252+
&params,
253+
Some(&user_pk),
254+
);
255+
assert!(proof.designated_verifier_pk_proof.is_some());
202256

203257
// User verifies both
204258
mac.verify(&messages, &user_pk, &signer_sk, &params)

kvac/src/bbs_sharp/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@
33
//! This assumes that the messages/attributes have already been prepared before signing, i.e. attributes are hashed
44
//! with public salts, etc and whats called `H_i` in the paper is already created.
55
//!
6-
//! Assumes that a Schnorr Signature will be generated by the user's secure hardware.
6+
//! Implements support for both Schnorr signature and ECDSA (on secp256r1) generated by the user's secure hardware.
77
//!
88
//! Implements both the offline and half-offline (HOL) mode. In the former, the verifier is either the
99
//! signer (has the secret key) or can ask the signer to verify the proof without revealing any user-specific info
1010
//! In the latter, the user needs to communicate with the signer before creating a proof and get "some helper data"
1111
//! to create a proof which the verifier can check without needing the secret key or interacting with the issuer.
1212
//! For efficiency and avoiding correlation (when signer and verifier collude), the user gets a batch of
1313
//! "helper data" to let him create several proofs.
14+
//!
15+
//! Implements designated verifier proof for both issuer's signature (proof of validity of MAC) and user's proof of
16+
//! knowledge of MAC
1417
18+
mod ecdsa;
1519
pub mod hol;
1620
pub mod mac;
1721
pub mod proof;

0 commit comments

Comments
 (0)