Skip to content

Commit

Permalink
Add ECDSA and designated verifier proof support in BBS-sharp, test to…
Browse files Browse the repository at this point in the history
… show BBS-sharp works with keyed-verification accumulator and relaxing the type of accumulator from Pairing to Affine

Signed-off-by: lovesh <[email protected]>
  • Loading branch information
lovesh committed Jul 27, 2024
1 parent ed791bb commit 6c57e76
Show file tree
Hide file tree
Showing 34 changed files with 1,817 additions and 762 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ serde_with = { version = "1.10.0", default-features = false, features = ["macros
zeroize = { version = "1.7.0", features = ["derive"] }
blake2 = { version = "0.10", default-features = false }
ark-bls12-381 = { version = "^0.4.0", default-features = false, features = [ "curve" ] }
ark-secp256r1 = { version = "^0.4.0", default-features = false }
itertools = "0.12.1"

[profile.release]
Expand Down
2 changes: 1 addition & 1 deletion kvac/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ serde_with.workspace = true
itertools.workspace = true
dock_crypto_utils = { version = "0.20.0", default-features = false, path = "../utils" }
schnorr_pok = { version = "0.20.0", default-features = false, path = "../schnorr_pok" }
ark-secp256r1.workspace = true

[dev-dependencies]
blake2.workspace = true
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]
Expand Down
6 changes: 4 additions & 2 deletions kvac/src/bbs_sharp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ BBS# as described [here](https://github.com/user-attachments/files/15905230/BBS_
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 support for both Schnorr signature and ECDSA (on secp256r1) 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.
"helper data" to let him create several proofs.

Implements designated verifier proof for both issuer's signature (proof of validity of MAC) and user's proof of knowledge of MAC
73 changes: 73 additions & 0 deletions kvac/src/bbs_sharp/ecdsa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::{Field, PrimeField};
use ark_secp256r1::{Affine, Fr, G_GENERATOR_X, G_GENERATOR_Y};
use ark_std::{rand::RngCore, UniformRand, Zero};

/// ECDSA signature
pub struct Signature {
pub rand_x_coord: Fr,
pub response: Fr,
}

impl Signature {
/// Create new signature given that the message has already been hashed into a scalar
pub fn new_prehashed<R: RngCore>(rng: &mut R, hashed_message: Fr, secret_key: Fr) -> Self {
let g = Self::generator();
// r = k * g
let mut r = Affine::zero();
// x coordinate of r
let mut rand_x_coord = Fr::zero();
// response = 1/k * (message + secret_key * rand_x_coord)
let mut response = Fr::zero();
// response should be invertible
while r.is_zero() || rand_x_coord.is_zero() || response.is_zero() {
let mut k = Fr::rand(rng);
// k should be invertible
while k.is_zero() {
k = Fr::rand(rng);
}
r = (g * k).into_affine();
rand_x_coord = Fr::from(r.x.into_bigint());
response = k.inverse().unwrap() * (hashed_message + secret_key * rand_x_coord);
}
Self {
rand_x_coord,
response,
}
}

/// Verify the signature given that the message has already been hashed into a scalar
pub fn verify_prehashed(&self, hashed_message: Fr, public_key: Affine) -> bool {
let g = Self::generator();
let resp_inv = if let Some(inv) = self.response.inverse() {
inv
} else {
return false;
};
let gc = g * (resp_inv * hashed_message);
let yr = public_key * (resp_inv * self.rand_x_coord);
self.rand_x_coord == Fr::from((gc + yr).into_affine().x.into_bigint())
}

/// Chosen generator of the group
pub fn generator() -> Affine {
Affine::new_unchecked(G_GENERATOR_X, G_GENERATOR_Y)
}
}

#[cfg(test)]
mod tests {
use super::*;
use ark_std::rand::{rngs::StdRng, SeedableRng};

#[test]
fn sig_verify() {
let mut rng = StdRng::seed_from_u64(0u64);
let message = Fr::rand(&mut rng);
let g = Signature::generator();
let sk = Fr::rand(&mut rng);
let pk = (g * sk).into_affine();
let sig = Signature::new_prehashed(&mut rng, message, sk);
assert!(sig.verify_prehashed(message, pk));
}
}
2 changes: 1 addition & 1 deletion kvac/src/bbs_sharp/hol.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Protocol between the user and signer to create blinded tokens in half-offline mode. Follows protocol in
//! Protocol between the user and signer to create blinded tokens in half-offline (HOL) mode. Follows protocol in
//! Fig. 8 but this supports batching

use super::setup::{MACParams, SecretKey, SignerPublicKey, UserPublicKey};
Expand Down
64 changes: 59 additions & 5 deletions kvac/src/bbs_sharp/mac.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::{
bbs_sharp::setup::{MACParams, SecretKey, SignerPublicKey, UserPublicKey},
bbs_sharp::setup::{
DesignatedVerifierPoKOfPublicKey, MACParams, SecretKey, SignerPublicKey, UserPublicKey,
},
error::KVACError,
};
use ark_ec::{AffineRepr, CurveGroup};
Expand Down Expand Up @@ -51,6 +53,8 @@ pub struct ProofOfValidityOfMAC<G: AffineRepr> {
pub sc_B: PokDiscreteLog<G>,
/// For proving knowledge of secret key, i.e. `pk = g_tilde * sk`
pub sc_pk: PokDiscreteLog<G>,
/// If set, then its a designated verifier proof which only the user can verify
pub designated_verifier_pk_proof: Option<DesignatedVerifierPoKOfPublicKey<G>>,
}

impl<G: AffineRepr> MAC<G> {
Expand Down Expand Up @@ -111,12 +115,14 @@ impl<G: AffineRepr> MAC<G> {
}

impl<G: AffineRepr> ProofOfValidityOfMAC<G> {
/// If `user_public_key` is provided, then create a designated verifier proof which only the user can verify
pub fn new<R: RngCore, D: Digest>(
rng: &mut R,
mac: &MAC<G>,
secret_key: &SecretKey<G::ScalarField>,
public_key: &SignerPublicKey<G>,
params: impl AsRef<MACParams<G>>,
user_public_key: Option<&UserPublicKey<G>>,
) -> Self {
let witness = secret_key.0;
let blinding = G::ScalarField::rand(rng);
Expand All @@ -130,10 +136,17 @@ impl<G: AffineRepr> ProofOfValidityOfMAC<G> {
.unwrap();
p2.challenge_contribution(&params.g_tilde, &public_key.0, &mut challenge_bytes)
.unwrap();
let challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
// Adjust challenge if creating designated verifier proof
let mut challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
let designated_verifier_pk_proof =
user_public_key.map(|pk| DesignatedVerifierPoKOfPublicKey::new(rng, &pk.0, &params.g));
if let Some(dvp) = &designated_verifier_pk_proof {
challenge = challenge - dvp.challenge;
}
Self {
sc_B: p1.gen_proof(&challenge),
sc_pk: p2.gen_proof(&challenge),
designated_verifier_pk_proof,
}
}

Expand All @@ -160,7 +173,12 @@ impl<G: AffineRepr> ProofOfValidityOfMAC<G> {
self.sc_pk
.challenge_contribution(&params.g_tilde, &signer_public_key.0, &mut challenge_bytes)
.unwrap();
let challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
let mut challenge = compute_random_oracle_challenge::<G::ScalarField, D>(&challenge_bytes);
// Adjust challenge if received designated verifier proof
if let Some(dvp) = &self.designated_verifier_pk_proof {
dvp.verify(&user_public_key.0, &params.g)?;
challenge = challenge - dvp.challenge
}
if !self.sc_B.verify(&B, &mac.A, &challenge) {
return Err(KVACError::InvalidMACProof);
}
Expand Down Expand Up @@ -197,8 +215,44 @@ mod tests {

// 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);
let proof = ProofOfValidityOfMAC::new::<_, Sha256>(
&mut rng, &mac, &signer_sk, &signer_pk, &params, None,
);
assert!(proof.designated_verifier_pk_proof.is_none());

// User verifies both
mac.verify(&messages, &user_pk, &signer_sk, &params)
.unwrap();
proof
.verify::<Sha256>(&mac, &messages, &user_pk, &signer_pk, params)
.unwrap();
}

#[test]
fn mac_verification_with_designated_verifier_proof_of_validity() {
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 = SignerPublicKey::new_from_params(&signer_sk, &params);

let user_sk = SecretKey::new(&mut rng);
let user_pk = UserPublicKey::new_from_params(&user_sk, &params);

// 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,
Some(&user_pk),
);
assert!(proof.designated_verifier_pk_proof.is_some());

// User verifies both
mac.verify(&messages, &user_pk, &signer_sk, &params)
Expand Down
6 changes: 5 additions & 1 deletion kvac/src/bbs_sharp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
//! 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 support for both Schnorr signature and ECDSA (on secp256r1) 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.
//!
//! Implements designated verifier proof for both issuer's signature (proof of validity of MAC) and user's proof of
//! knowledge of MAC

mod ecdsa;
pub mod hol;
pub mod mac;
pub mod proof;
Expand Down
Loading

0 comments on commit 6c57e76

Please sign in to comment.