use super::CredentialBuilder;
use crate::ec::curve::pairing;
use crate::ec::Scalar;
use crate::entry::entry_to_scalar;
use crate::entry::{Entry, MaxEntries};
use crate::set_commits::{Commitment, CrossSetCommitment};
use crate::set_commits::{ParamSetCommitment, ParamSetCommitmentCompressed};
use crate::utils::{try_decompress_g1, try_decompress_g2, try_into_scalar};
use crate::zkp::{DamgardTransformCompressed, Nonce, PedersenOpenCompressed};
use crate::{config, error, CredentialCompressed};
use crate::{
zkp::PedersenOpen,
zkp::Schnorr,
zkp::{ChallengeState, DamgardTransform},
};
use anyhow::Result;
pub use bls12_381_plus::elliptic_curve::bigint;
use bls12_381_plus::elliptic_curve::ops::MulByGenerator;
use bls12_381_plus::ff::Field;
use bls12_381_plus::group::{Curve, Group, GroupEncoding};
use bls12_381_plus::{G1Affine, G1Projective, G2Affine, G2Projective, Gt};
pub use delano_keys::vk::{VKCompressed, VK};
use rand::rngs::ThreadRng;
pub use secrecy::{ExposeSecret, Secret};
use serde::{Deserialize, Serialize};
use spseq_uc::Credential;
use spseq_uc::UpdateError;
use std::fmt::Display;
use std::ops::Mul;
use std::ops::{Deref, Neg};
use std::str::FromStr;
pub mod spseq_uc;
#[derive(Debug, Clone, PartialEq)]
pub struct MaxCardinality(pub usize);
impl From<&usize> for MaxCardinality {
fn from(item: &usize) -> Self {
MaxCardinality(*item)
}
}
impl From<usize> for MaxCardinality {
fn from(item: usize) -> Self {
MaxCardinality(item)
}
}
impl From<u8> for MaxCardinality {
fn from(item: u8) -> Self {
MaxCardinality(item as usize)
}
}
impl From<MaxCardinality> for usize {
fn from(item: MaxCardinality) -> Self {
item.0
}
}
impl Deref for MaxCardinality {
type Target = usize;
fn deref(&self) -> &usize {
&self.0
}
}
impl Default for MaxCardinality {
fn default() -> Self {
MaxCardinality(config::DEFAULT_MAX_CARDINALITY)
}
}
impl MaxCardinality {
pub fn new(item: usize) -> Self {
MaxCardinality(item)
}
}
#[derive(Debug, Clone)]
pub enum IssuerError {
TooLargeCardinality,
TooLongEntries,
InvalidNymProof,
UpdateError(String),
}
impl std::error::Error for IssuerError {}
impl std::fmt::Display for IssuerError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
IssuerError::TooLargeCardinality => write!(f, "TooLargeCardinality. You passed too many attributes per Entry. Hint: reduce the number of attributes to be less than the max cardinality of this Issuer."),
IssuerError::TooLongEntries => write!(f, "TooLongEntries. You passed too many Entries. Hint: reduce the number of Entries to be less than the max entries of this Issuer."),
IssuerError::InvalidNymProof => write!(f, "InvalidNymProof. The proof of the pseudonym is invalid."),
IssuerError::UpdateError(e) => write!(f, "UpdateError: {}", e), }
}
}
impl From<UpdateError> for IssuerError {
fn from(item: UpdateError) -> Self {
IssuerError::UpdateError(item.to_string())
}
}
pub struct Issuer {
pub public: IssuerPublic,
sk: Secret<Vec<Scalar>>,
}
pub trait CBORCodec {
fn to_cbor(&self) -> Result<Vec<u8>, error::Error>
where
Self: Sized + Serialize,
{
let mut bytes = Vec::new();
match ciborium::into_writer(&self, &mut bytes) {
Ok(_) => Ok(bytes),
Err(e) => Err(error::Error::CBORError(format!(
"CBORCodec Error serializing to bytes: {}",
e
))),
}
}
fn from_cbor(bytes: &[u8]) -> Result<Self, error::Error>
where
for<'a> Self: Sized + Deserialize<'a>,
{
match ciborium::from_reader(&bytes[..]) {
Ok(item) => Ok(item),
Err(e) => Err(error::Error::CBORError(format!(
"CBORCodec Error deserializing from bytes: {}",
e
))),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct IssuerPublic {
pub parameters: ParamSetCommitment,
pub vk: Vec<VK>,
}
impl CBORCodec for IssuerPublicCompressed {}
impl IssuerPublic {
pub fn to_compact(&self) -> IssuerPublicCompressed {
let vk_b64 = self
.vk
.clone()
.iter()
.take(2)
.map(|vk| match vk {
VK::G1(vk) => VKCompressed::G1(vk.to_compressed().to_vec()),
VK::G2(vk) => VKCompressed::G2(vk.to_compressed().to_vec()),
})
.collect::<Vec<_>>();
IssuerPublicCompressed {
vk: vk_b64,
parameters: self.parameters.clone().into(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct IssuerPublicCompressed {
pub parameters: ParamSetCommitmentCompressed,
pub vk: Vec<VKCompressed>,
}
#[cfg(feature = "serde_json")]
impl ToString for IssuerPublicCompressed {
fn to_string(&self) -> String {
serde_json::to_string(&self).unwrap()
}
}
impl From<IssuerPublic> for IssuerPublicCompressed {
fn from(item: IssuerPublic) -> Self {
Self {
parameters: item.parameters.into(),
vk: item.vk.iter().map(|vk| vk.into()).collect::<Vec<_>>(),
}
}
}
impl From<&IssuerPublic> for IssuerPublicCompressed {
fn from(item: &IssuerPublic) -> Self {
Self {
parameters: item.parameters.clone().into(),
vk: item.vk.iter().map(|vk| vk.into()).collect::<Vec<_>>(),
}
}
}
impl TryFrom<IssuerPublicCompressed> for IssuerPublic {
type Error = error::Error;
fn try_from(item: IssuerPublicCompressed) -> Result<Self, Self::Error> {
let vk = item
.vk
.iter()
.map(|vk| match vk {
VKCompressed::G1(vk_g1) => {
let mut bytes = [0u8; G1Affine::COMPRESSED_BYTES];
bytes.copy_from_slice(&vk_g1);
let vk_g1_maybe = G1Affine::from_compressed(&bytes);
if vk_g1_maybe.is_none().into() {
return Err(error::Error::InvalidG1Point);
}
Ok(VK::G1(G1Projective::from(vk_g1_maybe.unwrap())))
}
VKCompressed::G2(vk_g2) => {
let mut bytes = [0u8; G2Affine::COMPRESSED_BYTES];
bytes.copy_from_slice(&vk_g2);
let vk_g2_maybe = G2Affine::from_compressed(&bytes);
if vk_g2_maybe.is_none().into() {
return Err(error::Error::InvalidG2Point);
}
Ok(VK::G2(G2Projective::from(vk_g2_maybe.unwrap())))
}
})
.collect::<Result<Vec<_>, Self::Error>>()?;
Ok(Self {
parameters: item.parameters.try_into()?,
vk,
})
}
}
impl Default for Issuer {
fn default() -> Self {
Self::new(MaxCardinality::default(), MaxEntries::default())
}
}
impl Issuer {
pub fn new(t: MaxCardinality, l_message: MaxEntries) -> Self {
let sk = Secret::new(
(0..l_message.0 + 2)
.map(|_| Scalar::random(ThreadRng::default()))
.collect::<Vec<_>>(),
);
Self::new_with_secret(sk, t)
}
pub fn new_with_secret(sk: Secret<Vec<Scalar>>, t: MaxCardinality) -> Self {
let public_parameters = ParamSetCommitment::new(&t);
Self::new_with_params(sk, public_parameters)
}
pub fn new_with_params(sk: Secret<Vec<Scalar>>, params: ParamSetCommitment) -> Self {
let mut vk: Vec<VK> = sk
.expose_secret()
.iter()
.map(|sk_i| VK::G2(G2Projective::mul_by_generator(sk_i)))
.collect::<Vec<_>>();
let x_0 = G1Projective::mul_by_generator(&sk.expose_secret()[0]);
vk.insert(0, VK::G1(x_0));
Self {
sk,
public: IssuerPublic {
parameters: params,
vk,
},
}
}
pub fn credential(&self) -> CredentialBuilder {
CredentialBuilder::new(self)
}
pub fn issue_cred(
&self,
attr_vector: &[Entry],
k_prime: Option<usize>,
nym_proof: &NymProof,
nonce: Option<&Nonce>,
) -> Result<Credential, IssuerError> {
if !DamgardTransform::verify(nym_proof, nonce) {
return Err(IssuerError::InvalidNymProof);
}
let cred = self.sign(&nym_proof.public_key.into(), attr_vector, k_prime)?;
assert!(verify(
&self.public.vk,
&nym_proof.public_key.into(),
&cred.commitment_vector,
&cred.sigma
));
Ok(cred)
}
fn sign(
&self,
pk_u: &G1Projective,
messages_vector: &[Entry],
k_prime: Option<usize>,
) -> Result<Credential, IssuerError> {
if messages_vector
.iter()
.any(|mess| mess.len() > self.public.parameters.pp_commit_g1.len())
{
return Err(IssuerError::TooLargeCardinality);
}
if messages_vector.len() >= self.public.vk.len() - 2 {
return Err(IssuerError::TooLongEntries);
}
let (commitment_vector, opening_vector): (Vec<G1Projective>, Vec<Scalar>) = messages_vector
.iter()
.map(|mess| CrossSetCommitment::commit_set(&self.public.parameters, mess))
.collect::<Vec<_>>()
.into_iter()
.unzip();
let y_rand = Scalar::random(ThreadRng::default());
let list_z = commitment_vector
.iter()
.enumerate()
.map(|(i, c)| c * self.sk.expose_secret()[i + 2])
.collect::<Vec<_>>();
let temp_point = list_z
.iter()
.fold(G1Projective::identity(), |acc, x| acc + x);
let z = y_rand.invert().unwrap() * temp_point;
let y_g1 = G1Projective::mul_by_generator(&y_rand);
let y_hat = G2Projective::mul_by_generator(&y_rand);
let t = y_g1 * self.sk.expose_secret()[1] + pk_u * self.sk.expose_secret()[0];
let sigma = Signature { z, y_g1, y_hat, t };
let mut update_key = None;
if let Some(k_prime) = k_prime {
if k_prime > messages_vector.len() {
let k_prime = k_prime.min(self.sk.expose_secret().len() - 2);
let mut usign = Vec::new();
usign.resize(k_prime, Vec::new());
for k in (messages_vector.len() + 1)..=k_prime {
let mut uk = Vec::new();
for i in 0..self.public.parameters.pp_commit_g1.len() {
let uk_i = self.public.parameters.pp_commit_g1[i]
* y_rand.invert().unwrap()
* self.sk.expose_secret()[k + 1]; uk.push(uk_i);
}
usign[k - 1] = uk; }
update_key = Some(usign);
return Ok(Credential {
sigma,
update_key,
commitment_vector,
opening_vector,
issuer_public: self.public.clone(),
});
}
}
Ok(Credential {
sigma,
update_key,
commitment_vector,
opening_vector,
issuer_public: self.public.clone(),
})
}
}
pub fn verify(
vk: &[VK],
pk_u: &G1Projective,
commitment_vector: &[G1Projective],
sigma: &Signature,
) -> bool {
let g_1 = &G1Projective::GENERATOR;
let g_2 = &G2Projective::GENERATOR;
let Signature { z, y_g1, y_hat, t } = sigma;
let pairing_op = commitment_vector
.iter()
.zip(vk.iter().skip(3))
.map(|(c, vkj3)| {
if let VK::G2(vkj3) = vkj3 {
pairing(c, vkj3)
} else {
panic!("Invalid verification key");
}
})
.collect::<Vec<_>>();
if let VK::G2(vk2) = &vk[2] {
if let VK::G2(vk1) = &vk[1] {
let a = pairing(y_g1, g_2) == pairing(g_1, y_hat);
let b = pairing(t, g_2) == pairing(y_g1, vk2) * pairing(pk_u, vk1);
let c = pairing(z, y_hat) == pairing_op.iter().fold(Gt::IDENTITY, Gt::mul);
a && b && c
} else {
panic!("Invalid verification key");
}
} else {
panic!("Invalid verification key");
}
}
pub struct Initial;
pub struct Randomized;
pub struct Nym<Stage> {
secret: Secret<Scalar>,
pub public: NymPublic,
stage: std::marker::PhantomData<Stage>,
}
#[derive(Clone)]
pub struct NymPublic {
pub damgard: DamgardTransform,
pub key: G1Projective,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NymProof {
pub challenge: Scalar,
pub pedersen_open: PedersenOpen,
pub pedersen_commit: G1Affine,
pub public_key: G1Affine,
pub response: Scalar,
pub damgard: DamgardTransform,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NymProofCompressed {
pub challenge: Vec<u8>,
pub pedersen_open: PedersenOpenCompressed,
pub pedersen_commit: Vec<u8>,
pub public_key: Vec<u8>,
pub response: Vec<u8>,
pub damgard: DamgardTransformCompressed,
}
impl CBORCodec for NymProofCompressed {}
impl From<NymProof> for NymProofCompressed {
fn from(item: NymProof) -> Self {
Self {
challenge: item.challenge.into(),
pedersen_open: PedersenOpenCompressed::from(item.pedersen_open),
pedersen_commit: item.pedersen_commit.to_compressed().to_vec(),
public_key: item.public_key.to_compressed().to_vec(),
response: item.response.into(),
damgard: DamgardTransformCompressed::from(item.damgard),
}
}
}
impl TryFrom<NymProofCompressed> for NymProof {
type Error = error::Error;
fn try_from(item: NymProofCompressed) -> Result<Self, Self::Error> {
let challenge = try_into_scalar(item.challenge)?;
let response = try_into_scalar(item.response)?;
let damgard = DamgardTransform::try_from(item.damgard)?;
let pedersen_open = PedersenOpen::try_from(item.pedersen_open)?;
let pedersen_commit = {
let mut bytes = [0u8; G1Affine::COMPRESSED_BYTES];
bytes.copy_from_slice(&item.pedersen_commit);
let maybe_g1 = G1Affine::from_compressed(&bytes);
if maybe_g1.is_none().into() {
return Err(error::Error::InvalidG1Point);
} else {
G1Projective::from(maybe_g1.unwrap())
}
};
let public_key = {
let mut bytes = [0u8; G1Affine::COMPRESSED_BYTES];
bytes.copy_from_slice(&item.public_key);
let maybe_g1 = G1Affine::from_compressed(&bytes);
if maybe_g1.is_none().into() {
return Err(error::Error::InvalidG1Point);
} else {
G1Projective::from(maybe_g1.unwrap())
}
};
Ok(Self {
challenge,
pedersen_open,
pedersen_commit: pedersen_commit.into(),
public_key: public_key.into(),
response,
damgard,
})
}
}
impl<Stage> Nym<Stage> {
fn new_from_secret(secret_bytes: Secret<Scalar>) -> Nym<Stage> {
let key = G1Projective::mul_by_generator(secret_bytes.expose_secret());
Nym::<Stage> {
stage: std::marker::PhantomData,
secret: secret_bytes,
public: NymPublic {
damgard: DamgardTransform::new(),
key,
},
}
}
pub fn randomize(&self) -> Nym<Randomized> {
let psi = Scalar::random(ThreadRng::default());
let chi = Scalar::random(ThreadRng::default());
let secret_wit = Secret::new((self.secret.expose_secret() + chi) * psi);
Nym::new_from_secret(secret_wit)
}
pub fn offer_builder<'a>(
&'a self,
cred: &'a Credential,
entries: &[Entry],
) -> super::OfferBuilder<Stage> {
super::OfferBuilder::new(self, cred, entries)
}
pub fn proof_builder<'a>(
&'a self,
cred: &'a Credential,
entries: &'a [Entry],
) -> super::ProofBuilder<Stage> {
super::ProofBuilder::new(self, cred, entries)
}
pub fn extend(
&self,
cred: &Credential,
addl_attrs: &Entry,
) -> Result<Credential, error::Error> {
let mu = Scalar::ONE;
let cred = spseq_uc::change_rel(
&cred.issuer_public.parameters,
addl_attrs,
cred.clone(),
&mu,
)
.map_err(|e| {
error::Error::ChangeRelationsFailed(format!("Change Relations Failed: {}", e))
})?;
Ok(cred)
}
pub fn offer(
&self,
cred: &Credential,
addl_attrs: &Option<Entry>,
) -> Result<Offer, error::Error> {
let mu = Scalar::ONE;
let psi = Scalar::random(ThreadRng::default());
let (nym_p_pk, cred_prime, chi) =
spseq_uc::change_rep(&self.public.key, cred, &mu, &psi, true);
let nym = Nym::from_components(&self.secret, Chi(chi), Psi(psi));
let mut cred_prime = cred_prime;
if let Some(addl_attrs) = addl_attrs {
cred_prime = match spseq_uc::change_rel(
&cred.issuer_public.parameters,
addl_attrs,
cred_prime.clone(),
&mu,
) {
Ok(cred_pushed) => cred_pushed,
Err(e) => {
return Err(error::Error::ChangeRelationsFailed(format!(
"Change Relations Failed: {}",
e
)))
}
};
}
if !verify(
&cred_prime.issuer_public.vk,
&nym_p_pk,
&cred_prime.commitment_vector,
&cred_prime.sigma,
) {
return Err(error::Error::InvalidSignature(
"Credential Signature is not valid for the new Nym".into(),
));
}
let orphan = nym.send_convert_sig(&cred_prime.issuer_public.vk, cred_prime.sigma.clone());
Ok(Offer(Credential {
sigma: orphan,
..cred_prime
}))
}
pub fn accept(
&self,
offer: &Offer, ) -> Result<Credential, crate::error::Error> {
let sigma_new = self.receive_cred(&offer.issuer_public.vk, offer.sigma.clone())?;
if !verify(
&offer.issuer_public.vk,
&self.public.key,
&offer.commitment_vector,
&sigma_new,
) {
return Err(error::Error::AcceptOfferFailed(String::from(
"Invalid Signature",
)));
}
Ok(Credential {
sigma: sigma_new,
..offer.clone().into()
})
}
fn receive_cred(&self, vk: &[VK], mut orphan: Signature) -> Result<Signature, error::Error> {
match &vk[0] {
VK::G1(vk0) => {
orphan.t += vk0 * self.secret.expose_secret();
Ok(orphan)
}
_ => Err(error::Error::InvalidVerificationKey {
expected: String::from("G1"),
found: String::from("G2"),
}),
}
}
pub fn prove(
&self,
cred: &Credential,
all_attributes: &[Entry],
selected_attrs: &[Entry],
nonce: &Nonce,
) -> CredProof {
let mu = Scalar::random(ThreadRng::default());
let psi = Scalar::random(ThreadRng::default());
let (_nym_p, cred_p, chi) = spseq_uc::change_rep(&self.public.key, cred, &mu, &psi, false);
let nym: Nym<Randomized> = Nym::from_components(&self.secret, Chi(chi), Psi(psi));
let (witness_vector, commit_vector) = selected_attrs
.iter()
.enumerate()
.filter(|(_, selected_attr)| !selected_attr.is_empty()) .fold(
(Vec::new(), Vec::new()),
|(mut witness, mut commitment_vectors), (i, selected_attr)| {
if let Some(opened) = CrossSetCommitment::open_subset(
&cred.issuer_public.parameters,
&all_attributes[i],
&cred_p.opening_vector[i],
selected_attr,
) {
witness.push(opened); commitment_vectors.push(cred_p.commitment_vector[i]);
}
(witness, commitment_vectors)
},
);
let witness_pi = CrossSetCommitment::aggregate_cross(&witness_vector, &commit_vector);
CredProof {
sigma: cred_p.sigma,
commitment_vector: cred_p
.commitment_vector
.iter()
.map(|c| c.to_affine())
.collect(),
witness_pi: witness_pi.into(),
nym_proof: nym.nym_proof(nonce),
}
}
pub fn nym_proof(&self, nonce: &Nonce) -> NymProof {
let (pedersen_commit, pedersen_open) = self.public.damgard.announce(nonce);
let state = ChallengeState::new(
vec![self.public.damgard.pedersen.h.to_affine()],
&pedersen_commit.to_bytes(),
);
let challenge = DamgardTransform::challenge(&state);
let response = DamgardTransform::response(
&challenge,
&pedersen_open.announce_randomness,
&self.public.key.into(),
&self.secret,
);
NymProof {
challenge,
pedersen_open,
pedersen_commit,
public_key: self.public.key.into(),
response,
damgard: self.public.damgard.clone(),
}
}
}
impl Default for Nym<Randomized> {
fn default() -> Self {
Self::new()
}
}
struct Chi(Scalar);
impl Deref for Chi {
type Target = Scalar;
fn deref(&self) -> &Self::Target {
&self.0
}
}
struct Psi(Scalar);
impl Deref for Psi {
type Target = Scalar;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Nym<Initial> {
pub fn from_secret(secret_bytes: Secret<Scalar>) -> Self {
Nym::new_from_secret(secret_bytes)
}
}
impl Nym<Randomized> {
pub fn new() -> Self {
let secret = Secret::new(Scalar::random(ThreadRng::default()));
Nym::new_from_secret(secret)
}
fn from_components(secret: &Secret<Scalar>, chi: Chi, psi: Psi) -> Self {
let secret_wit = Secret::new((secret.expose_secret() + (*chi)) * (*psi));
Nym::new_from_secret(secret_wit)
}
fn send_convert_sig(&self, vk: &[VK], mut sigma: Signature) -> Signature {
if let VK::G1(vk0) = &vk[0] {
sigma.t += (vk0 * self.secret.expose_secret()).neg();
sigma
} else {
panic!("Invalid verification key"); }
}
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct Signature {
z: G1Projective,
y_g1: G1Projective,
y_hat: G2Projective,
t: G1Projective,
}
impl Display for Signature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let sig_compressed = SignatureCompressed::from(self.clone());
let sig_json = serde_json::to_string(&sig_compressed).unwrap();
write!(f, "{}", sig_json)
}
}
impl FromStr for Signature {
type Err = error::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let sig_compressed: SignatureCompressed = serde_json::from_str(s)?;
Signature::try_from(sig_compressed)
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SignatureCompressed {
pub z: Vec<u8>,
pub y_g1: Vec<u8>,
pub y_hat: Vec<u8>,
pub t: Vec<u8>,
}
impl From<Signature> for SignatureCompressed {
fn from(sig: Signature) -> Self {
SignatureCompressed {
z: sig.z.to_bytes().into(),
y_g1: sig.y_g1.to_bytes().into(),
y_hat: sig.y_hat.to_bytes().into(),
t: sig.t.to_bytes().into(),
}
}
}
impl TryFrom<SignatureCompressed> for Signature {
type Error = error::Error;
fn try_from(sig: SignatureCompressed) -> Result<Self, Self::Error> {
let z = try_decompress_g1(sig.z)?.into();
let y_g1 = try_decompress_g1(sig.y_g1)?.into();
let y_hat = try_decompress_g2(sig.y_hat)?.into();
let t = try_decompress_g1(sig.t)?.into();
Ok(Signature { z, y_g1, y_hat, t })
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Offer(Credential);
impl AsRef<Credential> for Offer {
fn as_ref(&self) -> &Credential {
&self.0
}
}
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OfferCompressed(CredentialCompressed);
impl CBORCodec for OfferCompressed {}
impl Deref for Offer {
type Target = Credential;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Offer> for OfferCompressed {
fn from(offer: Offer) -> Self {
OfferCompressed(offer.0.into())
}
}
impl TryFrom<OfferCompressed> for Offer {
type Error = error::Error;
fn try_from(offer: OfferCompressed) -> Result<Self, Self::Error> {
Ok(Offer(offer.0.try_into()?))
}
}
impl TryFrom<CredentialCompressed> for Offer {
type Error = error::Error;
fn try_from(cred: CredentialCompressed) -> Result<Self, Self::Error> {
Ok(Offer(cred.try_into()?))
}
}
impl From<CredentialCompressed> for OfferCompressed {
fn from(cred: CredentialCompressed) -> Self {
OfferCompressed(cred)
}
}
impl From<Offer> for Credential {
fn from(offer: Offer) -> Self {
offer.0
}
}
impl From<Credential> for Offer {
fn from(cred: Credential) -> Self {
Offer(cred)
}
}
impl From<OfferCompressed> for CredentialCompressed {
fn from(offer: OfferCompressed) -> Self {
offer.0
}
}
impl TryFrom<OfferCompressed> for Vec<u8> {
type Error = error::Error;
fn try_from(value: OfferCompressed) -> Result<Self, Self::Error> {
let cred: CredentialCompressed = value.into();
Ok(cred.to_cbor()?)
}
}
impl TryFrom<Vec<u8>> for OfferCompressed {
type Error = error::Error;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
let cred = CredentialCompressed::from_cbor(&value)?;
Ok(cred.into())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct CredProof {
sigma: Signature,
commitment_vector: Vec<G1Affine>,
witness_pi: G1Affine,
nym_proof: NymProof,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CredProofCompressed {
pub sigma: SignatureCompressed,
pub commitment_vector: Vec<Vec<u8>>,
pub witness_pi: Vec<u8>,
pub nym_proof: NymProofCompressed,
}
impl CBORCodec for CredProofCompressed {}
impl From<CredProof> for CredProofCompressed {
fn from(item: CredProof) -> Self {
Self {
sigma: SignatureCompressed::from(item.sigma),
commitment_vector: item
.commitment_vector
.iter()
.map(|c| c.to_compressed().to_vec())
.collect(),
witness_pi: item.witness_pi.to_compressed().to_vec(),
nym_proof: NymProofCompressed::from(item.nym_proof),
}
}
}
impl TryFrom<CredProofCompressed> for CredProof {
type Error = error::Error;
fn try_from(item: CredProofCompressed) -> Result<Self, Self::Error> {
let sigma = Signature::try_from(item.sigma)?;
let nym_proof = NymProof::try_from(item.nym_proof)?;
let commitment_vector = item
.commitment_vector
.iter()
.map(|c| {
let mut byte = [0u8; G1Affine::COMPRESSED_BYTES];
byte.copy_from_slice(c);
let maybe_g1 = G1Affine::from_compressed(&byte);
if maybe_g1.is_none().into() {
return Err(error::Error::InvalidG1Point);
} else {
Ok(G1Projective::from(maybe_g1.unwrap()))
}
})
.map(|item| item.unwrap().into())
.collect::<Vec<G1Projective>>();
let witness_pi = {
let mut byte = [0u8; G1Affine::COMPRESSED_BYTES];
byte.copy_from_slice(&item.witness_pi);
let maybe_g1 = G1Affine::from_compressed(&byte);
if maybe_g1.is_none().into() {
return Err(error::Error::InvalidG1Point);
} else {
G1Projective::from(maybe_g1.unwrap())
}
};
Ok(Self {
sigma,
commitment_vector: commitment_vector.iter().map(|c| c.to_affine()).collect(),
witness_pi: witness_pi.into(),
nym_proof,
})
}
}
pub fn verify_proof(
issuer_public: &IssuerPublic,
proof: &CredProof,
selected_attrs: &[Entry],
nonce: Option<&Nonce>,
) -> bool {
let commitment_vectors = proof
.commitment_vector
.iter()
.zip(selected_attrs.iter())
.filter(|(_, selected_attr)| !selected_attr.is_empty())
.map(|(commitment_vector, _)| *commitment_vector)
.collect::<Vec<_>>();
let check_verify_cross = CrossSetCommitment::verify_cross(
&issuer_public.parameters,
&commitment_vectors
.iter()
.map(|c| c.into())
.collect::<Vec<_>>(),
selected_attrs,
&proof.witness_pi.into(),
);
let check_zkp_verify = DamgardTransform::verify(&proof.nym_proof, nonce);
let verify_sig = verify(
&issuer_public.vk,
&proof.nym_proof.public_key.into(),
&proof
.commitment_vector
.iter()
.map(|c| c.into())
.collect::<Vec<_>>(),
&proof.sigma,
);
check_verify_cross && check_zkp_verify && verify_sig
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
attributes::{attribute, Attribute},
zkp::Pedersen,
};
lazy_static::lazy_static! {
static ref NONCE: Nonce = Nonce::default();
}
fn init() {
let _ = env_logger::builder().is_test(true).try_init();
if log::log_enabled!(log::Level::Debug) {
log::error!("Debug logging enabled");
}
}
struct TestMessages {
message1_str: Vec<Attribute>,
message2_str: Vec<Attribute>,
message3_str: Vec<Attribute>,
}
fn setup_tests() -> TestMessages {
let age = attribute("age = 30");
let name = attribute("name = Alice ");
let drivers = attribute("driver license = 12");
let gender = attribute("gender = male");
let company = attribute("company = ACME");
let insurance = attribute("Insurance = 2");
let car_type = attribute("Car type = BMW");
let message1_str = vec![age, name];
let message2_str = vec![gender, company, drivers];
let message3_str = vec![insurance, car_type];
TestMessages {
message1_str,
message2_str,
message3_str,
}
}
#[test]
fn test_sign() -> Result<(), IssuerError> {
let issuer = Issuer::new(MaxCardinality::new(5), MaxEntries::new(10));
let user = Nym::new();
let messages_vectors = setup_tests();
let cred = issuer.sign(
&user.public.key,
&[
Entry(messages_vectors.message1_str),
Entry(messages_vectors.message2_str),
],
None,
)?;
assert!(verify(
&issuer.public.vk,
&user.public.key,
&cred.commitment_vector,
&cred.sigma
));
Ok(())
}
#[test]
fn test_signature_roundtrip_convert_compressed() {
let issuer = Issuer::new(MaxCardinality::new(5), MaxEntries::new(10));
let user = Nym::new();
let messages_vectors = setup_tests();
let cred = issuer
.sign(
&user.public.key,
&[
Entry(messages_vectors.message1_str),
Entry(messages_vectors.message2_str),
],
None,
)
.unwrap();
let sig_compressed = SignatureCompressed::from(cred.sigma.clone());
let sig_json = serde_json::to_string(&sig_compressed).unwrap();
let sig_compressed2: SignatureCompressed = serde_json::from_str(&sig_json).unwrap();
let sig2 = Signature::try_from(sig_compressed2).unwrap();
assert_eq!(cred.sigma, sig2);
}
#[test]
fn test_sign_too_large_cardinality_error() {
let issuer = Issuer::new(MaxCardinality::new(1), MaxEntries::new(10));
let user = Nym::new();
let messages_vectors = setup_tests();
let cred = issuer.sign(
&user.public.key,
&[
Entry(messages_vectors.message1_str),
Entry(messages_vectors.message2_str),
Entry(messages_vectors.message3_str),
],
None,
);
assert!(cred.is_err());
}
#[test]
fn test_sign_too_large_entries_error() {
let issuer = Issuer::new(MaxCardinality::new(5), MaxEntries::new(2));
let user = Nym::new();
let messages_vectors = setup_tests();
let cred = issuer.sign(
&user.public.key,
&[
Entry(messages_vectors.message1_str),
Entry(messages_vectors.message2_str),
Entry(messages_vectors.message3_str),
],
None,
);
assert!(cred.is_err());
}
#[test]
fn test_sign_empty_message_vector() {
let issuer = Issuer::default();
let user = Nym::new();
let cred = issuer.sign(&user.public.key, &[], None);
assert!(cred.is_ok());
}
#[test]
fn test_changerep() -> Result<(), IssuerError> {
let issuer = Issuer::new(MaxCardinality::new(5), MaxEntries::new(10));
let user = Nym::new();
let messages_vectors = setup_tests();
let k_prime = Some(4);
let signature_original = issuer.sign(
&user.public.key,
&[
Entry(messages_vectors.message1_str),
Entry(messages_vectors.message2_str),
],
k_prime,
)?;
let updatable = false;
let mu = Scalar::random(ThreadRng::default());
let psi = Scalar::random(ThreadRng::default());
let (rndmz_pk_u, signature, _chi) =
spseq_uc::change_rep(&user.public.key, &signature_original, &mu, &psi, updatable);
assert!(verify(
&issuer.public.vk,
&rndmz_pk_u,
&signature.commitment_vector,
&signature.sigma
));
Ok(())
}
#[test]
fn test_changerep_update_key() -> Result<(), IssuerError> {
let issuer = Issuer::new(MaxCardinality::new(5), MaxEntries::new(10));
let user = Nym::new();
let messages_vectors = setup_tests();
let k_prime = Some(4);
let signature_original = issuer.sign(
&user.public.key,
&[
Entry(messages_vectors.message1_str),
Entry(messages_vectors.message2_str),
],
k_prime,
)?;
let updatable = true;
let mu = Scalar::random(ThreadRng::default());
let psi = Scalar::random(ThreadRng::default());
let (rndmz_pk_u, signature, _chi) =
spseq_uc::change_rep(&user.public.key, &signature_original, &mu, &psi, updatable);
assert!(verify(
&issuer.public.vk,
&rndmz_pk_u,
&signature.commitment_vector,
&signature.sigma
));
Ok(())
}
#[test]
fn test_change_rel_from_sign() -> Result<(), IssuerError> {
let issuer = Issuer::new(MaxCardinality::new(5), MaxEntries::new(10));
let nym = Nym::new();
let messages_vectors = setup_tests();
let k_prime = 3;
let messages_vector = &vec![
Entry(messages_vectors.message1_str),
Entry(messages_vectors.message2_str),
];
let signature_original = issuer.sign(&nym.public.key, messages_vector, Some(k_prime))?;
assert_eq!(
signature_original
.update_key
.as_ref()
.expect("There to be an update key")
.len(),
k_prime
);
for k in 0..messages_vector.len() {
assert_eq!(
signature_original
.update_key
.as_ref()
.expect("There to be an update key")[k],
Vec::new()
);
}
for k in messages_vector.len()..k_prime {
assert_ne!(
signature_original
.update_key
.as_ref()
.expect("There to be an update key")[k],
Vec::new()
);
}
let message_l = Entry(messages_vectors.message3_str);
let mu = Scalar::ONE;
let signature_changed = spseq_uc::change_rel(
&issuer.public.parameters,
&message_l,
signature_original,
&mu,
)?;
assert!(verify(
&issuer.public.vk,
&nym.public.key,
&signature_changed.commitment_vector,
&signature_changed.sigma
));
Ok(())
}
#[test]
fn test_change_rel_from_rep() -> Result<()> {
let issuer = Issuer::new(MaxCardinality::new(5), MaxEntries::new(10));
let nym = Nym::new();
let messages_vectors = setup_tests();
let k_prime = Some(4);
let messages_vector = &vec![
Entry(messages_vectors.message1_str),
Entry(messages_vectors.message2_str),
];
let signature_original = issuer.sign(&nym.public.key, messages_vector, k_prime)?;
let updatable = true;
let mu = Scalar::random(ThreadRng::default());
let psi = Scalar::random(ThreadRng::default());
let (rndmz_pk_u, signature_prime, _chi) =
spseq_uc::change_rep(&nym.public.key, &signature_original, &mu, &psi, updatable);
assert!(verify(
&issuer.public.vk,
&rndmz_pk_u,
&signature_prime.commitment_vector,
&signature_prime.sigma
));
let message_l = Entry(messages_vectors.message3_str);
let cred_tilde =
spseq_uc::change_rel(&issuer.public.parameters, &message_l, signature_prime, &mu)?;
assert!(verify(
&issuer.public.vk,
&rndmz_pk_u,
&cred_tilde.commitment_vector,
&cred_tilde.sigma
));
Ok(())
}
#[test]
fn test_change_rel_from_rep_rep() -> Result<()> {
let issuer = Issuer::new(MaxCardinality::new(5), MaxEntries::new(10));
let nym = Nym::new();
let messages_vectors = setup_tests();
let message_l = Entry(messages_vectors.message3_str);
let k_prime = Some(8);
let messages_vector = &vec![
Entry(messages_vectors.message1_str),
Entry(messages_vectors.message2_str),
];
let cred = issuer.sign(&nym.public.key, messages_vector, k_prime)?;
let mu = Scalar::ONE;
let psi = Scalar::random(ThreadRng::default());
let (rndmz_pk_u, cred_r, _chi) =
spseq_uc::change_rep(&nym.public.key, &cred, &mu, &psi, true);
assert!(verify(
&cred_r.issuer_public.vk,
&rndmz_pk_u,
&cred_r.commitment_vector,
&cred_r.sigma
));
let mu = Scalar::random(ThreadRng::default()); let psi = Scalar::random(ThreadRng::default());
let (rndmz_pk_u, cred_r, _chi) =
spseq_uc::change_rep(&rndmz_pk_u, &cred_r, &mu, &psi, true);
assert!(verify(
&cred_r.issuer_public.vk,
&rndmz_pk_u,
&cred_r.commitment_vector,
&cred_r.sigma
));
let cred_rel =
spseq_uc::change_rel(&issuer.public.parameters, &message_l, cred_r, &mu).unwrap();
assert!(verify(
&cred_rel.issuer_public.vk,
&rndmz_pk_u,
&cred_rel.commitment_vector,
&cred_rel.sigma
));
Ok(())
}
#[test]
fn test_convert() -> Result<(), error::Error> {
let issuer = Issuer::new(MaxCardinality::new(5), MaxEntries::new(10));
let nym = Nym::new().randomize();
let messages_vectors = setup_tests();
let k_prime = Some(4);
let messages_vector = &vec![
Entry(messages_vectors.message1_str),
Entry(messages_vectors.message2_str),
];
let signature_original = issuer
.sign(&nym.public.key, messages_vector, k_prime)
.expect("valid tests");
let nym_new = Nym::new();
let orphan = nym.send_convert_sig(&issuer.public.vk, signature_original.sigma);
let sigma_new = nym_new.receive_cred(&issuer.public.vk, orphan)?;
assert!(verify(
&issuer.public.vk,
&nym_new.public.key,
&signature_original.commitment_vector,
&sigma_new
));
Ok(())
}
#[test]
fn test_issue_root_cred() -> Result<(), IssuerError> {
let issuer = Issuer::new(MaxCardinality::new(5), MaxEntries::new(10));
let nym = Nym::new().randomize();
let messages_vectors = setup_tests();
let k_prime = Some(4);
let messages_vector = &vec![
Entry(messages_vectors.message1_str),
Entry(messages_vectors.message2_str),
];
let cred = issuer.issue_cred(
messages_vector,
k_prime,
&nym.nym_proof(&NONCE),
Some(&NONCE),
)?;
assert!(verify(
&issuer.public.vk,
&nym.public.key,
&cred.commitment_vector,
&cred.sigma
));
Ok(())
}
#[test]
fn test_delegate_only() -> Result<()> {
let issuer = Issuer::default();
let nym = Nym::new().randomize();
let messages_vectors = setup_tests();
let k_prime = Some(5);
let messages_vector = &mut vec![
Entry(messages_vectors.message1_str),
Entry(messages_vectors.message2_str),
];
let cred = issuer.issue_cred(
messages_vector,
k_prime,
&nym.nym_proof(&NONCE),
Some(&NONCE),
)?;
let nym_r = Nym::new();
let addl_entry = Entry::new(&[attribute("age > 21")]);
messages_vector.push(addl_entry.clone());
let offer = nym.offer(&cred, &Some(addl_entry))?;
assert!(verify(
&issuer.public.vk,
&nym.public.key,
&cred.commitment_vector,
&cred.sigma
));
let cred_r = nym_r.accept(&offer)?;
assert!(verify(
&cred_r.issuer_public.vk,
&nym_r.public.key,
&cred_r.commitment_vector,
&cred_r.sigma
));
let proof = nym_r.prove(&cred_r, messages_vector, messages_vector, &NONCE);
assert!(verify_proof(
&issuer.public,
&proof,
messages_vector,
Some(&NONCE)
));
Ok(())
}
#[test]
fn test_prove_subset_creds() -> Result<(), IssuerError> {
let age = attribute("age = 30");
let name = attribute("name = Alice");
let drivers = attribute("driver license = 12");
let gender = attribute("gender = male");
let company = attribute("company = ACME");
let drivers_type_b = attribute("driver license type = B");
let insurance = attribute("Insurance = 2");
let car_type = attribute("Car type = BMW");
let message1_str = vec![age.clone(), name.clone(), drivers];
let message2_str = vec![gender.clone(), company.clone(), drivers_type_b];
let message3_str = vec![insurance, car_type];
let all_attributes = vec![
Entry::new(&message1_str),
Entry::new(&message2_str),
Entry::new(&message3_str),
];
let issuer = Issuer::new(MaxCardinality::new(5), MaxEntries::new(10));
let nym_p = Nym::new().randomize();
let k_prime = Some(4);
let cred = issuer.issue_cred(
&all_attributes,
k_prime,
&nym_p.nym_proof(&NONCE),
Some(&NONCE),
)?;
let search_for = [age, name];
let sub_list1_str = message1_str
.iter()
.filter(|&x| search_for.contains(x))
.cloned()
.collect::<Vec<Attribute>>();
let sub_list2_str = vec![gender, company];
let selected_attrs = vec![Entry(sub_list1_str), Entry(sub_list2_str)];
let proof = nym_p.prove(&cred, &all_attributes, &selected_attrs, &NONCE);
assert!(verify_proof(
&issuer.public,
&proof,
&selected_attrs,
Some(&NONCE)
));
Ok(())
}
#[test]
fn test_delegate_subset() -> Result<()> {
let age = attribute("age = 30");
let name = attribute("name = Alice");
let drivers = attribute("driver license = 12");
let gender = attribute("gender = male");
let company = attribute("company = ACME");
let drivers_type_b = attribute("driver license type = B");
let insurance = attribute("Insurance = 2");
let car_type = attribute("Car type = BMW");
let message1_str = vec![age.clone(), name, drivers];
let message2_str = vec![gender, company, drivers_type_b];
let message3_str = vec![insurance.clone(), car_type];
let mut all_attributes = vec![
Entry::new(&message1_str),
Entry::new(&message2_str),
Entry::new(&message3_str),
];
let l_message = MaxEntries::new(10);
let issuer = Issuer::new(MaxCardinality::new(8), l_message);
let alice_nym = Nym::new().randomize();
let position = 5; let index_l = all_attributes.len() + position;
let k_prime = Some(std::cmp::min(index_l, l_message.into()));
let cred = issuer.issue_cred(
&all_attributes,
k_prime,
&alice_nym.nym_proof(&NONCE),
Some(&NONCE),
)?;
let bobby_nym = Nym::new();
let mut opening_vector_restricted = cred.opening_vector.clone();
opening_vector_restricted[0] = Scalar::ZERO; opening_vector_restricted[1] = Scalar::ZERO;
let cred_restricted = Credential {
opening_vector: opening_vector_restricted, ..cred
};
let legal_age = Attribute::new("age > 21");
let addl_entry = Entry::new(&[legal_age.clone()]);
all_attributes.push(addl_entry.clone());
let alice_del_to_bobby = alice_nym.offer(&cred_restricted, &Some(addl_entry))?;
let bobby_cred = bobby_nym.accept(&alice_del_to_bobby)?;
assert!(verify(
&issuer.public.vk,
&bobby_nym.public.key,
&bobby_cred.commitment_vector,
&bobby_cred.sigma
));
assert_eq!(bobby_cred.opening_vector.len(), 4);
let selected_attrs = vec![
Entry(vec![]), Entry(vec![]),
Entry(vec![insurance.clone()]),
Entry(vec![legal_age.clone()]),
];
let proof = bobby_nym.prove(&bobby_cred, &all_attributes, &selected_attrs, &NONCE);
assert!(verify_proof(
&issuer.public,
&proof,
&selected_attrs,
Some(&NONCE)
));
let selected_attrs = vec![
Entry(vec![age]),
Entry(vec![]),
Entry(vec![insurance]),
Entry(vec![legal_age]),
];
let proof = bobby_nym.prove(&bobby_cred, &all_attributes, &selected_attrs, &NONCE);
assert!(!verify_proof(
&issuer.public,
&proof,
&selected_attrs,
Some(&NONCE)
));
Ok(())
}
#[test]
fn test_second_offer() -> Result<()> {
init();
let age = attribute("age = 30");
let name = attribute("name = Alice");
let drivers = attribute("driver license = 12");
let gender = attribute("gender = male");
let company = attribute("company = ACME");
let drivers_type_b = attribute("driver license type = B");
let insurance = attribute("Insurance = 2");
let car_type = attribute("Car type = BMW");
let message1_str = vec![age, name, drivers];
let message2_str = vec![gender, company, drivers_type_b];
let message3_str = vec![insurance.clone(), car_type];
let mut all_attributes = vec![
Entry::new(&message1_str),
Entry::new(&message2_str),
Entry::new(&message3_str),
];
let l_message = MaxEntries::new(10);
let issuer = Issuer::new(MaxCardinality::new(8), l_message);
let alice_nym = Nym::new().randomize();
let position = 5; let index_l = all_attributes.len() + position;
let k_prime = Some(std::cmp::min(index_l, l_message.into()));
let alice_cred = issuer.issue_cred(
&all_attributes,
k_prime,
&alice_nym.nym_proof(&NONCE),
Some(&NONCE),
)?;
let bobby_nym = Nym::new();
let alice_offer = alice_nym.offer(&alice_cred, &None)?;
let bobby_cred = bobby_nym.accept(&alice_offer)?;
let charlie_nym = Nym::new();
let handsome_attribute = Attribute::new("also handsome");
let additional_entry = Entry::new(&[handsome_attribute.clone()]);
all_attributes.push(additional_entry.clone());
let bobby_offer = bobby_nym.offer(&bobby_cred, &Some(additional_entry))?;
assert_eq!(
bobby_offer.commitment_vector.len(),
bobby_cred.commitment_vector.len() + 1
);
let charlie_cred = charlie_nym.accept(&bobby_offer)?;
let selected_attrs = vec![
Entry(vec![]), Entry(vec![]),
Entry(vec![insurance]),
Entry(vec![handsome_attribute]),
];
let proof = charlie_nym.prove(&charlie_cred, &all_attributes, &selected_attrs, &NONCE);
assert!(verify_proof(
&issuer.public,
&proof,
&selected_attrs,
Some(&NONCE)
));
Ok(())
}
#[test]
fn test_nonce_matches() {
let nonce = Nonce::new(vec![1, 2, 3, 4, 5, 6, 7, 8]);
let issuer = Issuer::default();
let nym = Nym::new();
let messages_vectors = setup_tests();
let k_prime = None;
let cred = issuer
.issue_cred(
&[Entry(messages_vectors.message1_str.clone())],
k_prime,
&nym.nym_proof(&nonce),
Some(&nonce),
)
.unwrap();
let proof = nym.prove(
&cred,
&[Entry(messages_vectors.message1_str.clone())],
&[Entry(messages_vectors.message1_str.clone())],
&nonce,
);
assert_eq!(proof.nym_proof.pedersen_open.open_randomness, nonce);
assert!(verify_proof(
&issuer.public,
&proof,
&[Entry(messages_vectors.message1_str)],
Some(&nonce)
));
}
#[test]
fn test_nym_proof_roundtrip() {
let nym = Nym::new();
let nonce = Nonce::new(vec![1, 2, 3, 4, 5, 6, 7, 8]);
let nym_proof = nym.nym_proof(&nonce);
let compressed_nym_proof = NymProofCompressed::from(nym_proof.clone());
let decomp_nym_proof = NymProof::try_from(compressed_nym_proof).expect("valid conversion");
assert_eq!(nym_proof, decomp_nym_proof);
}
#[test]
fn test_extend_credential() -> Result<()> {
let issuer = Issuer::default();
let nym = Nym::new();
let messages_vectors = setup_tests();
let k_prime = Some(4);
let cred = issuer
.issue_cred(
&[Entry(messages_vectors.message1_str.clone())],
k_prime,
&nym.nym_proof(&NONCE),
Some(&NONCE),
)
.unwrap();
let nonce = Nonce::new(vec![1, 2, 3, 4, 5, 6, 7, 8]);
let proof = nym.prove(
&cred,
&[Entry(messages_vectors.message1_str.clone())],
&[Entry(messages_vectors.message1_str.clone())],
&nonce,
);
assert_eq!(proof.nym_proof.pedersen_open.open_randomness, nonce);
assert!(verify_proof(
&issuer.public,
&proof,
&[Entry(messages_vectors.message1_str.clone())],
Some(&nonce)
));
let extended_cred = nym.extend(&cred, &Entry(messages_vectors.message2_str.clone()))?;
let proof = nym.prove(
&extended_cred,
&[
Entry(messages_vectors.message1_str.clone()),
Entry(messages_vectors.message2_str.clone()),
],
&[Entry(messages_vectors.message1_str.clone())],
&NONCE,
);
assert!(verify_proof(
&issuer.public,
&proof,
&[Entry(messages_vectors.message1_str.clone())],
Some(&NONCE)
));
let selected_entries = vec![Entry(vec![]), Entry(messages_vectors.message2_str.clone())];
let proof = nym.prove(
&extended_cred,
&[
Entry(messages_vectors.message1_str.clone()),
Entry(messages_vectors.message2_str.clone()),
],
&selected_entries,
&nonce,
);
assert!(verify_proof(
&issuer.public,
&proof,
&selected_entries,
Some(&nonce)
));
Ok(())
}
#[test]
fn test_nym_proof_compressed_roundtrip() {
let nym = Nym::new();
let nonce = Nonce::new(vec![1, 2, 3, 4, 5, 6, 7, 8]);
let nym_proof = nym.nym_proof(&nonce);
let nym_proof_compressed = NymProofCompressed::from(nym_proof.clone());
let nym_proof2 = NymProof::try_from(nym_proof_compressed).expect("valid conversion");
assert_eq!(nym_proof, nym_proof2);
}
#[test]
fn test_issuer_public_compressed_roundtrip() {
let issuer = Issuer::default();
let issuer_public = issuer.public.clone();
let issuer_public_compressed = IssuerPublicCompressed::from(issuer_public.clone());
let issuer_public2 =
IssuerPublic::try_from(issuer_public_compressed).expect("valid conversion");
assert_eq!(issuer_public, issuer_public2);
}
#[test]
fn test_signature_compress_rountrip() {
let signature = Signature {
z: G1Projective::random(&mut ThreadRng::default()).into(),
y_g1: G1Projective::random(&mut ThreadRng::default()).into(),
y_hat: G2Projective::random(&mut ThreadRng::default()).into(),
t: G1Projective::random(&mut ThreadRng::default()).into(),
};
let signature_compressed = SignatureCompressed::from(signature.clone());
let signature2 =
Signature::try_from(signature_compressed.clone()).expect("valid conversion");
eprintln!(
"\nOrig: {:?} \n\n Compressed: {:?} \n\nUncomp {:?} \n",
signature, signature_compressed, signature2
);
assert_eq!(signature, signature2);
}
#[test]
fn test_cred_proofcompressed_roundtrip() {
let cred_proof = CredProof {
sigma: Signature {
z: G1Projective::random(&mut ThreadRng::default()).into(),
y_g1: G1Projective::random(&mut ThreadRng::default()).into(),
y_hat: G2Projective::random(&mut ThreadRng::default()).into(),
t: G1Projective::random(&mut ThreadRng::default()).into(),
},
commitment_vector: vec![G1Projective::random(&mut ThreadRng::default()).into(); 4],
witness_pi: G1Projective::random(&mut ThreadRng::default()).into(),
nym_proof: NymProof {
challenge: Scalar::random(&mut ThreadRng::default()),
pedersen_open: PedersenOpen {
announce_randomness: Scalar::random(&mut ThreadRng::default()),
open_randomness: Nonce::default(),
announce_element: Some(G1Projective::random(&mut ThreadRng::default()).into()),
},
pedersen_commit: G1Projective::random(&mut ThreadRng::default()).into(),
public_key: G1Projective::random(&mut ThreadRng::default()).into(),
response: Scalar::random(&mut ThreadRng::default()),
damgard: DamgardTransform {
pedersen: Pedersen {
h: G1Projective::random(&mut ThreadRng::default()).into(),
},
},
},
};
let commit_vec_comp = cred_proof
.commitment_vector
.iter()
.map(|x| x.to_compressed().to_vec())
.collect::<Vec<Vec<u8>>>();
let commit_vec_decomp = commit_vec_comp
.iter()
.map(|x| try_decompress_g1(x.to_vec()))
.collect::<Result<Vec<G1Affine>, _>>()
.expect("valid conversion");
assert_eq!(cred_proof.commitment_vector, commit_vec_decomp);
let wit_comp = cred_proof.witness_pi.to_compressed().to_vec();
let wit_decomp = try_decompress_g1(wit_comp).expect("valid conversion");
assert_eq!(cred_proof.witness_pi, wit_decomp);
let nym_proof_comp = NymProofCompressed::from(cred_proof.nym_proof.clone());
let nym_proof_decomp = NymProof::try_from(nym_proof_comp).expect("valid conversion");
assert_eq!(cred_proof.nym_proof, nym_proof_decomp);
let cred_proof_compressed = CredProofCompressed::from(cred_proof.clone());
let proof2 = CredProof::try_from(cred_proof_compressed.clone()).expect("valid conversion");
assert_eq!(cred_proof, proof2);
}
#[test]
fn test_offer_compressed_roundtrip() {
let issuer = Issuer::default();
let nym = Nym::new();
let messages_vectors = setup_tests();
let k_prime = Some(4);
let cred = issuer
.issue_cred(
&[Entry(messages_vectors.message1_str.clone())],
k_prime,
&nym.nym_proof(&NONCE),
Some(&NONCE),
)
.unwrap();
let nonce = Nonce::new(vec![1, 2, 3, 4, 5, 6, 7, 8]);
let proof = nym.prove(
&cred,
&[Entry(messages_vectors.message1_str.clone())],
&[Entry(messages_vectors.message1_str.clone())],
&nonce,
);
assert_eq!(proof.nym_proof.pedersen_open.open_randomness, nonce);
assert!(verify_proof(
&issuer.public,
&proof,
&[Entry(messages_vectors.message1_str.clone())],
Some(&nonce)
));
let extended_cred = nym
.extend(&cred, &Entry(messages_vectors.message2_str.clone()))
.unwrap();
let proof = nym.prove(
&extended_cred,
&[
Entry(messages_vectors.message1_str.clone()),
Entry(messages_vectors.message2_str.clone()),
],
&[Entry(messages_vectors.message1_str.clone())],
&NONCE,
);
assert!(verify_proof(
&issuer.public,
&proof,
&[Entry(messages_vectors.message1_str.clone())],
Some(&NONCE)
));
let offer = nym.offer(&extended_cred, &None).unwrap();
let offer_compressed = OfferCompressed::from(offer.clone());
let bytes = offer_compressed.to_cbor().expect("valid bytes");
let offer_compressed2 = OfferCompressed::from_cbor(&bytes).expect("valid bytes");
let offer2 = Offer::try_from(offer_compressed2).expect("valid conversion");
assert_eq!(offer, offer2);
}
}