use super::{constants::AttributeKind, types::*};
use crate::{
bulletproofs::{
range_proof::RangeProof, set_membership_proof::SetMembershipProof,
set_non_membership_proof::SetNonMembershipProof,
},
common::*,
curve_arithmetic::Curve,
sigma_protocols::dlog::Response as DlogResponse,
};
use pairing::bls12_381::G1;
use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize};
use std::{collections::BTreeSet, convert::TryFrom, marker::PhantomData, str::FromStr};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
pub enum ProofVersion {
Version1,
Version2,
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, SerdeSerialize, SerdeDeserialize)]
pub struct RevealAttributeStatement<TagType: Serialize> {
#[serde(rename = "attributeTag")]
pub attribute_tag: TagType,
}
#[derive(Debug, Clone, Serialize, PartialEq, SerdeSerialize, SerdeDeserialize, Eq)]
#[serde(bound(
serialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeSerialize, TagType: \
SerdeSerialize",
deserialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeDeserialize<'de>, \
TagType: SerdeDeserialize<'de>"
))]
pub struct AttributeInRangeStatement<
C: Curve,
TagType: Serialize,
AttributeType: Attribute<C::Scalar>,
> {
#[serde(rename = "attributeTag")]
pub attribute_tag: TagType,
#[serde(rename = "lower")]
pub lower: AttributeType,
#[serde(rename = "upper")]
pub upper: AttributeType,
#[serde(skip)]
pub _phantom: PhantomData<C>,
}
#[derive(Debug, Clone, PartialEq, SerdeSerialize, SerdeDeserialize, Serialize, Eq)]
#[serde(bound(
serialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeSerialize, TagType: \
SerdeSerialize",
deserialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeDeserialize<'de>, \
TagType: SerdeDeserialize<'de>"
))]
pub struct AttributeInSetStatement<
C: Curve,
TagType: Serialize,
AttributeType: Attribute<C::Scalar>,
> {
#[serde(rename = "attributeTag")]
pub attribute_tag: TagType,
#[serde(rename = "set")]
pub set: std::collections::BTreeSet<AttributeType>,
#[serde(skip)]
pub _phantom: PhantomData<C>,
}
#[derive(Debug, Clone, PartialEq, SerdeSerialize, SerdeDeserialize, Serialize, Eq)]
#[serde(bound(
serialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeSerialize, TagType: \
SerdeSerialize",
deserialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeDeserialize<'de>, \
TagType: SerdeDeserialize<'de>"
))]
pub struct AttributeNotInSetStatement<
C: Curve,
TagType: Serialize,
AttributeType: Attribute<C::Scalar>,
> {
#[serde(rename = "attributeTag")]
pub attribute_tag: TagType,
#[serde(rename = "set")]
pub set: std::collections::BTreeSet<AttributeType>,
#[serde(skip)]
pub _phantom: PhantomData<C>,
}
#[derive(Debug, Clone, PartialEq, SerdeSerialize, SerdeDeserialize, Eq)]
#[serde(bound(
serialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeSerialize, TagType: \
SerdeSerialize",
deserialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeDeserialize<'de>, \
TagType: SerdeDeserialize<'de>"
))]
#[serde(tag = "type")]
pub enum AtomicStatement<C: Curve, TagType: Serialize, AttributeType: Attribute<C::Scalar>> {
RevealAttribute {
#[serde(flatten)]
statement: RevealAttributeStatement<TagType>,
},
AttributeInRange {
#[serde(flatten)]
statement: AttributeInRangeStatement<C, TagType, AttributeType>,
},
AttributeInSet {
#[serde(flatten)]
statement: AttributeInSetStatement<C, TagType, AttributeType>,
},
AttributeNotInSet {
#[serde(flatten)]
statement: AttributeNotInSetStatement<C, TagType, AttributeType>,
},
}
impl<C: Curve, TagType: Serialize + Copy, AttributeType: Attribute<C::Scalar>>
AtomicStatement<C, TagType, AttributeType>
{
pub fn attribute(&self) -> TagType {
match self {
AtomicStatement::RevealAttribute { statement } => statement.attribute_tag,
AtomicStatement::AttributeInRange { statement } => statement.attribute_tag,
AtomicStatement::AttributeInSet { statement } => statement.attribute_tag,
AtomicStatement::AttributeNotInSet { statement } => statement.attribute_tag,
}
}
}
impl<C: Curve, TagType: Serialize, AttributeType: Attribute<C::Scalar>> Serial
for AtomicStatement<C, TagType, AttributeType>
{
fn serial<B: Buffer>(&self, out: &mut B) {
match self {
AtomicStatement::RevealAttribute { statement } => {
0u8.serial(out);
statement.serial(out);
}
AtomicStatement::AttributeInRange { statement } => {
1u8.serial(out);
statement.serial(out);
}
AtomicStatement::AttributeInSet { statement } => {
2u8.serial(out);
statement.serial(out);
}
AtomicStatement::AttributeNotInSet { statement } => {
3u8.serial(out);
statement.serial(out);
}
}
}
}
impl<C: Curve, TagType: Serialize, AttributeType: Attribute<C::Scalar>> Deserial
for AtomicStatement<C, TagType, AttributeType>
{
fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
match u8::deserial(source)? {
0u8 => {
let statement = source.get()?;
Ok(Self::RevealAttribute { statement })
}
1u8 => {
let statement = source.get()?;
Ok(Self::AttributeInRange { statement })
}
2u8 => {
let statement = source.get()?;
Ok(Self::AttributeInSet { statement })
}
3u8 => {
let statement = source.get()?;
Ok(Self::AttributeNotInSet { statement })
}
n => anyhow::bail!("Unknown statement tag: {}.", n),
}
}
}
#[derive(Debug, Clone, SerdeSerialize, SerdeDeserialize)]
#[serde(bound(
serialize = "C: Curve, AttributeType: Attribute<C::Scalar> +
SerdeSerialize",
deserialize = "C: Curve, AttributeType:
Attribute<C::Scalar> + SerdeDeserialize<'de>"
))]
#[serde(tag = "type")]
pub enum AtomicProof<C: Curve, AttributeType: Attribute<C::Scalar>> {
RevealAttribute {
attribute: AttributeType, proof: crate::sigma_protocols::common::SigmaProof<DlogResponse<C>>,
},
AttributeInRange {
#[serde(
rename = "proof",
serialize_with = "base16_encode",
deserialize_with = "base16_decode"
)]
proof: RangeProof<C>,
},
AttributeInSet {
#[serde(
rename = "proof",
serialize_with = "base16_encode",
deserialize_with = "base16_decode"
)]
proof: SetMembershipProof<C>,
},
AttributeNotInSet {
#[serde(
rename = "proof",
serialize_with = "base16_encode",
deserialize_with = "base16_decode"
)]
proof: SetNonMembershipProof<C>,
},
}
impl<C: Curve, AttributeType: Attribute<C::Scalar>> Serial for AtomicProof<C, AttributeType> {
fn serial<B: Buffer>(&self, out: &mut B) {
match self {
AtomicProof::RevealAttribute { attribute, proof } => {
0u8.serial(out);
attribute.serial(out);
proof.serial(out);
}
AtomicProof::AttributeInRange { proof } => {
1u8.serial(out);
proof.serial(out);
}
AtomicProof::AttributeInSet { proof } => {
2u8.serial(out);
proof.serial(out);
}
AtomicProof::AttributeNotInSet { proof } => {
3u8.serial(out);
proof.serial(out);
}
}
}
}
impl<C: Curve, AttributeType: Attribute<C::Scalar>> Deserial for AtomicProof<C, AttributeType> {
fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
match u8::deserial(source)? {
0u8 => {
let attribute = source.get()?;
let proof = source.get()?;
Ok(Self::RevealAttribute { attribute, proof })
}
1u8 => {
let proof = source.get()?;
Ok(Self::AttributeInRange { proof })
}
2u8 => {
let proof = source.get()?;
Ok(Self::AttributeInSet { proof })
}
3u8 => {
let proof = source.get()?;
Ok(Self::AttributeNotInSet { proof })
}
n => anyhow::bail!("Unknown proof type tag: {}", n),
}
}
}
#[derive(Debug, Clone, SerdeSerialize, SerdeDeserialize, Serialize)]
#[serde(bound(
serialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeSerialize",
deserialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeDeserialize<'de>"
))]
pub struct StatementWithContext<C: Curve, AttributeType: Attribute<C::Scalar>> {
#[serde(serialize_with = "base16_encode", deserialize_with = "base16_decode")]
pub credential: CredId<C>,
pub statement: Statement<C, AttributeType>,
}
#[derive(Debug, Clone, PartialEq, SerdeSerialize, SerdeDeserialize, Serialize)]
#[serde(bound(
serialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeSerialize",
deserialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeDeserialize<'de>"
))]
#[serde(transparent)]
pub struct Statement<C: Curve, AttributeType: Attribute<C::Scalar>> {
pub statements: Vec<AtomicStatement<C, AttributeTag, AttributeType>>,
}
impl<C: Curve, AttributeType: Attribute<C::Scalar>> Default for Statement<C, AttributeType> {
fn default() -> Self { Statement { statements: vec![] } }
}
impl Statement<G1, AttributeKind> {
pub fn older_than(mut self, age: u64) -> Option<Self> {
use chrono::Datelike;
let now = chrono::Utc::now();
let year = u64::try_from(now.year()).ok()?;
let years_ago = year.checked_sub(age)?;
let date_years_ago = format!("{:04}{:02}{:02}", years_ago, now.month(), now.day());
let upper = AttributeKind(date_years_ago);
let lower = AttributeKind(String::from("18000101"));
let statement = AttributeInRangeStatement::<G1, _, _> {
attribute_tag: AttributeTag::from_str("dob").ok()?, lower,
upper,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeInRange { statement });
Some(self)
}
pub fn younger_than(mut self, age: u64) -> Option<Self> {
use chrono::Datelike;
let now = chrono::Utc::now();
let year = u64::try_from(now.year()).ok()?;
let years_ago = year.checked_sub(age)?;
let date_years_ago = format!("{:04}{:02}{:02}", years_ago, now.month(), now.day());
let today = format!("{:04}{:02}{:02}", year, now.month(), now.day());
let lower = AttributeKind(date_years_ago);
let upper = AttributeKind(today);
let statement = AttributeInRangeStatement::<G1, _, _> {
attribute_tag: AttributeTag::from_str("dob").ok()?, lower,
upper,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeInRange { statement });
Some(self)
}
pub fn age_in_range(mut self, lower: u64, upper: u64) -> Option<Self> {
use chrono::Datelike;
let now = chrono::Utc::now();
let year = u64::try_from(now.year()).ok()?;
let lower_year = year.checked_sub(upper)?;
let upper_year = year.checked_sub(lower)?;
let lower_date = format!("{:04}{:02}{:02}", lower_year, now.month(), now.day());
let upper_date = format!("{:04}{:02}{:02}", upper_year, now.month(), now.day());
let lower = AttributeKind(lower_date);
let upper = AttributeKind(upper_date);
let statement = AttributeInRangeStatement::<G1, _, _> {
attribute_tag: AttributeTag::from_str("dob").ok()?, lower,
upper,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeInRange { statement });
Some(self)
}
pub fn doc_expiry_no_earlier_than(mut self, lower: AttributeKind) -> Option<Self> {
let upper = AttributeKind(String::from("99990101"));
let statement = AttributeInRangeStatement::<G1, _, _> {
attribute_tag: AttributeTag::from_str("idDocExpiresAt").ok()?, lower,
upper,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeInRange { statement });
Some(self)
}
}
impl<C: Curve, AttributeType: Attribute<C::Scalar>> Statement<C, AttributeType> {
pub fn new() -> Self { Self::default() }
pub fn reveal_attribute(mut self, attribute_tag: AttributeTag) -> Self {
let statement = RevealAttributeStatement { attribute_tag };
self.statements
.push(AtomicStatement::RevealAttribute { statement });
self
}
pub fn in_range(
mut self,
tag: AttributeTag,
lower: AttributeType,
upper: AttributeType,
) -> Self {
let statement = AttributeInRangeStatement {
attribute_tag: tag,
lower,
upper,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeInRange { statement });
self
}
pub fn member_of(mut self, tag: AttributeTag, set: BTreeSet<AttributeType>) -> Self {
let statement = AttributeInSetStatement {
attribute_tag: tag,
set,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeInSet { statement });
self
}
pub fn not_member_of(mut self, tag: AttributeTag, set: BTreeSet<AttributeType>) -> Self {
let statement = AttributeNotInSetStatement {
attribute_tag: tag,
set,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeNotInSet { statement });
self
}
pub fn residence_in(mut self, set: BTreeSet<AttributeType>) -> Option<Self> {
let statement = AttributeInSetStatement {
attribute_tag: AttributeTag::from_str("countryOfResidence").ok()?, set,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeInSet { statement });
Some(self)
}
pub fn residence_not_in(mut self, set: BTreeSet<AttributeType>) -> Option<Self> {
let statement = AttributeNotInSetStatement {
attribute_tag: AttributeTag::from_str("countryOfResidence").ok()?, set,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeNotInSet { statement });
Some(self)
}
pub fn document_issuer_in(mut self, set: BTreeSet<AttributeType>) -> Option<Self> {
let statement = AttributeInSetStatement {
attribute_tag: AttributeTag::from_str("idDocIssuer").ok()?,
set,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeInSet { statement });
Some(self)
}
pub fn document_issuer_not_in(mut self, set: BTreeSet<AttributeType>) -> Option<Self> {
let statement = AttributeNotInSetStatement {
attribute_tag: AttributeTag::from_str("idDocIssuer").ok()?,
set,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeNotInSet { statement });
Some(self)
}
pub fn nationality_in(mut self, set: BTreeSet<AttributeType>) -> Option<Self> {
let statement = AttributeInSetStatement {
attribute_tag: AttributeTag::from_str("nationality").ok()?,
set,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeInSet { statement });
Some(self)
}
pub fn nationality_not_in(mut self, set: BTreeSet<AttributeType>) -> Option<Self> {
let statement = AttributeNotInSetStatement {
attribute_tag: AttributeTag::from_str("nationality").ok()?,
set,
_phantom: PhantomData::default(),
};
self.statements
.push(AtomicStatement::AttributeNotInSet { statement });
Some(self)
}
}
#[derive(Debug, Clone, SerdeSerialize, SerdeDeserialize, Serialize)]
#[serde(bound(
serialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeSerialize",
deserialize = "C: Curve, AttributeType: Attribute<C::Scalar> + SerdeDeserialize<'de>"
))]
pub struct Proof<C: Curve, AttributeType: Attribute<C::Scalar>> {
pub proofs: Vec<AtomicProof<C, AttributeType>>,
}