#![allow(non_snake_case)]
#![doc = include_str!("../README.md")]
pub use cmz_derive::*;
use core::any::Any;
use ff::{Field, PrimeField};
use generic_static::StaticTypeMap;
use group::prime::PrimeGroup;
use group::{Group, GroupEncoding};
#[cfg(feature = "wnaf_is_constant_time")]
use group::{WnafBase, WnafScalar};
use lazy_static::lazy_static;
use rand::RngCore;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub use serde_with::serde_as;
use serde_with::{DeserializeAs, SerializeAs};
use sigma_compiler::*;
pub use sigma_compiler::{self};
use thiserror::Error;
mod group_serde;
pub struct SerdeScalar;
impl<F: PrimeField> SerializeAs<F> for SerdeScalar {
fn serialize_as<S>(value: &F, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
group_serde::serialize_scalar(value, serializer)
}
}
impl<'de, F: PrimeField> DeserializeAs<'de, F> for SerdeScalar {
fn deserialize_as<D>(deserializer: D) -> Result<F, D::Error>
where
D: Deserializer<'de>,
{
group_serde::deserialize_scalar(deserializer)
}
}
pub struct SerdePoint;
impl<G: Group + GroupEncoding> SerializeAs<G> for SerdePoint {
fn serialize_as<S>(value: &G, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
group_serde::serialize_point(value, serializer)
}
}
impl<'de, G: Group + GroupEncoding> DeserializeAs<'de, G> for SerdePoint {
fn deserialize_as<D>(deserializer: D) -> Result<G, D::Error>
where
D: Deserializer<'de>,
{
group_serde::deserialize_point(deserializer)
}
}
#[serde_as]
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct CMZMac<G: PrimeGroup> {
#[serde_as(as = "SerdePoint")]
pub P: G,
#[serde_as(as = "SerdePoint")]
pub Q: G,
}
#[serde_as]
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct CMZPrivkey<G: PrimeGroup> {
pub muCMZ: bool,
#[serde_as(as = "SerdeScalar")]
pub x0: <G as Group>::Scalar,
#[serde_as(as = "SerdeScalar")]
pub xr: <G as Group>::Scalar,
#[serde_as(as = "Vec<SerdeScalar>")]
pub x: Vec<<G as Group>::Scalar>,
}
#[serde_as]
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct CMZPubkey<G: PrimeGroup> {
#[serde_as(as = "Option<SerdePoint>")]
pub X0: Option<G>,
#[serde_as(as = "Option<SerdePoint>")]
pub Xr: Option<G>,
#[serde_as(as = "Vec<SerdePoint>")]
pub X: Vec<G>,
}
#[cfg(feature = "wnaf_is_constant_time")]
const WNAF_SIZE: usize = 6;
#[derive(Clone)]
pub struct CMZBasepoints<G: Group> {
A_: G,
B_: G,
#[cfg(feature = "wnaf_is_constant_time")]
A_TABLE: WnafBase<G, WNAF_SIZE>,
#[cfg(feature = "wnaf_is_constant_time")]
B_TABLE: WnafBase<G, WNAF_SIZE>,
}
impl<G: Group> CMZBasepoints<G> {
pub fn init(generator_A: G) -> Self {
let A_ = generator_A;
let B_ = G::generator();
#[cfg(feature = "wnaf_is_constant_time")]
let A_TABLE = WnafBase::new(A_);
#[cfg(feature = "wnaf_is_constant_time")]
let B_TABLE = WnafBase::new(B_);
CMZBasepoints {
A_,
B_,
#[cfg(feature = "wnaf_is_constant_time")]
A_TABLE,
#[cfg(feature = "wnaf_is_constant_time")]
B_TABLE,
}
}
#[cfg(feature = "wnaf_is_constant_time")]
pub fn mulA(&self, s: &G::Scalar) -> G {
let wnaf_s = WnafScalar::<G::Scalar, WNAF_SIZE>::new(s);
&self.A_TABLE * &wnaf_s
}
#[cfg(feature = "wnaf_is_constant_time")]
pub fn mulB(&self, s: &G::Scalar) -> G {
let wnaf_s = WnafScalar::<G::Scalar, WNAF_SIZE>::new(s);
&self.B_TABLE * &wnaf_s
}
#[cfg(not(feature = "wnaf_is_constant_time"))]
pub fn mulA(&self, s: &G::Scalar) -> G {
self.A_ * s
}
#[cfg(not(feature = "wnaf_is_constant_time"))]
pub fn mulB(&self, s: &G::Scalar) -> G {
self.B_ * s
}
pub fn keypairA(&self, rng: &mut impl RngCore) -> (G::Scalar, G) {
let x = G::Scalar::random(&mut *rng);
(x, self.mulA(&x))
}
pub fn keypairB(&self, rng: &mut impl RngCore) -> (G::Scalar, G) {
let x = G::Scalar::random(&mut *rng);
(x, self.mulB(&x))
}
pub fn A(&self) -> G {
self.A_
}
pub fn B(&self) -> G {
self.B_
}
}
trait CMZbp: Sync + Send {
fn as_any(&self) -> &dyn Any;
}
impl<G: Group> CMZbp for CMZBasepoints<G> {
fn as_any(&self) -> &dyn Any {
self
}
}
lazy_static! {
static ref basepoints_map: StaticTypeMap<Box<dyn CMZbp>> = StaticTypeMap::new();
}
fn load_bp<G: Group>(bp: Option<CMZBasepoints<G>>) -> &'static CMZBasepoints<G> {
match bp {
Some(b) => basepoints_map.call_once::<Box<dyn CMZbp>, _>(|| Box::new(b.clone())),
None => {
basepoints_map.call_once::<Box<dyn CMZbp>, _>(|| panic!("basepoints uninitialized"))
}
}
.as_any()
.downcast_ref::<CMZBasepoints<G>>()
.unwrap()
}
pub fn cmz_group_init<G: PrimeGroup>(generator_A: G) {
let bp = CMZBasepoints::<G>::init(generator_A);
load_bp(Some(bp));
}
pub fn cmz_basepoints<G: PrimeGroup>() -> &'static CMZBasepoints<G> {
load_bp(None)
}
pub fn cmz_privkey_to_pubkey<G: PrimeGroup>(privkey: &CMZPrivkey<G>) -> CMZPubkey<G> {
let bp = load_bp::<G>(None);
let X0: Option<G> = if privkey.muCMZ {
Some(bp.mulB(&privkey.x0))
} else {
Some(bp.mulA(&privkey.xr) + bp.mulB(&privkey.x0))
};
let Xr: Option<G> = if privkey.muCMZ {
Some(bp.mulA(&privkey.xr))
} else {
None
};
let X: Vec<G> = privkey.x.iter().map(|x| bp.mulA(x)).collect();
CMZPubkey { X0, Xr, X }
}
pub trait CMZCredential
where
Self: Default + Sized,
{
type Scalar: PrimeField;
type Point: PrimeGroup;
fn attrs() -> Vec<&'static str>;
fn num_attrs() -> usize;
fn attr_num(name: &str) -> usize;
fn attr(&self, name: &str) -> &Option<Self::Scalar>;
fn attr_mut(&mut self, name: &str) -> &mut Option<Self::Scalar>;
fn set_pubkey(&mut self, pubkey: &CMZPubkey<Self::Point>) -> &mut Self;
fn get_pubkey(&self) -> &CMZPubkey<Self::Point>;
fn set_privkey(&mut self, privkey: &CMZPrivkey<Self::Point>) -> &mut Self;
fn set_keypair(
&mut self,
privkey: &CMZPrivkey<Self::Point>,
pubkey: &CMZPubkey<Self::Point>,
) -> &mut Self;
fn get_privkey(&self) -> &CMZPrivkey<Self::Point>;
fn privkey_x(&self, name: &str) -> Self::Scalar;
fn pubkey_X(&self, name: &str) -> Self::Point;
fn gen_keys(
rng: &mut impl RngCore,
muCMZ: bool,
) -> (CMZPrivkey<Self::Point>, CMZPubkey<Self::Point>);
fn cmz14_gen_keys(rng: &mut impl RngCore) -> (CMZPrivkey<Self::Point>, CMZPubkey<Self::Point>) {
Self::gen_keys(rng, false)
}
fn mucmz_gen_keys(rng: &mut impl RngCore) -> (CMZPrivkey<Self::Point>, CMZPubkey<Self::Point>) {
Self::gen_keys(rng, true)
}
fn using_privkey(privkey: &CMZPrivkey<Self::Point>) -> Self {
let mut slf = Self::default();
slf.set_privkey(privkey);
slf
}
fn using_pubkey(pubkey: &CMZPubkey<Self::Point>) -> Self {
let mut slf = Self::default();
slf.set_pubkey(pubkey);
slf
}
fn create_MAC(
&mut self,
rng: &mut impl RngCore,
privkey: &CMZPrivkey<Self::Point>,
) -> Result<(), ()>;
fn compute_MAC_coeff(&self, privkey: &CMZPrivkey<Self::Point>) -> Result<Self::Scalar, ()>;
fn verify_MAC(&self, privkey: &CMZPrivkey<Self::Point>) -> Result<(), ()>;
fn fake_MAC(&mut self, rng: &mut impl RngCore);
}
#[macro_export]
macro_rules! CMZ {
( $name: ident < $G: ident > : $( $id: ident ),+ ) => {
#[serde_as]
#[derive(CMZCred,Clone,Debug,Default,serde::Serialize,serde::Deserialize)]
#[cmzcred_group(group = $G)]
pub struct $name {
$(
#[serde_as(as="Option<SerdeScalar>")]
pub $id: Option<<$G as Group>::Scalar>,
)+
pub MAC: CMZMac<$G>,
privkey: CMZPrivkey<$G>,
pubkey: CMZPubkey<$G>,
}
};
( $name: ident : $( $id: ident ),+ ) => {
#[serde_as]
#[derive(CMZCred,Clone,Debug,Default,serde::Serialize,serde::Deserialize)]
#[cmzcred_group(group = G)]
pub struct $name {
$(
#[serde_as(as="Option<SerdeScalar>")]
pub $id: Option<<G as Group>::Scalar>,
)+
pub MAC: CMZMac<G>,
privkey: CMZPrivkey<G>,
pubkey: CMZPubkey<G>,
}
};
}
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum CMZError {
#[error("Hide attribute {1} of credential {0} was not passed to prepare")]
HideAttrMissing(&'static str, &'static str),
#[error("Reveal attribute {1} of credential {0} was not passed to prepare")]
RevealAttrMissing(&'static str, &'static str),
#[error("Implicit attribute {1} of credential {0} was not passed to prepare")]
ImplicitAttrCliMissing(&'static str, &'static str),
#[error("Implicit attribute {1} of credential {0} was not set by fill_creds")]
ImplicitAttrIssMissing(&'static str, &'static str),
#[error("Set attribute {1} of credential {0} was not set by fill_creds")]
SetAttrMissing(&'static str, &'static str),
#[error("private key for credential {0} was not set by fill_creds")]
PrivkeyMissing(&'static str),
#[error("public key for credential {0} was not passed to prepare")]
PubkeyMissing(&'static str),
#[error("credential initialized with wrong protocol")]
WrongProtocol(&'static str),
#[error("client proof did not verify")]
CliProofFailed,
#[error("issuer proof did not verify")]
IssProofFailed,
#[error("unknown CMZ proof error")]
Unknown,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lox_credential_test() {
use curve25519_dalek::ristretto::RistrettoPoint as G;
CMZ! { Lox:
id,
bucket,
trust_level,
level_since,
invites_remaining,
blockages
}
println!("{:#?}", Lox::attrs());
let mut L = Lox::default();
println!("{:#?}", L);
L.bucket = Some(<G as Group>::Scalar::ONE);
println!("{:#?}", L);
println!("{:#?}", L.attr("bucket"));
*L.attr_mut("id") = Some(<G as Group>::Scalar::ONE);
println!("{:#?}", L);
}
}