lib.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. // We really want points to be capital letters and scalars to be
  2. // lowercase letters
  3. #![allow(non_snake_case)]
  4. pub use cmzcred_derive::*;
  5. use core::any::Any;
  6. use ff::{Field, PrimeField};
  7. use generic_static::StaticTypeMap;
  8. use group::prime::PrimeGroup;
  9. use group::{Group, GroupEncoding, WnafBase, WnafScalar};
  10. use lazy_static::lazy_static;
  11. use rand_core::RngCore;
  12. pub use serde::{Deserialize, Deserializer, Serialize, Serializer};
  13. pub use serde_with::{serde_as, DeserializeAs, SerializeAs};
  14. pub use sigma_compiler::*;
  15. use thiserror::Error;
  16. // We need wrappers for group::Group and ff::PrimeField elements to be
  17. // handled by serde
  18. //
  19. // Pattern from https://docs.rs/serde_with/3.12.0/serde_with/guide/serde_as/index.html
  20. mod group_serde;
  21. pub struct SerdeScalar;
  22. impl<F: PrimeField> SerializeAs<F> for SerdeScalar {
  23. fn serialize_as<S>(value: &F, serializer: S) -> Result<S::Ok, S::Error>
  24. where
  25. S: Serializer,
  26. {
  27. group_serde::serialize_scalar(value, serializer)
  28. }
  29. }
  30. impl<'de, F: PrimeField> DeserializeAs<'de, F> for SerdeScalar {
  31. fn deserialize_as<D>(deserializer: D) -> Result<F, D::Error>
  32. where
  33. D: Deserializer<'de>,
  34. {
  35. group_serde::deserialize_scalar(deserializer)
  36. }
  37. }
  38. pub struct SerdePoint;
  39. impl<G: Group + GroupEncoding> SerializeAs<G> for SerdePoint {
  40. fn serialize_as<S>(value: &G, serializer: S) -> Result<S::Ok, S::Error>
  41. where
  42. S: Serializer,
  43. {
  44. group_serde::serialize_point(value, serializer)
  45. }
  46. }
  47. impl<'de, G: Group + GroupEncoding> DeserializeAs<'de, G> for SerdePoint {
  48. fn deserialize_as<D>(deserializer: D) -> Result<G, D::Error>
  49. where
  50. D: Deserializer<'de>,
  51. {
  52. group_serde::deserialize_point(deserializer)
  53. }
  54. }
  55. /// The CMZMac struct represents a MAC on a CMZ credential.
  56. #[serde_as]
  57. #[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
  58. pub struct CMZMac<G: PrimeGroup> {
  59. #[serde_as(as = "SerdePoint")]
  60. pub P: G,
  61. #[serde_as(as = "SerdePoint")]
  62. pub Q: G,
  63. }
  64. /// The CMZPrivkey struct represents a CMZ private key
  65. #[serde_as]
  66. #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
  67. pub struct CMZPrivkey<G: PrimeGroup> {
  68. // Is this key for µCMZ or classic CMZ14?
  69. pub muCMZ: bool,
  70. #[serde_as(as = "SerdeScalar")]
  71. pub x0: <G as Group>::Scalar,
  72. // The next field is xr for µCMZ, and serves the role of x0tilde for
  73. // CMZ14
  74. #[serde_as(as = "SerdeScalar")]
  75. pub xr: <G as Group>::Scalar,
  76. // The elements of x correspond to the attributes of the credential
  77. #[serde_as(as = "Vec<SerdeScalar>")]
  78. pub x: Vec<<G as Group>::Scalar>,
  79. }
  80. /// The CMZPubkey struct represents a CMZ public key
  81. #[serde_as]
  82. #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
  83. pub struct CMZPubkey<G: PrimeGroup> {
  84. #[serde_as(as = "Option<SerdePoint>")]
  85. pub X0: Option<G>,
  86. // Xr is only used for µCMZ, not CMZ14 (where it will be None)
  87. #[serde_as(as = "Option<SerdePoint>")]
  88. pub Xr: Option<G>,
  89. // The elements of X correspond to the attributes of the credential
  90. #[serde_as(as = "Vec<SerdePoint>")]
  91. pub X: Vec<G>,
  92. }
  93. // The size of the WNAF windows. Larger sizes take more memory, but
  94. // result in faster multiplications.
  95. const WNAF_SIZE: usize = 6;
  96. /// A struct (generic over G) holding the two CMZ bases, and their Wnaf
  97. /// basepoint tables
  98. #[derive(Clone)]
  99. pub struct CMZBasepoints<G: Group> {
  100. A_: G,
  101. B_: G,
  102. A_TABLE: WnafBase<G, WNAF_SIZE>,
  103. B_TABLE: WnafBase<G, WNAF_SIZE>,
  104. }
  105. impl<G: Group> CMZBasepoints<G> {
  106. pub fn init(generator_A: G) -> Self {
  107. let A_ = generator_A;
  108. let B_ = G::generator();
  109. let A_TABLE = WnafBase::new(A_);
  110. let B_TABLE = WnafBase::new(B_);
  111. CMZBasepoints {
  112. A_,
  113. B_,
  114. A_TABLE,
  115. B_TABLE,
  116. }
  117. }
  118. pub fn mulA(&self, s: &G::Scalar) -> G {
  119. let wnaf_s = WnafScalar::<G::Scalar, WNAF_SIZE>::new(s);
  120. &self.A_TABLE * &wnaf_s
  121. }
  122. pub fn mulB(&self, s: &G::Scalar) -> G {
  123. let wnaf_s = WnafScalar::<G::Scalar, WNAF_SIZE>::new(s);
  124. &self.B_TABLE * &wnaf_s
  125. }
  126. pub fn keypairA(&self, rng: &mut impl RngCore) -> (G::Scalar, G) {
  127. let x = G::Scalar::random(&mut *rng);
  128. (x, self.mulA(&x))
  129. }
  130. pub fn keypairB(&self, rng: &mut impl RngCore) -> (G::Scalar, G) {
  131. let x = G::Scalar::random(&mut *rng);
  132. (x, self.mulB(&x))
  133. }
  134. pub fn A(&self) -> G {
  135. self.A_
  136. }
  137. pub fn B(&self) -> G {
  138. self.B_
  139. }
  140. }
  141. // What's going on here needs some explanation. For each group G, we
  142. // want to pre-compute the WnafBase tables in a [`CMZBasepoints`] struct,
  143. // and we want that pre-computed struct to remain globally accessible.
  144. // So ideally, we'd just have a generic static CMZBasepoints<G> struct,
  145. // and instantiate it once for each G that we use.
  146. //
  147. // The tricky bit is that we don't know what group(s) G the programmer
  148. // (the person using this cmz crate) will end up using, and Rust doesn't
  149. // support generic statics.
  150. //
  151. // So what we'd like is a non-generic static _map_ that maps a group
  152. // type G to the precomputed CMZBasepoints<G> struct. But types aren't
  153. // values that can be mapped by a normal HashMap. Luckily, there's a
  154. // generic_static crate that provides a StaticTypeMap that has the
  155. // ability to map types to objects.
  156. //
  157. // However, all of those *mapped-to* objects have to all be of the same
  158. // type, whereas we want the type G to map to a struct of type
  159. // CMZBasepoints<G>, which is different for each value of G.
  160. //
  161. // So we make a non-generic trait CMZBP that all instantiations of
  162. // CMZBasepoints<G> implement (for all group types G), and have the
  163. // StaticTypeMap map each type G to a trait object Box<dyn CMZBP>.
  164. //
  165. // Then to read the CMZBasepoints<G> back out, we look up the trait
  166. // object in the StaticTypeMap, yielding a Box<dyn CMZBP>. We now need
  167. // to downcast this trait object to the concrete type CMZBasepoints<G>,
  168. // for a _specific_ G. Rust provides downcasting, but only from &dyn Any
  169. // to the original concrete type, not from other things like &dyn CMZBP.
  170. // So first we need to upcast the trait object to &dyn Any, which we do
  171. // with an "as_any()" function in the CMZBP trait, and then downcast the
  172. // result to a CMZBasepoints<G> struct.
  173. //
  174. // The up/down casting pattern is from
  175. // https://stackoverflow.com/questions/33687447/how-to-get-a-reference-to-a-concrete-type-from-a-trait-object
  176. // Static objects have to be Sync + Send, so enforce that as part of the
  177. // CMXBP trait
  178. trait CMZBP: Sync + Send {
  179. fn as_any(&self) -> &dyn Any;
  180. }
  181. impl<G: Group> CMZBP for CMZBasepoints<G> {
  182. fn as_any(&self) -> &dyn Any {
  183. self
  184. }
  185. }
  186. // The StaticTypeMap mapping group types G to trait objects Box<dyn CMZBP>
  187. lazy_static! {
  188. static ref basepoints_map: StaticTypeMap<Box<dyn CMZBP>> = StaticTypeMap::new();
  189. }
  190. /// For a given group type G, if bp is Some(b), then load the mapping
  191. /// from G to b into the basepoints_map. (If a mapping from G already
  192. /// exists, the old one will be kept and the new one ignored.) Whether
  193. /// bp is Some(b) or None, this function returns the (possibly new)
  194. /// target of the basepoints_map, as a &'static CMZBasepoints<G>.
  195. fn load_bp<G: Group>(bp: Option<CMZBasepoints<G>>) -> &'static CMZBasepoints<G> {
  196. match bp {
  197. Some(b) => basepoints_map.call_once::<Box<dyn CMZBP>, _>(|| Box::new(b.clone())),
  198. None => {
  199. basepoints_map.call_once::<Box<dyn CMZBP>, _>(|| panic!("basepoints uninitialized"))
  200. }
  201. }
  202. .as_any()
  203. .downcast_ref::<CMZBasepoints<G>>()
  204. .unwrap()
  205. }
  206. /// CMZ credentials require two generators, A and B. B is the
  207. /// "standard" generator. A can be any other generator (that is, any
  208. /// other non-identity point in a prime-order group), but it is required
  209. /// that no one know the discrete log between A and B. So you can't
  210. /// generate A by multiplying B by some scalar, for example. If your
  211. /// group has a hash_from_bytes function, then you can use that to generate
  212. /// A. For example, if your group is a curve25519 group, you can
  213. ///
  214. /// use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT as B;
  215. /// use sha2::Sha512;
  216. /// let A = G::hash_from_bytes::<Sha512>(b"CMZ Generator A")
  217. /// assert_ne!(A, B);
  218. ///
  219. /// Otherwise, you're possibly on your own to generate an appropriate generator A.
  220. /// Everyone who uses a given credential type with a given group will
  221. /// need to use the same A. You need to call this before doing any
  222. /// operations with a credential.
  223. pub fn cmz_group_init<G: PrimeGroup>(generator_A: G) {
  224. let bp = CMZBasepoints::<G>::init(generator_A);
  225. load_bp(Some(bp));
  226. }
  227. /// Get the loaded CMZBasepoints for the given group
  228. pub fn cmz_basepoints<G: PrimeGroup>() -> &'static CMZBasepoints<G> {
  229. load_bp(None)
  230. }
  231. /// Compute a public key from a private key
  232. pub fn cmz_privkey_to_pubkey<G: PrimeGroup>(privkey: &CMZPrivkey<G>) -> CMZPubkey<G> {
  233. let bp = load_bp::<G>(None);
  234. let X0: Option<G> = if privkey.muCMZ {
  235. Some(bp.mulB(&privkey.x0))
  236. } else {
  237. Some(bp.mulA(&privkey.xr) + bp.mulB(&privkey.x0))
  238. };
  239. let Xr: Option<G> = if privkey.muCMZ {
  240. Some(bp.mulA(&privkey.xr))
  241. } else {
  242. None
  243. };
  244. let X: Vec<G> = privkey.x.iter().map(|x| bp.mulA(x)).collect();
  245. CMZPubkey { X0, Xr, X }
  246. }
  247. /// The CMZCredential trait implemented by all CMZ credential struct types.
  248. pub trait CMZCredential
  249. where
  250. Self: Default + Sized,
  251. {
  252. /// The type of attributes for this credential
  253. type Scalar: PrimeField;
  254. /// The type of the coordinates of the MAC for this credential
  255. type Point: PrimeGroup;
  256. /// Produce a vector of strings containing the names of the
  257. /// attributes of this credential. (The MAC is not included.)
  258. fn attrs() -> Vec<&'static str>;
  259. /// The number of attributes in this credential
  260. fn num_attrs() -> usize;
  261. /// The attribute number for a given name as a string
  262. fn attr_num(name: &str) -> usize;
  263. /// Get a reference to one of the attributes, specified by name as a
  264. /// string.
  265. fn attr(&self, name: &str) -> &Option<Self::Scalar>;
  266. /// Get a mutable reference to one of the attributes, specified by
  267. /// name as a string.
  268. fn attr_mut(&mut self, name: &str) -> &mut Option<Self::Scalar>;
  269. /// Set the public key for this credential.
  270. fn set_pubkey(&mut self, pubkey: &CMZPubkey<Self::Point>) -> &mut Self;
  271. /// Get a copy of the public key for this credential. If the public
  272. /// key has not yet been set or computed, a pubkey with X0 == None
  273. /// will be returned.
  274. fn get_pubkey(&self) -> &CMZPubkey<Self::Point>;
  275. /// Set the private key for this credential. The public key will
  276. /// automatically be computed from the private key.
  277. fn set_privkey(&mut self, privkey: &CMZPrivkey<Self::Point>) -> &mut Self;
  278. /// Get a copy of the private key for this credential. If the
  279. /// private key has not yet been set, a privkey with an empty x
  280. /// vector will be returned.
  281. fn get_privkey(&self) -> &CMZPrivkey<Self::Point>;
  282. /// Get the element of the privkey x vector associated with the
  283. /// given field name
  284. fn privkey_x(&self, name: &str) -> Self::Scalar;
  285. /// Get the element of the pubkey X vector associated with the given
  286. /// field name
  287. fn pubkey_X(&self, name: &str) -> Self::Point;
  288. /// Generate random private and public keys for this credential
  289. /// type. muCMZ should be true if this credential will be issued
  290. /// with muCMZ protocols (and _not_ classic CMZ14 protocols).
  291. fn gen_keys(
  292. rng: &mut impl RngCore,
  293. muCMZ: bool,
  294. ) -> (CMZPrivkey<Self::Point>, CMZPubkey<Self::Point>);
  295. /// Convenience functions for the above
  296. fn cmz14_gen_keys(rng: &mut impl RngCore) -> (CMZPrivkey<Self::Point>, CMZPubkey<Self::Point>) {
  297. Self::gen_keys(rng, false)
  298. }
  299. fn mucmz_gen_keys(rng: &mut impl RngCore) -> (CMZPrivkey<Self::Point>, CMZPubkey<Self::Point>) {
  300. Self::gen_keys(rng, true)
  301. }
  302. /// Convenience function for creating a new Self, and loading the
  303. /// given private key (which will also compute the public key).
  304. fn using_privkey(privkey: &CMZPrivkey<Self::Point>) -> Self {
  305. let mut slf = Self::default();
  306. slf.set_privkey(privkey);
  307. slf
  308. }
  309. /// Convenience function for creating a new Self, and loading the
  310. /// given public key.
  311. fn using_pubkey(pubkey: &CMZPubkey<Self::Point>) -> Self {
  312. let mut slf = Self::default();
  313. slf.set_pubkey(pubkey);
  314. slf
  315. }
  316. /// Verify the MAC in this credential, given the private key. This
  317. /// is mainly useful for debugging, since the client will not have
  318. /// the private key and the issuer will typically not have the
  319. /// complete credential.
  320. fn verify_MAC(&self, privkey: &CMZPrivkey<Self::Point>) -> Result<(), ()>;
  321. /// Create a fake MAC for this credential. This is useful, for
  322. /// example, when you're doing an OR proof, and in some arms of the
  323. /// disjunction, the credential does not have to be valid.
  324. fn fake_MAC(&mut self, rng: &mut impl RngCore);
  325. }
  326. /// The CMZ macro for declaring CMZ credentials.
  327. ///
  328. /// Use this macro to declare a CMZ credential struct type.
  329. ///
  330. /// use cmz::{CMZ, CMZCred, CMZCredential, CMZPrivkey, CMZPubkey, CMZMac};
  331. /// use cmz::{cmz_privkey_to_pubkey, serde_as, SerdeScalar, Serialize, Deserialize};
  332. /// use group::Group;
  333. /// use rand_core::RngCore;
  334. /// use curve25519_dalek::ristretto::RistrettoPoint as Grp;
  335. /// CMZ!{ Name<Grp>: attr1, attr2, attr3 }
  336. ///
  337. /// will declare a struct type called `Name`, containing one field for each
  338. /// of the listed attributes. The attribute fields will be of type
  339. /// `Option<Scalar>`. It will also automatically add a field called `MAC`
  340. /// of type [`CMZMac`], and an implementation (via the `CMZCred` derive) of
  341. /// the [`CMZCredential`] trait. The mathematical group used (the field for
  342. /// the values of the attributes and the private key elements, and the group
  343. /// elements for the commitments, MAC components, and public key elements)
  344. /// is [`Grp`]. If [`Grp`] is omitted, the macro will default to using a
  345. /// group called "G", which you can define, for example, as:
  346. ///
  347. /// use curve25519_dalek::ristretto::RistrettoPoint as G;
  348. ///
  349. /// or:
  350. ///
  351. /// use curve25519_dalek::ristretto::RistrettoPoint;
  352. /// type G = RistrettoPoint;
  353. ///
  354. /// The group must implement the trait [`PrimeGroup`](https://docs.rs/group/latest/group/prime/trait.PrimeGroup.html).
  355. #[macro_export]
  356. macro_rules! CMZ {
  357. ( $name: ident < $G: ident > : $( $id: ident ),+ ) => {
  358. #[serde_as]
  359. #[derive(CMZCred,Clone,Debug,Default,Serialize,Deserialize)]
  360. #[cmzcred_group(group = $G)]
  361. pub struct $name {
  362. $(
  363. #[serde_as(as="Option<SerdeScalar>")]
  364. pub $id: Option<<$G as Group>::Scalar>,
  365. )+
  366. pub MAC: CMZMac<$G>,
  367. privkey: CMZPrivkey<$G>,
  368. pubkey: CMZPubkey<$G>,
  369. }
  370. };
  371. ( $name: ident : $( $id: ident ),+ ) => {
  372. #[serde_as]
  373. #[derive(CMZCred,Clone,Debug,Default,Serialize,Deserialize)]
  374. #[cmzcred_group(group = G)]
  375. pub struct $name {
  376. $(
  377. #[serde_as(as="Option<SerdeScalar>")]
  378. pub $id: Option<<G as Group>::Scalar>,
  379. )+
  380. pub MAC: CMZMac<G>,
  381. privkey: CMZPrivkey<G>,
  382. pubkey: CMZPubkey<G>,
  383. }
  384. };
  385. }
  386. /// The type for errors generated by the prepare, handle, and finalize
  387. /// functions generated by the CMZProtocol family of macros
  388. #[derive(Error, Debug)]
  389. pub enum CMZError {
  390. #[error("Hide attribute {1} of credential {0} was not passed to prepare")]
  391. HideAttrMissing(&'static str, &'static str),
  392. #[error("Reveal attribute {1} of credential {0} was not passed to prepare")]
  393. RevealAttrMissing(&'static str, &'static str),
  394. #[error("Implicit attribute {1} of credential {0} was not passed to prepare")]
  395. ImplicitAttrCliMissing(&'static str, &'static str),
  396. #[error("Implicit attribute {1} of credential {0} was not set by fill_creds")]
  397. ImplicitAttrIssMissing(&'static str, &'static str),
  398. #[error("Set attribute {1} of credential {0} was not set by fill_creds")]
  399. SetAttrMissing(&'static str, &'static str),
  400. #[error("private key for credential {0} was not set by fill_creds")]
  401. PrivkeyMissing(&'static str),
  402. #[error("public key for credential {0} was not passed to prepare")]
  403. PubkeyMissing(&'static str),
  404. #[error("credential initialized with wrong protocol")]
  405. WrongProtocol(&'static str),
  406. #[error("client proof did not verify")]
  407. CliProofFailed,
  408. #[error("issuer proof did not verify")]
  409. IssProofFailed,
  410. #[error("unknown CMZ proof error")]
  411. Unknown,
  412. }
  413. #[cfg(test)]
  414. mod tests {
  415. use super::*;
  416. #[test]
  417. fn lox_credential_test() {
  418. use curve25519_dalek::ristretto::RistrettoPoint as G;
  419. CMZ! { Lox:
  420. id,
  421. bucket,
  422. trust_level,
  423. level_since,
  424. invites_remaining,
  425. blockages
  426. }
  427. println!("{:#?}", Lox::attrs());
  428. let mut L = Lox::default();
  429. println!("{:#?}", L);
  430. L.bucket = Some(<G as Group>::Scalar::ONE);
  431. println!("{:#?}", L);
  432. println!("{:#?}", L.attr("bucket"));
  433. *L.attr_mut("id") = Some(<G as Group>::Scalar::ONE);
  434. println!("{:#?}", L);
  435. }
  436. }