lib.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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::CMZCred;
  5. use core::any::Any;
  6. use ff::PrimeField;
  7. use generic_static::StaticTypeMap;
  8. use group::prime::PrimeGroup;
  9. use group::{Group, WnafBase, WnafScalar};
  10. use lazy_static::lazy_static;
  11. use rand_core::RngCore;
  12. /// The CMZMac struct represents a MAC on a CMZ credential.
  13. #[derive(Copy, Clone, Debug, Default)]
  14. pub struct CMZMac<G: PrimeGroup> {
  15. pub P: G,
  16. pub Q: G,
  17. }
  18. /// The CMZPrivkey struct represents a CMZ private key
  19. #[derive(Clone, Debug, Default)]
  20. pub struct CMZPrivkey<G: PrimeGroup> {
  21. pub x0tilde: <G as Group>::Scalar,
  22. pub x0: <G as Group>::Scalar,
  23. // The elements of x correspond to the attributes of the credential
  24. pub x: Vec<<G as Group>::Scalar>,
  25. }
  26. /// The CMZPubkey struct represents a CMZ public key
  27. #[derive(Clone, Debug, Default)]
  28. pub struct CMZPubkey<G: PrimeGroup> {
  29. pub X0: Option<G>,
  30. // The elements of X correspond to the attributes of the credential
  31. pub X: Vec<G>,
  32. }
  33. // The size of the WNAF windows. Larger sizes take more memory, but
  34. // result in faster multiplications.
  35. const WNAF_SIZE: usize = 6;
  36. // A struct (generic over G) holding the two CMZ bases, and their Wnaf
  37. // basepoint tables
  38. #[derive(Clone)]
  39. struct CMZBasepoints<G: Group> {
  40. A: G,
  41. B: G,
  42. A_TABLE: WnafBase<G, WNAF_SIZE>,
  43. B_TABLE: WnafBase<G, WNAF_SIZE>,
  44. }
  45. impl<G: Group> CMZBasepoints<G> {
  46. fn init(generator_A: G) -> Self {
  47. let A = generator_A;
  48. let B = G::generator();
  49. let A_TABLE = WnafBase::new(A);
  50. let B_TABLE = WnafBase::new(B);
  51. CMZBasepoints {
  52. A,
  53. B,
  54. A_TABLE,
  55. B_TABLE,
  56. }
  57. }
  58. fn mulA(&self, s: &G::Scalar) -> G {
  59. let wnaf_s = WnafScalar::<G::Scalar, WNAF_SIZE>::new(&s);
  60. &self.A_TABLE * &wnaf_s
  61. }
  62. fn mulB(&self, s: &G::Scalar) -> G {
  63. let wnaf_s = WnafScalar::<G::Scalar, WNAF_SIZE>::new(&s);
  64. &self.B_TABLE * &wnaf_s
  65. }
  66. }
  67. // What's going on here needs some explanation. For each group G, we
  68. // want to pre-compute the WnafBase tables in a CMZBasepoints<G> struct,
  69. // and we want that pre-computed struct to remain globally accessible.
  70. // So ideally, we'd just have a generic static CMZBasepoints<G> struct,
  71. // and instantiate it once for each G that we use.
  72. //
  73. // The tricky bit is that we don't know what group(s) G the programmer
  74. // (the person using this cmz crate) will end up using, and Rust doesn't
  75. // support generic statics.
  76. //
  77. // So what we'd like is a non-generic static _map_ that maps a group
  78. // type G to the precomputed CMZBasepoints<G> struct. But types aren't
  79. // values that can be mapped by a normal HashMap. Luckily, there's a
  80. // generic_static crate that provides a StaticTypeMap that has the
  81. // ability to map types to objects.
  82. //
  83. // However, all of those *mapped-to* objects have to all be of the same
  84. // type, whereas we want the type G to map to a struct of type
  85. // CMZBasepoints<G>, which is different for each value of G.
  86. //
  87. // So we make a non-generic trait CMZBP that all instantiations of
  88. // CMZBasepoints<G> implement (for all group types G), and have the
  89. // StaticTypeMap map each type G to a trait object Box<dyn CMZBP>.
  90. //
  91. // Then to read the CMZBasepoints<G> back out, we look up the trait
  92. // object in the StaticTypeMap, yielding a Box<dyn CMZBP>. We now need
  93. // to downcast this trait object to the concrete type CMZBasepoints<G>,
  94. // for a _specific_ G. Rust provides downcasting, but only from &dyn Any
  95. // to the original concrete type, not from other things like &dyn CMZBP.
  96. // So first we need to upcast the trait object to &dyn Any, which we do
  97. // with an "as_any()" function in the CMZBP trait, and then downcast the
  98. // result to a CMZBasepoints<G> struct.
  99. //
  100. // The up/down casting pattern is from
  101. // https://stackoverflow.com/questions/33687447/how-to-get-a-reference-to-a-concrete-type-from-a-trait-object
  102. // Static objects have to be Sync + Send, so enforce that as part of the
  103. // CMXBP trait
  104. trait CMZBP: Sync + Send {
  105. fn as_any(&self) -> &dyn Any;
  106. }
  107. impl<G: Group> CMZBP for CMZBasepoints<G> {
  108. fn as_any(&self) -> &dyn Any {
  109. self
  110. }
  111. }
  112. // The StaticTypeMap mapping group types G to trait objects Box<dyn CMZBP>
  113. lazy_static! {
  114. static ref basepoints_map: StaticTypeMap<Box<dyn CMZBP>> = StaticTypeMap::new();
  115. }
  116. /// For a given group type G, if bp is Some(b), then load the mapping
  117. /// from G to b into the basepoints_map. (If a mapping from G already
  118. /// exists, the old one will be kept and the new one ignored.) Whether
  119. /// bp is Some(b) or None, this function returns the (possibly new)
  120. /// target of the basepoints_map, as a &'static CMZBasepoints<G>.
  121. fn load_bp<G: Group>(bp: Option<CMZBasepoints<G>>) -> &'static CMZBasepoints<G> {
  122. match bp {
  123. Some(b) => basepoints_map.call_once::<Box<dyn CMZBP>, _>(|| Box::new(b.clone())),
  124. None => {
  125. basepoints_map.call_once::<Box<dyn CMZBP>, _>(|| panic!("basepoints uninitialized"))
  126. }
  127. }
  128. .as_any()
  129. .downcast_ref::<CMZBasepoints<G>>()
  130. .unwrap()
  131. }
  132. /// CMZ credentials require two generators, A and B. B is the
  133. /// "standard" generator. A can be any other generator (that is, any
  134. /// other non-identity point in a prime-order group), but it is required
  135. /// that no one know the discrete log between A and B. So you can't
  136. /// generate A by multiplying B by some scalar, for example. If your
  137. /// group has a hash_from_bytes function, then pass
  138. /// hash_from_bytes::<Sha512>(b"CMZ Generator A"). Otherwise, you're
  139. /// possibly on your own to generate an appropriate generator A.
  140. /// Everyone who uses a given credential type with a given group will
  141. /// need to use the same A. You need to call this before doing any
  142. /// operations with a credential.
  143. pub fn cmz_group_init<G: PrimeGroup>(generator_A: G) {
  144. let bp = CMZBasepoints::<G>::init(generator_A);
  145. load_bp(Some(bp));
  146. }
  147. /// Compute a public key from a private key
  148. pub fn cmz_privkey_to_pubkey<G: PrimeGroup>(privkey: &CMZPrivkey<G>) -> CMZPubkey<G> {
  149. let bp = load_bp::<G>(None);
  150. let X0: Option<G> = Some(bp.mulA(&privkey.x0tilde) + bp.mulB(&privkey.x0));
  151. let X: Vec<G> = privkey.x.iter().map(|x| bp.mulA(x)).collect();
  152. CMZPubkey { X0, X }
  153. }
  154. /// The CMZCredential trait implemented by all CMZ credential struct types.
  155. pub trait CMZCredential
  156. where
  157. Self: Default + Sized,
  158. {
  159. /// The type of attributes for this credential
  160. type Scalar: PrimeField;
  161. /// The type of the coordinates of the MAC for this credential
  162. type Point: PrimeGroup;
  163. /// Produce a vector of strings containing the names of the
  164. /// attributes of this credential. (The MAC is not included.)
  165. fn attrs() -> Vec<&'static str>;
  166. /// The number of attributes in this credential
  167. fn num_attrs() -> usize;
  168. /// Get a reference to one of the attributes, specified by name as a
  169. /// string.
  170. fn attr(&self, name: &str) -> &Option<Self::Scalar>;
  171. /// Get a mutable reference to one of the attributes, specified by
  172. /// name as a string.
  173. fn attr_mut(&mut self, name: &str) -> &mut Option<Self::Scalar>;
  174. /// Set the public key for this credential.
  175. fn set_pubkey(&mut self, pubkey: &CMZPubkey<Self::Point>) -> &mut Self;
  176. /// Get a copy of the public key for this credential. If the public
  177. /// key has not yet been set or computed, a pubkey with X0 == None
  178. /// will be returned.
  179. fn get_pubkey(&self) -> CMZPubkey<Self::Point>;
  180. /// Set the private key for this credential. The public key will
  181. /// automatically be computed from the private key.
  182. fn set_privkey(&mut self, privkey: &CMZPrivkey<Self::Point>) -> &mut Self;
  183. /// Get a copy of the private key for this credential. If the
  184. /// private key has not yet been set, a privkey with an empty x
  185. /// vector will be returned.
  186. fn get_privkey(&self) -> CMZPrivkey<Self::Point>;
  187. /// Generate random private and public keys for this credential
  188. /// type.
  189. fn gen_keys(rng: &mut impl RngCore) -> (CMZPrivkey<Self::Point>, CMZPubkey<Self::Point>);
  190. /// Convenience function for creating a new Self, and loading the
  191. /// given private key (which will also compute the public key).
  192. fn using_privkey(privkey: &CMZPrivkey<Self::Point>) -> Self {
  193. let mut slf = Self::default();
  194. slf.set_privkey(privkey);
  195. slf
  196. }
  197. /// Convenience function for creating a new Self, and loading the
  198. /// given public key.
  199. fn using_pubkey(pubkey: &CMZPubkey<Self::Point>) -> Self {
  200. let mut slf = Self::default();
  201. slf.set_pubkey(pubkey);
  202. slf
  203. }
  204. }
  205. /** The CMZ macro for declaring CMZ credentials.
  206. Use this macro to declare a CMZ credential struct type.
  207. `CMZ!{ Name<Group>: attr1, attr2, attr3 }`
  208. will declare a struct type called `Name`, containing one field for each
  209. of the listed attributes. The attribute fields will be of type
  210. `Option<Scalar>`. It will also automatically add a field called `MAC`
  211. of type `CMZMac`, and an implementation (via the `CMZCred` derive) of
  212. the `CMZCredential` trait. The mathematical group used (the field for
  213. the values of the attributes and the private key elements, and the group
  214. elements for the commitments, MAC components, and public key elements)
  215. is Group (which must satisfy the group::Group trait). If "<Group>" is
  216. omitted, the macro will default to using a group called "G", which you
  217. can define, for example, as:
  218. use curve25519_dalek::ristretto::RistrettoPoint as G;
  219. or:
  220. use curve25519_dalek::ristretto::RistrettoPoint;
  221. type G = RistrettoPoint;
  222. The group must implement the trait group::prime::PrimeGroup.
  223. */
  224. #[macro_export]
  225. macro_rules! CMZ {
  226. ( $name: ident < $G: ident > : $( $id: ident ),+ ) => {
  227. #[derive(CMZCred,Clone,Debug,Default)]
  228. #[cmzcred_group(group = $G)]
  229. pub struct $name {
  230. $(
  231. pub $id: Option<<$G as Group>::Scalar>,
  232. )+
  233. pub MAC: CMZMac<$G>,
  234. privkey: CMZPrivkey<$G>,
  235. pubkey: CMZPubkey<$G>,
  236. }
  237. };
  238. ( $name: ident : $( $id: ident ),+ ) => {
  239. #[derive(CMZCred,Clone,Debug,Default)]
  240. #[cmzcred_group(group = G)]
  241. pub struct $name {
  242. $(
  243. pub $id: Option<<G as Group>::Scalar>,
  244. )+
  245. pub MAC: CMZMac<G>,
  246. privkey: CMZPrivkey<G>,
  247. pubkey: CMZPubkey<G>,
  248. }
  249. };
  250. }
  251. #[cfg(test)]
  252. mod tests {
  253. use super::*;
  254. #[test]
  255. fn lox_credential_test() {
  256. use curve25519_dalek::ristretto::RistrettoPoint as G;
  257. CMZ! { Lox:
  258. id,
  259. bucket,
  260. trust_level,
  261. level_since,
  262. invites_remaining,
  263. blockages
  264. }
  265. println!("{:#?}", Lox::attrs());
  266. let mut L = Lox::default();
  267. println!("{:#?}", L);
  268. L.bucket = Some(<G as Group>::Scalar::ONE);
  269. println!("{:#?}", L);
  270. println!("{:#?}", L.attr("bucket"));
  271. *L.attr_mut("id") = Some(<G as Group>::Scalar::ONE);
  272. println!("{:#?}", L);
  273. }
  274. }