use super::{
commitment::BlindFactor,
keys::{BBSplusPublicKey, BBSplusSecretKey},
signature::{core_verify, BBSplusSignature},
};
use crate::{
bbsplus::{ciphersuites::BbsCiphersuite, generators::Generators},
errors::Error,
schemes::{
algorithms::BBSplus,
generics::{BlindSignature, Commitment},
},
utils::{
message::bbsplus_message::BBSplusMessage,
util::bbsplus_utils::{calculate_domain, hash_to_scalar},
},
};
use bls12_381_plus::{G1Projective, Scalar, group::Curve};
use elliptic_curve::hash2curve::ExpandMsg;
impl<CS: BbsCiphersuite> BlindSignature<BBSplus<CS>> {
pub fn blind_sign(
sk: &BBSplusSecretKey,
pk: &BBSplusPublicKey,
commitment_with_proof: Option<&[u8]>,
header: Option<&[u8]>,
messages: Option<&[Vec<u8>]>,
) -> Result<Self, Error> {
let messages = messages.unwrap_or(&[]);
let L = messages.len();
let commitment_with_proof = commitment_with_proof.unwrap_or(&[]);
let mut M: usize = commitment_with_proof.len();
if M != 0 {
M = M
.checked_sub(G1Projective::COMPRESSED_BYTES)
.ok_or(Error::InvalidCommitmentProof)?;
M = M
.checked_sub(Scalar::BYTES * 2)
.ok_or(Error::InvalidCommitmentProof)?;
M = M
.checked_div(Scalar::BYTES)
.ok_or(Error::InvalidCommitmentProof)?;
}
let generators = Generators::create::<CS>(L + 1, Some(CS::API_ID_BLIND));
let blind_generators =
Generators::create::<CS>(M + 1, Some(&[b"BLIND_", CS::API_ID_BLIND].concat()));
let commit: G1Projective = Commitment::<BBSplus<CS>>::deserialize_and_validate_commit(
Some(commitment_with_proof),
&blind_generators,
Some(CS::API_ID_BLIND)
)?;
let message_scalars: Vec<BBSplusMessage> = BBSplusMessage::messages_to_scalar::<CS>(messages, CS::API_ID_BLIND)?;
let mut B: Vec<G1Projective> = calculate_b(&generators, Some(commit), message_scalars)?;
let B_val = B.pop().unwrap();
let blind_sig = finalize_blind_sign::<CS>(
sk,
pk,
B_val,
&generators,
&blind_generators,
header,
Some(CS::API_ID_BLIND),
)?;
Ok(Self::BBSplus(blind_sig))
}
pub fn verify_blind_sign(
&self,
pk: &BBSplusPublicKey,
header: Option<&[u8]>,
messages: Option<&[Vec<u8>]>,
committed_messages: Option<&[Vec<u8>]>,
secret_prover_blind: Option<&BlindFactor>,
) -> Result<(), Error> {
let api_id: &[u8] = CS::API_ID_BLIND;
let messages = messages.unwrap_or(&[]);
let committed_messages = committed_messages.unwrap_or(&[]);
let secret_prover_blind = secret_prover_blind.unwrap_or(&BlindFactor(Scalar::ZERO));
let (message_scalars, generators) = prepare_parameters::<CS>(
Some(messages),
Some(committed_messages),
messages.len() + 1,
committed_messages.len() + 1,
Some(secret_prover_blind),
Some(api_id)
)?;
core_verify::<CS>(
pk,
self.bbsPlusBlindSignature(),
&message_scalars,
generators,
header,
Some(api_id),
)
}
pub fn A(&self) -> G1Projective {
match self {
Self::BBSplus(inner) => inner.A,
_ => panic!("Cannot happen!"),
}
}
pub fn e(&self) -> Scalar {
match self {
Self::BBSplus(inner) => inner.e,
_ => panic!("Cannot happen!"),
}
}
pub fn bbsPlusBlindSignature(&self) -> &BBSplusSignature {
match self {
Self::BBSplus(inner) => inner,
_ => panic!("Cannot happen!"),
}
}
pub fn to_bytes(&self) -> [u8; BBSplusSignature::BYTES] {
self.bbsPlusBlindSignature().to_bytes()
}
pub fn from_bytes(data: &[u8; BBSplusSignature::BYTES]) -> Result<Self, Error> {
Ok(Self::BBSplus(BBSplusSignature::from_bytes(data)?))
}
}
pub fn prepare_parameters<CS>(
messages: Option<&[Vec<u8>]>,
committed_messages: Option<&[Vec<u8>]>,
generators_number: usize,
blind_generators_number: usize,
secret_prover_blind: Option<&BlindFactor>,
api_id: Option<&[u8]>
) -> Result<(Vec<BBSplusMessage>, Generators), Error>
where
CS: BbsCiphersuite,
CS::Expander: for<'a> ExpandMsg<'a>,
{
let messages = messages.unwrap_or(&[]);
let committed_messages = committed_messages.unwrap_or(&[]);
let api_id = api_id.unwrap_or(b"");
let mut message_scalars = BBSplusMessage::messages_to_scalar::<CS>(messages, api_id)?;
let mut committed_message_scalars = Vec::<BBSplusMessage>::new();
if let Some(secret_prover_blind ) = secret_prover_blind {
committed_message_scalars.push(BBSplusMessage::new(secret_prover_blind.0));
}
committed_message_scalars.append(&mut BBSplusMessage::messages_to_scalar::<CS>(committed_messages, api_id)?);
let generators = Generators::create::<CS>(generators_number, Some(api_id));
let blind_generators = Generators::create::<CS>(
blind_generators_number,
Some(&[b"BLIND_", api_id].concat())
);
message_scalars.append(&mut committed_message_scalars);
Ok((message_scalars, generators.append(blind_generators)))
}
fn calculate_b(
generators: &Generators,
commitment: Option<G1Projective>,
message_scalars: Vec<BBSplusMessage>,
) -> Result<Vec<G1Projective>, Error> {
let commitment = commitment.unwrap_or(G1Projective::IDENTITY);
let L = message_scalars.len();
if generators.values.len() != L + 1 {
return Err(Error::InvalidNumberOfGenerators);
}
let _Q1 = generators.values[0];
let H_points = &generators.values[1..];
let mut B = generators.g1_base_point;
for i in 0..L {
B += H_points[i] * message_scalars[i].value;
}
B += commitment;
if B.is_identity().into() {
return Err(Error::G1IdentityError);
}
let mut b_value:Vec<G1Projective> = Vec::<G1Projective>::new();
b_value.push(B);
Ok(b_value)
}
pub(super) fn finalize_blind_sign<CS>(
sk: &BBSplusSecretKey,
pk: &BBSplusPublicKey,
B: G1Projective,
generators: &Generators,
blind_generators: &Generators,
header: Option<&[u8]>,
api_id: Option<&[u8]>,
) -> Result<BBSplusSignature, Error>
where
CS: BbsCiphersuite,
CS::Expander: for<'a> ExpandMsg<'a>,
{
let Q1 = generators.values[0];
let Q2 = blind_generators.values[0];
let api_id = api_id.unwrap_or(b"");
let signature_dst = [api_id, CS::H2S].concat();
let tmp_generators = [
&generators.values[1..],
core::slice::from_ref(&Q2), &blind_generators.values[1..]
]
.concat();
let domain = calculate_domain::<CS>(pk, Q1, &tmp_generators, header, Some(api_id))?;
let B = B + Q1 * domain;
let mut e_octs: Vec<u8> = Vec::new();
e_octs.extend_from_slice(&sk.to_bytes());
e_octs.extend_from_slice(&B.to_affine().to_compressed());
let e = hash_to_scalar::<CS>(&e_octs, &signature_dst)?;
let sk_e = sk.0 + e;
let sk_e_inv = Option::<Scalar>::from(sk_e.invert())
.ok_or_else(|| Error::BlindSignError("Invert scalar failed".to_owned()))?;
let A = B * sk_e_inv;
Ok(BBSplusSignature { A, e })
}
#[cfg(test)]
mod tests {
use crate::{
bbsplus::{
ciphersuites::BbsCiphersuite,
commitment::BlindFactor,
keys::{BBSplusPublicKey, BBSplusSecretKey},
},
schemes::{
algorithms::{BBSplus, BbsBls12381Sha256, BbsBls12381Shake256, Scheme},
generics::BlindSignature,
},
};
use elliptic_curve::hash2curve::ExpandMsg;
use std::fs;
macro_rules! sign_tests {
( $( ($t:ident, $p:literal): { $( ($n:ident, $f:literal), )+ },)+ ) => { $($(
#[test] fn $n() { blind_sign::<$t>($p, $f); }
)+)+ }
}
sign_tests! {
(BbsBls12381Sha256, "./fixture_data/fixture_data_blind/bls12-381-sha-256/"): {
(blind_sign_sha256_1, "signature/signature001.json"),
(blind_sign_sha256_2, "signature/signature002.json"),
(blind_sign_sha256_3, "signature/signature003.json"),
(blind_sign_sha256_4, "signature/signature004.json"),
(blind_sign_sha256_5, "signature/signature005.json"),
},
(BbsBls12381Shake256, "./fixture_data/fixture_data_blind/bls12-381-shake-256/"): {
(blind_sign_shake256_1, "signature/signature001.json"),
(blind_sign_shake256_2, "signature/signature002.json"),
(blind_sign_shake256_3, "signature/signature003.json"),
(blind_sign_shake256_4, "signature/signature004.json"),
(blind_sign_shake256_5, "signature/signature005.json"),
},
}
fn blind_sign<S: Scheme>(pathname: &str, filename: &str)
where
S::Ciphersuite: BbsCiphersuite,
<S::Ciphersuite as BbsCiphersuite>::Expander: for<'a> ExpandMsg<'a>,
{
let data = fs::read_to_string([pathname, filename].concat()).expect("Unable to read file");
let proof_json: serde_json::Value = serde_json::from_str(&data).expect("Unable to parse");
println!("{}", proof_json["caseName"]);
let sk_hex = proof_json["signerKeyPair"]["secretKey"].as_str().unwrap();
let pk_hex = proof_json["signerKeyPair"]["publicKey"].as_str().unwrap();
let sk = BBSplusSecretKey::from_bytes(&hex::decode(sk_hex).unwrap()).unwrap();
let pk = BBSplusPublicKey::from_bytes(&hex::decode(pk_hex).unwrap()).unwrap();
let committed_messages: Option<Vec<String>> =
proof_json["committedMessages"].as_array().and_then(|cm| {
cm.iter()
.map(|m| serde_json::from_value(m.clone()).unwrap())
.collect()
});
let prover_blind = proof_json["proverBlind"].as_str().map(|b| {
BlindFactor::from_bytes(&hex::decode(b).unwrap().try_into().unwrap()).unwrap()
});
let commitment_with_proof = proof_json["commitmentWithProof"]
.as_str()
.map(|c| hex::decode(c).unwrap());
let committed_messages: Option<Vec<Vec<u8>>> = match committed_messages {
Some(cm) => Some(cm.iter().map(|m| hex::decode(m).unwrap()).collect()),
None => None,
};
let header = hex::decode(proof_json["header"].as_str().unwrap()).unwrap();
let messages: Vec<String> = proof_json["messages"]
.as_array()
.unwrap()
.iter()
.map(|m| serde_json::from_value(m.clone()).unwrap())
.collect();
let messages: Vec<Vec<u8>> = messages.iter().map(|m| hex::decode(m).unwrap()).collect();
let signature = BlindSignature::<BBSplus<S::Ciphersuite>>::blind_sign(
&sk,
&pk,
commitment_with_proof.as_deref(),
Some(&header),
Some(&messages),
)
.unwrap();
let expected_signature = proof_json["signature"].as_str().unwrap();
let signature_oct = signature.to_bytes();
assert_eq!(hex::encode(&signature_oct), expected_signature);
let result = signature
.verify_blind_sign(
&pk,
Some(&header),
Some(&messages),
committed_messages.as_deref(),
prover_blind.as_ref(),
)
.is_ok();
let expected_result = proof_json["result"]["valid"].as_bool().unwrap();
assert_eq!(result, expected_result);
}
}