ggm.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. // Implementation of CMZ14 credentials (GGM version, which is more
  2. // efficient, but makes a stronger security assumption): "Algebraic MACs
  3. // and Keyed-Verification Anonymous Credentials" (Chase, Meiklejohn,
  4. // and Zaverucha, CCS 2014)
  5. // The notation follows that of the paper "Hyphae: Social Secret
  6. // Sharing" (Lovecruft and de Valence, 2017), Section 4.
  7. #![allow(non_snake_case)]
  8. #![allow(non_camel_case_types)]
  9. use sha2::Sha512;
  10. use curve25519_dalek::constants as dalek_constants;
  11. use curve25519_dalek::ristretto::RistrettoPoint;
  12. use curve25519_dalek::ristretto::RistrettoBasepointTable;
  13. use curve25519_dalek::scalar::Scalar;
  14. use curve25519_dalek::traits::IsIdentity;
  15. use zkp::CompactProof;
  16. use zkp::ProofError;
  17. use zkp::Transcript;
  18. use lazy_static::lazy_static;
  19. lazy_static! {
  20. pub static ref CMZ_A: RistrettoPoint =
  21. RistrettoPoint::hash_from_bytes::<Sha512>(b"CMZ Generator A");
  22. pub static ref CMZ_B: RistrettoPoint =
  23. dalek_constants::RISTRETTO_BASEPOINT_POINT;
  24. pub static ref CMZ_A_TABLE: RistrettoBasepointTable =
  25. RistrettoBasepointTable::create(&CMZ_A);
  26. pub static ref CMZ_B_TABLE: RistrettoBasepointTable =
  27. dalek_constants::RISTRETTO_BASEPOINT_TABLE;
  28. }
  29. #[derive(Clone,Debug)]
  30. pub struct IssuerPrivKey {
  31. x0tilde: Scalar,
  32. x: Vec<Scalar>,
  33. }
  34. impl IssuerPrivKey {
  35. // Create an IssuerPrivKey for credentials with the given number of
  36. // attributes.
  37. pub fn new(n: u16) -> IssuerPrivKey {
  38. let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
  39. let x0tilde: Scalar = Scalar::random(&mut rng);
  40. let mut x: Vec<Scalar> = Vec::with_capacity((n+1) as usize);
  41. // Set x to a vector of n+1 random Scalars
  42. x.resize_with((n+1) as usize, || { Scalar::random(&mut rng) });
  43. IssuerPrivKey { x0tilde, x }
  44. }
  45. }
  46. #[derive(Clone,Debug)]
  47. pub struct IssuerPubKey {
  48. X: Vec<RistrettoPoint>,
  49. }
  50. impl IssuerPubKey {
  51. // Create an IssuerPubKey from the corresponding IssuerPrivKey
  52. pub fn new(privkey: &IssuerPrivKey) -> IssuerPubKey {
  53. let Atable : &RistrettoBasepointTable = &CMZ_A_TABLE;
  54. let Btable : &RistrettoBasepointTable = &CMZ_B_TABLE;
  55. let n_plus_one: usize = privkey.x.len();
  56. let mut X: Vec<RistrettoPoint> = Vec::with_capacity(n_plus_one);
  57. // The first element is a special case; it is
  58. // X[0] = x0tilde*A + x[0]*B
  59. X.push(&privkey.x0tilde * Atable + &privkey.x[0] * Btable);
  60. // The other elements (1 through n) are X[i] = x[i]*A
  61. for i in 1..n_plus_one {
  62. X.push(&privkey.x[i] * Atable);
  63. }
  64. IssuerPubKey { X }
  65. }
  66. }
  67. #[derive(Debug)]
  68. pub struct Issuer {
  69. privkey: IssuerPrivKey,
  70. pub pubkey: IssuerPubKey,
  71. }
  72. define_proof! {
  73. issue_nonblind_5,
  74. "Nonblind 5 issuing proof",
  75. (x0, x0tilde, x1, x2, x3, x4, x5),
  76. (P, Q, X0, X1, X2, X3, X4, X5, P1, P2, P3, P4, P5),
  77. (A, B) :
  78. X1 = (x1*A),
  79. X2 = (x2*A),
  80. X3 = (x3*A),
  81. X4 = (x4*A),
  82. X5 = (x5*A),
  83. X0 = (x0*B + x0tilde*A),
  84. Q = (x0*P + x1*P1 + x2*P2 + x3*P3 + x4*P4 + x5*P5)
  85. }
  86. impl Issuer {
  87. // Create an issuer for credentials with the given number of
  88. // attributes
  89. pub fn new(n: u16) -> Issuer {
  90. let privkey = IssuerPrivKey::new(n);
  91. let pubkey = IssuerPubKey::new(&privkey);
  92. Issuer { privkey, pubkey }
  93. }
  94. // Issue a credential with (for example) 5 given attributes. In
  95. // this (nonblinded) version, the issuer sees all of the attributes.
  96. pub fn issue_nonblind_5(&self, req: &CredentialRequest_Nonblind_5)
  97. -> CredentialResponse_Nonblind_5 {
  98. let A : &RistrettoPoint = &CMZ_A;
  99. let B : &RistrettoPoint = &CMZ_B;
  100. let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
  101. let b: Scalar = Scalar::random(&mut rng);
  102. let P: RistrettoPoint = b * B;
  103. // There is a typo in the Hyphae paper: in Section 4.1, Q should
  104. // also have an x0*P term (also in Q'). (You can see that term
  105. // in Section 4.2.)
  106. let Q: RistrettoPoint = (self.privkey.x[0] + (
  107. self.privkey.x[1] * req.m1 +
  108. self.privkey.x[2] * req.m2 +
  109. self.privkey.x[3] * req.m3 +
  110. self.privkey.x[4] * req.m4 +
  111. self.privkey.x[5] * req.m5)) * P;
  112. let mut transcript = Transcript::new(b"Nonblind 5 issuing proof");
  113. let pi: CompactProof = issue_nonblind_5::prove_compact(
  114. &mut transcript,
  115. issue_nonblind_5::ProveAssignments {
  116. A: &A,
  117. B: &B,
  118. P: &P,
  119. Q: &Q,
  120. X0: &self.pubkey.X[0],
  121. X1: &self.pubkey.X[1],
  122. X2: &self.pubkey.X[2],
  123. X3: &self.pubkey.X[3],
  124. X4: &self.pubkey.X[4],
  125. X5: &self.pubkey.X[5],
  126. P1: &(&req.m1 * &P),
  127. P2: &(&req.m2 * &P),
  128. P3: &(&req.m3 * &P),
  129. P4: &(&req.m4 * &P),
  130. P5: &(&req.m5 * &P),
  131. x0: &self.privkey.x[0],
  132. x1: &self.privkey.x[1],
  133. x2: &self.privkey.x[2],
  134. x3: &self.privkey.x[3],
  135. x4: &self.privkey.x[4],
  136. x5: &self.privkey.x[5],
  137. x0tilde: &self.privkey.x0tilde
  138. }).0;
  139. CredentialResponse_Nonblind_5 { P, Q, pi }
  140. }
  141. }
  142. #[derive(Debug)]
  143. pub struct CredentialRequest_Nonblind_5 {
  144. m1: Scalar,
  145. m2: Scalar,
  146. m3: Scalar,
  147. m4: Scalar,
  148. m5: Scalar,
  149. }
  150. pub struct CredentialResponse_Nonblind_5 {
  151. P: RistrettoPoint,
  152. Q: RistrettoPoint,
  153. pi: CompactProof,
  154. }
  155. // Example of a 5-attribute credential where the issuer sees attributes
  156. // 3 and 5, but attributes 1, 2, and 4 are blinded.
  157. pub struct CredentialRequest_Blind124_5 {
  158. D: RistrettoPoint,
  159. Encm1B: (RistrettoPoint, RistrettoPoint),
  160. Encm2B: (RistrettoPoint, RistrettoPoint),
  161. m3: Scalar,
  162. Encm4B: (RistrettoPoint, RistrettoPoint),
  163. m5: Scalar,
  164. piUserBlinding: CompactProof,
  165. }
  166. #[derive(Debug)]
  167. pub struct CredentialRequestState_Blind124_5 {
  168. d: Scalar,
  169. D: RistrettoPoint,
  170. Encm1B: (RistrettoPoint, RistrettoPoint),
  171. Encm2B: (RistrettoPoint, RistrettoPoint),
  172. Encm4B: (RistrettoPoint, RistrettoPoint),
  173. m1: Scalar,
  174. m2: Scalar,
  175. m3: Scalar,
  176. m4: Scalar,
  177. m5: Scalar,
  178. }
  179. pub struct CredentialResponse_Blind124_5 {
  180. P: RistrettoPoint,
  181. EncQ: (RistrettoPoint, RistrettoPoint),
  182. T1: RistrettoPoint,
  183. T2: RistrettoPoint,
  184. T4: RistrettoPoint,
  185. piBlindIssue: CompactProof,
  186. }
  187. #[derive(Debug)]
  188. pub struct Credential_5 {
  189. P: RistrettoPoint,
  190. Q: RistrettoPoint,
  191. m1: Scalar,
  192. m2: Scalar,
  193. m3: Scalar,
  194. m4: Scalar,
  195. m5: Scalar,
  196. }
  197. pub fn request_nonblind_5(m1: &Scalar, m2: &Scalar, m3: &Scalar,
  198. m4: &Scalar, m5: &Scalar) -> CredentialRequest_Nonblind_5 {
  199. // For nonblind requests, just send the attributes in the clear
  200. CredentialRequest_Nonblind_5 {
  201. m1: *m1,
  202. m2: *m2,
  203. m3: *m3,
  204. m4: *m4,
  205. m5: *m5
  206. }
  207. }
  208. pub fn verify_nonblind_5(req: &CredentialRequest_Nonblind_5,
  209. resp: CredentialResponse_Nonblind_5, pubkey: &IssuerPubKey)
  210. -> Result<Credential_5, ProofError> {
  211. let A : &RistrettoPoint = &CMZ_A;
  212. let B : &RistrettoPoint = &CMZ_B;
  213. if resp.P.is_identity() {
  214. return Err(ProofError::VerificationFailure);
  215. }
  216. let mut transcript = Transcript::new(b"Nonblind 5 issuing proof");
  217. issue_nonblind_5::verify_compact(
  218. &resp.pi,
  219. &mut transcript,
  220. issue_nonblind_5::VerifyAssignments {
  221. A: &A.compress(),
  222. B: &B.compress(),
  223. P: &resp.P.compress(),
  224. Q: &resp.Q.compress(),
  225. X0: &pubkey.X[0].compress(),
  226. X1: &pubkey.X[1].compress(),
  227. X2: &pubkey.X[2].compress(),
  228. X3: &pubkey.X[3].compress(),
  229. X4: &pubkey.X[4].compress(),
  230. X5: &pubkey.X[5].compress(),
  231. P1: &(&req.m1 * &resp.P).compress(),
  232. P2: &(&req.m2 * &resp.P).compress(),
  233. P3: &(&req.m3 * &resp.P).compress(),
  234. P4: &(&req.m4 * &resp.P).compress(),
  235. P5: &(&req.m5 * &resp.P).compress(),
  236. }
  237. )?;
  238. Ok(Credential_5 {
  239. P: resp.P,
  240. Q: resp.Q,
  241. m1: req.m1,
  242. m2: req.m2,
  243. m3: req.m3,
  244. m4: req.m4,
  245. m5: req.m5,
  246. })
  247. }
  248. // The client-created proof that the blinded attributes in the request
  249. // to issue a credential are well formed. If you want the client to
  250. // prove other statements about the blinded attributes (m1, m2, m4 in
  251. // this example), this is where to add them (and in the code that
  252. // creates and verifies this proof of course).
  253. define_proof! {
  254. userblinding_124_5,
  255. "Blind124 5 userblind proof",
  256. (d, e1, e2, e4, m1, m2, m4),
  257. (Encm1B0, Encm1B1, Encm2B0, Encm2B1, Encm4B0, Encm4B1, D),
  258. (B) :
  259. Encm1B0 = (e1*B),
  260. Encm1B1 = (m1*B + e1*D),
  261. Encm2B0 = (e2*B),
  262. Encm2B1 = (m2*B + e2*D),
  263. Encm4B0 = (e4*B),
  264. Encm4B1 = (m4*B + e4*D),
  265. D = (d*B)
  266. }
  267. // The issuer-created proof for the same scenario
  268. define_proof! {
  269. blindissue_124_5,
  270. "Blind124 5 issuing proof",
  271. (x0, x0tilde, x1, x2, x3, x4, x5, s, b, t1, t2, t4),
  272. (P, EncQ0, EncQ1, X0, X1, X2, X3, X4, X5, P3, P5, T1, T2, T4, D,
  273. Encm1B0, Encm1B1, Encm2B0, Encm2B1, Encm4B0, Encm4B1),
  274. (A, B) :
  275. X1 = (x1*A),
  276. X2 = (x2*A),
  277. X3 = (x3*A),
  278. X4 = (x4*A),
  279. X5 = (x5*A),
  280. X0 = (x0*B + x0tilde*A),
  281. P = (b*B),
  282. T1 = (b*X1),
  283. T2 = (b*X2),
  284. T4 = (b*X4),
  285. T1 = (t1*A),
  286. T2 = (t2*A),
  287. T4 = (t4*A),
  288. EncQ0 = (s*B + t1*Encm1B0 + t2*Encm2B0 + t4*Encm4B0),
  289. EncQ1 = (s*D + t1*Encm1B1 + t2*Encm2B1 + t4*Encm4B1 +
  290. x0*P + x3*P3 + x5*P5)
  291. }
  292. pub fn request_blind124_5(m1: &Scalar, m2: &Scalar, m3: &Scalar,
  293. m4: &Scalar, m5: &Scalar) -> (CredentialRequest_Blind124_5,
  294. CredentialRequestState_Blind124_5) {
  295. let B : &RistrettoPoint = &CMZ_B;
  296. let Btable : &RistrettoBasepointTable = &CMZ_B_TABLE;
  297. // Pick an ElGamal keypair
  298. let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
  299. let d: Scalar = Scalar::random(&mut rng);
  300. let D: RistrettoPoint = &d * Btable;
  301. let e1: Scalar = Scalar::random(&mut rng);
  302. let e2: Scalar = Scalar::random(&mut rng);
  303. let e4: Scalar = Scalar::random(&mut rng);
  304. let Encm1B = (&e1 * Btable, m1 * Btable + &e1 * D);
  305. let Encm2B = (&e2 * Btable, m2 * Btable + &e2 * D);
  306. let Encm4B = (&e4 * Btable, m4 * Btable + &e4 * D);
  307. let mut transcript = Transcript::new(b"Blind124 5 userblind proof");
  308. let piUserBlinding: CompactProof = userblinding_124_5::prove_compact(
  309. &mut transcript,
  310. userblinding_124_5::ProveAssignments {
  311. B: &B,
  312. Encm1B0: &Encm1B.0,
  313. Encm1B1: &Encm1B.1,
  314. Encm2B0: &Encm2B.0,
  315. Encm2B1: &Encm2B.1,
  316. Encm4B0: &Encm4B.0,
  317. Encm4B1: &Encm4B.1,
  318. D: &D,
  319. d: &d,
  320. e1: &e1,
  321. e2: &e2,
  322. e4: &e4,
  323. m1: &m1,
  324. m2: &m2,
  325. m4: &m4,
  326. }).0;
  327. (
  328. CredentialRequest_Blind124_5 {
  329. D: D,
  330. Encm1B, Encm2B, Encm4B, piUserBlinding,
  331. m3: *m3,
  332. m5: *m5,
  333. },
  334. CredentialRequestState_Blind124_5 {
  335. d, D, Encm1B, Encm2B, Encm4B,
  336. m1: *m1,
  337. m2: *m2,
  338. m3: *m3,
  339. m4: *m4,
  340. m5: *m5,
  341. }
  342. )
  343. }
  344. impl Issuer {
  345. // Issue a credential with 5 attributes, of which attributes 1, 2,
  346. // and 4 are blinded from the issuer, and 3 and 5 are visible.
  347. pub fn issue_blind124_5(&self, req: &CredentialRequest_Blind124_5)
  348. -> Result<CredentialResponse_Blind124_5, ProofError> {
  349. let A : &RistrettoPoint = &CMZ_A;
  350. let B : &RistrettoPoint = &CMZ_B;
  351. // First check the proof in the request
  352. let mut transcript = Transcript::new(b"Blind124 5 userblind proof");
  353. userblinding_124_5::verify_compact(
  354. &req.piUserBlinding,
  355. &mut transcript,
  356. userblinding_124_5::VerifyAssignments {
  357. B: &B.compress(),
  358. Encm1B0: &req.Encm1B.0.compress(),
  359. Encm1B1: &req.Encm1B.1.compress(),
  360. Encm2B0: &req.Encm2B.0.compress(),
  361. Encm2B1: &req.Encm2B.1.compress(),
  362. Encm4B0: &req.Encm4B.0.compress(),
  363. Encm4B1: &req.Encm4B.1.compress(),
  364. D: &req.D.compress(),
  365. }
  366. )?;
  367. // Compute the MAC on the visible attributes
  368. let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
  369. let b: Scalar = Scalar::random(&mut rng);
  370. let P: RistrettoPoint = b * B;
  371. let QHc: RistrettoPoint = (self.privkey.x[0] + (
  372. self.privkey.x[3] * req.m3 +
  373. self.privkey.x[5] * req.m5)) * P;
  374. // El Gamal encrypt it to the public key req.D
  375. let s: Scalar = Scalar::random(&mut rng);
  376. let EncQHc = (s*B, QHc + s*req.D);
  377. // Homomorphically compute the part of the MAC corresponding to
  378. // the blinded attributes
  379. let t1 = self.privkey.x[1] * b;
  380. let T1 = t1 * A;
  381. let EncQ1 = ( t1 * req.Encm1B.0, t1 * req.Encm1B.1 );
  382. let t2 = self.privkey.x[2] * b;
  383. let T2 = t2 * A;
  384. let EncQ2 = ( t2 * req.Encm2B.0, t2 * req.Encm2B.1 );
  385. let t4 = self.privkey.x[4] * b;
  386. let T4 = t4 * A;
  387. let EncQ4 = ( t4 * req.Encm4B.0, t4 * req.Encm4B.1 );
  388. let EncQ = ( EncQHc.0 + EncQ1.0 + EncQ2.0 + EncQ4.0,
  389. EncQHc.1 + EncQ1.1 + EncQ2.1 + EncQ4.1 );
  390. let mut transcript = Transcript::new(b"Blind124 5 issuing proof");
  391. let piBlindIssue: CompactProof = blindissue_124_5::prove_compact(
  392. &mut transcript,
  393. blindissue_124_5::ProveAssignments {
  394. A: &A,
  395. B: &B,
  396. P: &P,
  397. EncQ0: &EncQ.0,
  398. EncQ1: &EncQ.1,
  399. X0: &self.pubkey.X[0],
  400. X1: &self.pubkey.X[1],
  401. X2: &self.pubkey.X[2],
  402. X3: &self.pubkey.X[3],
  403. X4: &self.pubkey.X[4],
  404. X5: &self.pubkey.X[5],
  405. P3: &(&req.m3 * &P),
  406. P5: &(&req.m5 * &P),
  407. T1: &T1,
  408. T2: &T2,
  409. T4: &T4,
  410. D: &req.D,
  411. Encm1B0: &req.Encm1B.0,
  412. Encm1B1: &req.Encm1B.1,
  413. Encm2B0: &req.Encm2B.0,
  414. Encm2B1: &req.Encm2B.1,
  415. Encm4B0: &req.Encm4B.0,
  416. Encm4B1: &req.Encm4B.1,
  417. x0: &self.privkey.x[0],
  418. x0tilde: &self.privkey.x0tilde,
  419. x1: &self.privkey.x[1],
  420. x2: &self.privkey.x[2],
  421. x3: &self.privkey.x[3],
  422. x4: &self.privkey.x[4],
  423. x5: &self.privkey.x[5],
  424. s: &s,
  425. b: &b,
  426. t1: &t1,
  427. t2: &t2,
  428. t4: &t4
  429. }).0;
  430. Ok(CredentialResponse_Blind124_5 {
  431. P, EncQ, T1, T2, T4, piBlindIssue
  432. })
  433. }
  434. }
  435. pub fn verify_blind124_5(state: CredentialRequestState_Blind124_5,
  436. resp: CredentialResponse_Blind124_5, pubkey: &IssuerPubKey)
  437. -> Result<Credential_5, ProofError> {
  438. let A : &RistrettoPoint = &CMZ_A;
  439. let B : &RistrettoPoint = &CMZ_B;
  440. if resp.P.is_identity() {
  441. return Err(ProofError::VerificationFailure);
  442. }
  443. let mut transcript = Transcript::new(b"Blind124 5 issuing proof");
  444. blindissue_124_5::verify_compact(
  445. &resp.piBlindIssue,
  446. &mut transcript,
  447. blindissue_124_5::VerifyAssignments {
  448. A: &A.compress(),
  449. B: &B.compress(),
  450. P: &resp.P.compress(),
  451. EncQ0: &resp.EncQ.0.compress(),
  452. EncQ1: &resp.EncQ.1.compress(),
  453. X0: &pubkey.X[0].compress(),
  454. X1: &pubkey.X[1].compress(),
  455. X2: &pubkey.X[2].compress(),
  456. X3: &pubkey.X[3].compress(),
  457. X4: &pubkey.X[4].compress(),
  458. X5: &pubkey.X[5].compress(),
  459. P3: &(&state.m3 * &resp.P).compress(),
  460. P5: &(&state.m5 * &resp.P).compress(),
  461. T1: &resp.T1.compress(),
  462. T2: &resp.T2.compress(),
  463. T4: &resp.T4.compress(),
  464. D: &state.D.compress(),
  465. Encm1B0: &state.Encm1B.0.compress(),
  466. Encm1B1: &state.Encm1B.1.compress(),
  467. Encm2B0: &state.Encm2B.0.compress(),
  468. Encm2B1: &state.Encm2B.1.compress(),
  469. Encm4B0: &state.Encm4B.0.compress(),
  470. Encm4B1: &state.Encm4B.1.compress(),
  471. }
  472. )?;
  473. // Decrypt EncQ
  474. let Q = &resp.EncQ.1 - (state.d * &resp.EncQ.0);
  475. Ok(Credential_5 {
  476. P: resp.P,
  477. Q: Q,
  478. m1: state.m1,
  479. m2: state.m2,
  480. m3: state.m3,
  481. m4: state.m4,
  482. m5: state.m5,
  483. })
  484. }