ggm.rs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  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. // We really want points to be capital letters and scalars to be
  8. // lowercase letters
  9. #![allow(non_snake_case)]
  10. use sha2::Sha512;
  11. use curve25519_dalek::constants as dalek_constants;
  12. use curve25519_dalek::ristretto::RistrettoBasepointTable;
  13. use curve25519_dalek::ristretto::RistrettoPoint;
  14. use curve25519_dalek::scalar::Scalar;
  15. use lazy_static::lazy_static;
  16. lazy_static! {
  17. pub static ref CMZ_A: RistrettoPoint =
  18. RistrettoPoint::hash_from_bytes::<Sha512>(b"CMZ Generator A");
  19. pub static ref CMZ_B: RistrettoPoint = dalek_constants::RISTRETTO_BASEPOINT_POINT;
  20. pub static ref CMZ_A_TABLE: RistrettoBasepointTable = RistrettoBasepointTable::create(&CMZ_A);
  21. pub static ref CMZ_B_TABLE: RistrettoBasepointTable =
  22. dalek_constants::RISTRETTO_BASEPOINT_TABLE;
  23. }
  24. #[derive(Clone, Debug)]
  25. pub struct IssuerPrivKey {
  26. x0tilde: Scalar,
  27. x: Vec<Scalar>,
  28. }
  29. impl IssuerPrivKey {
  30. // Create an IssuerPrivKey for credentials with the given number of
  31. // attributes.
  32. pub fn new(n: u16) -> IssuerPrivKey {
  33. let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
  34. let x0tilde: Scalar = Scalar::random(&mut rng);
  35. let mut x: Vec<Scalar> = Vec::with_capacity((n + 1) as usize);
  36. // Set x to a vector of n+1 random Scalars
  37. x.resize_with((n + 1) as usize, || Scalar::random(&mut rng));
  38. IssuerPrivKey { x0tilde, x }
  39. }
  40. }
  41. #[derive(Clone, Debug)]
  42. pub struct IssuerPubKey {
  43. X: Vec<RistrettoPoint>,
  44. }
  45. impl IssuerPubKey {
  46. // Create an IssuerPubKey from the corresponding IssuerPrivKey
  47. pub fn new(privkey: &IssuerPrivKey) -> IssuerPubKey {
  48. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  49. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  50. let n_plus_one: usize = privkey.x.len();
  51. let mut X: Vec<RistrettoPoint> = Vec::with_capacity(n_plus_one);
  52. // The first element is a special case; it is
  53. // X[0] = x0tilde*A + x[0]*B
  54. X.push(&privkey.x0tilde * Atable + &privkey.x[0] * Btable);
  55. // The other elements (1 through n) are X[i] = x[i]*A
  56. for i in 1..n_plus_one {
  57. X.push(&privkey.x[i] * Atable);
  58. }
  59. IssuerPubKey { X }
  60. }
  61. }
  62. #[derive(Debug)]
  63. pub struct Issuer {
  64. privkey: IssuerPrivKey,
  65. pub pubkey: IssuerPubKey,
  66. }
  67. impl Issuer {
  68. // Create an issuer for credentials with the given number of
  69. // attributes
  70. pub fn new(n: u16) -> Issuer {
  71. let privkey = IssuerPrivKey::new(n);
  72. let pubkey = IssuerPubKey::new(&privkey);
  73. Issuer { privkey, pubkey }
  74. }
  75. }
  76. #[derive(Debug)]
  77. pub struct Credential {
  78. P: RistrettoPoint,
  79. Q: RistrettoPoint,
  80. // For numbering consistency with the Hyphae paper, the attributes
  81. // are stored in m[1], m[2], ... ; the m[0] element is set to the
  82. // dummy value 0.
  83. m: Vec<Scalar>,
  84. }
  85. // A submodule for issuing credentials with 5 attributes, none of which
  86. // are blinded to the issuer. We create these submodules because the
  87. // zero knowledge proofs (ZKPs) have to have the number of attributes
  88. // hardcoded. One might imagine a Rust macro that could generate
  89. // submodules like these automatically, but for now, if you need a
  90. // different number of attributes, or different combinations of blinded
  91. // attributes, it is hopefully straighforward to adapt these given ones.
  92. // Note that the "nonblind" issuing case is special: the client doesn't
  93. // do a ZKP at all. The more general blinded issuing case is the next
  94. // submodule after this one.
  95. pub mod issue_nonblind_5 {
  96. use curve25519_dalek::ristretto::RistrettoBasepointTable;
  97. use curve25519_dalek::ristretto::RistrettoPoint;
  98. use curve25519_dalek::scalar::Scalar;
  99. use curve25519_dalek::traits::IsIdentity;
  100. use zkp::CompactProof;
  101. use zkp::ProofError;
  102. use zkp::Transcript;
  103. use super::{Credential, Issuer, IssuerPubKey, CMZ_A, CMZ_B, CMZ_B_TABLE};
  104. #[derive(Debug)]
  105. pub struct CredentialRequest {
  106. m1: Scalar,
  107. m2: Scalar,
  108. m3: Scalar,
  109. m4: Scalar,
  110. m5: Scalar,
  111. }
  112. #[derive(Debug)]
  113. pub struct CredentialRequestState {
  114. m1: Scalar,
  115. m2: Scalar,
  116. m3: Scalar,
  117. m4: Scalar,
  118. m5: Scalar,
  119. }
  120. pub struct CredentialResponse {
  121. P: RistrettoPoint,
  122. Q: RistrettoPoint,
  123. piNonblindIssue: CompactProof,
  124. }
  125. define_proof! {
  126. issue,
  127. "Nonblind 5 issuing proof",
  128. (x0, x0tilde, x1, x2, x3, x4, x5),
  129. (P, Q, X0, X1, X2, X3, X4, X5, P1, P2, P3, P4, P5),
  130. (A, B) :
  131. X1 = (x1*A),
  132. X2 = (x2*A),
  133. X3 = (x3*A),
  134. X4 = (x4*A),
  135. X5 = (x5*A),
  136. X0 = (x0*B + x0tilde*A),
  137. Q = (x0*P + x1*P1 + x2*P2 + x3*P3 + x4*P4 + x5*P5)
  138. }
  139. pub fn request(
  140. m1: &Scalar,
  141. m2: &Scalar,
  142. m3: &Scalar,
  143. m4: &Scalar,
  144. m5: &Scalar,
  145. ) -> (CredentialRequest, CredentialRequestState) {
  146. // For nonblind requests, just send the attributes in the clear
  147. (
  148. CredentialRequest {
  149. m1: *m1,
  150. m2: *m2,
  151. m3: *m3,
  152. m4: *m4,
  153. m5: *m5,
  154. },
  155. CredentialRequestState {
  156. m1: *m1,
  157. m2: *m2,
  158. m3: *m3,
  159. m4: *m4,
  160. m5: *m5,
  161. },
  162. )
  163. }
  164. impl Issuer {
  165. // Issue a credential with (for example) 5 given attributes. In
  166. // this (nonblinded) version, the issuer sees all of the attributes.
  167. pub fn issue_nonblind_5(&self, req: CredentialRequest) -> CredentialResponse {
  168. let A: &RistrettoPoint = &CMZ_A;
  169. let B: &RistrettoPoint = &CMZ_B;
  170. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  171. let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
  172. let b: Scalar = Scalar::random(&mut rng);
  173. let P: RistrettoPoint = &b * Btable;
  174. // There is a typo in the Hyphae paper: in Section 4.1, Q should
  175. // also have an x0*P term (also in Q'). (You can see that term
  176. // in Section 4.2.)
  177. let Q: RistrettoPoint = (self.privkey.x[0]
  178. + (self.privkey.x[1] * req.m1
  179. + self.privkey.x[2] * req.m2
  180. + self.privkey.x[3] * req.m3
  181. + self.privkey.x[4] * req.m4
  182. + self.privkey.x[5] * req.m5))
  183. * P;
  184. let mut transcript = Transcript::new(b"Nonblind 5 issuing proof");
  185. let piNonblindIssue: CompactProof = issue::prove_compact(
  186. &mut transcript,
  187. issue::ProveAssignments {
  188. A: &A,
  189. B: &B,
  190. P: &P,
  191. Q: &Q,
  192. X0: &self.pubkey.X[0],
  193. X1: &self.pubkey.X[1],
  194. X2: &self.pubkey.X[2],
  195. X3: &self.pubkey.X[3],
  196. X4: &self.pubkey.X[4],
  197. X5: &self.pubkey.X[5],
  198. P1: &(req.m1 * P),
  199. P2: &(req.m2 * P),
  200. P3: &(req.m3 * P),
  201. P4: &(req.m4 * P),
  202. P5: &(req.m5 * P),
  203. x0: &self.privkey.x[0],
  204. x1: &self.privkey.x[1],
  205. x2: &self.privkey.x[2],
  206. x3: &self.privkey.x[3],
  207. x4: &self.privkey.x[4],
  208. x5: &self.privkey.x[5],
  209. x0tilde: &self.privkey.x0tilde,
  210. },
  211. )
  212. .0;
  213. CredentialResponse {
  214. P,
  215. Q,
  216. piNonblindIssue,
  217. }
  218. }
  219. }
  220. pub fn verify(
  221. state: CredentialRequestState,
  222. resp: CredentialResponse,
  223. pubkey: &IssuerPubKey,
  224. ) -> Result<Credential, ProofError> {
  225. let A: &RistrettoPoint = &CMZ_A;
  226. let B: &RistrettoPoint = &CMZ_B;
  227. if resp.P.is_identity() {
  228. return Err(ProofError::VerificationFailure);
  229. }
  230. let mut transcript = Transcript::new(b"Nonblind 5 issuing proof");
  231. issue::verify_compact(
  232. &resp.piNonblindIssue,
  233. &mut transcript,
  234. issue::VerifyAssignments {
  235. A: &A.compress(),
  236. B: &B.compress(),
  237. P: &resp.P.compress(),
  238. Q: &resp.Q.compress(),
  239. X0: &pubkey.X[0].compress(),
  240. X1: &pubkey.X[1].compress(),
  241. X2: &pubkey.X[2].compress(),
  242. X3: &pubkey.X[3].compress(),
  243. X4: &pubkey.X[4].compress(),
  244. X5: &pubkey.X[5].compress(),
  245. P1: &(state.m1 * resp.P).compress(),
  246. P2: &(state.m2 * resp.P).compress(),
  247. P3: &(state.m3 * resp.P).compress(),
  248. P4: &(state.m4 * resp.P).compress(),
  249. P5: &(state.m5 * resp.P).compress(),
  250. },
  251. )?;
  252. Ok(Credential {
  253. P: resp.P,
  254. Q: resp.Q,
  255. m: vec![
  256. Scalar::zero(),
  257. state.m1,
  258. state.m2,
  259. state.m3,
  260. state.m4,
  261. state.m5,
  262. ],
  263. })
  264. }
  265. }
  266. // A submodule for issuing credentials with 5 attributes, of which
  267. // attributes 1, 2, and 4 are blinded (the issuer does not see them),
  268. // and only attributes 3 and 5 are visible to the issuer.
  269. //
  270. // One might imagine generalizing this submodule using a macro.
  271. // Currently, the number of attributes and the selection of which are
  272. // blinded have to be hardcoded in order to use the very helpful zkp
  273. // proof macros. This shouldn't be a problem in practice, as one
  274. // generally knows the set of statements one will require at compile,
  275. // and not at run, time.
  276. pub mod issue_blind124_5 {
  277. use curve25519_dalek::ristretto::RistrettoBasepointTable;
  278. use curve25519_dalek::ristretto::RistrettoPoint;
  279. use curve25519_dalek::scalar::Scalar;
  280. use curve25519_dalek::traits::IsIdentity;
  281. use zkp::CompactProof;
  282. use zkp::ProofError;
  283. use zkp::Transcript;
  284. use super::{Credential, Issuer, IssuerPubKey};
  285. use super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
  286. // Example of a 5-attribute credential where the issuer sees attributes
  287. // 3 and 5, but attributes 1, 2, and 4 are blinded.
  288. pub struct CredentialRequest {
  289. D: RistrettoPoint,
  290. Encm1B: (RistrettoPoint, RistrettoPoint),
  291. Encm2B: (RistrettoPoint, RistrettoPoint),
  292. m3: Scalar,
  293. Encm4B: (RistrettoPoint, RistrettoPoint),
  294. m5: Scalar,
  295. piUserBlinding: CompactProof,
  296. }
  297. #[derive(Debug)]
  298. pub struct CredentialRequestState {
  299. d: Scalar,
  300. D: RistrettoPoint,
  301. Encm1B: (RistrettoPoint, RistrettoPoint),
  302. Encm2B: (RistrettoPoint, RistrettoPoint),
  303. Encm4B: (RistrettoPoint, RistrettoPoint),
  304. m1: Scalar,
  305. m2: Scalar,
  306. m3: Scalar,
  307. m4: Scalar,
  308. m5: Scalar,
  309. }
  310. pub struct CredentialResponse {
  311. P: RistrettoPoint,
  312. EncQ: (RistrettoPoint, RistrettoPoint),
  313. T1: RistrettoPoint,
  314. T2: RistrettoPoint,
  315. T4: RistrettoPoint,
  316. piBlindIssue: CompactProof,
  317. }
  318. // The client-created proof that the blinded attributes in the request
  319. // to issue a credential are well formed. If you want the client to
  320. // prove other statements about the blinded attributes (m1, m2, m4 in
  321. // this example), this is where to add them (and in the code that
  322. // creates and verifies this proof of course).
  323. define_proof! {
  324. userblinding,
  325. "Blind124 5 userblind proof",
  326. (d, e1, e2, e4, m1, m2, m4),
  327. (Encm1B0, Encm1B1, Encm2B0, Encm2B1, Encm4B0, Encm4B1, D),
  328. (B) :
  329. Encm1B0 = (e1*B),
  330. Encm1B1 = (m1*B + e1*D),
  331. Encm2B0 = (e2*B),
  332. Encm2B1 = (m2*B + e2*D),
  333. Encm4B0 = (e4*B),
  334. Encm4B1 = (m4*B + e4*D),
  335. D = (d*B)
  336. }
  337. // The issuer-created proof for the same scenario
  338. define_proof! {
  339. blindissue,
  340. "Blind124 5 issuing proof",
  341. (x0, x0tilde, x1, x2, x3, x4, x5, s, b, t1, t2, t4),
  342. (P, EncQ0, EncQ1, X0, X1, X2, X3, X4, X5, P3, P5, T1, T2, T4, D,
  343. Encm1B0, Encm1B1, Encm2B0, Encm2B1, Encm4B0, Encm4B1),
  344. (A, B) :
  345. X1 = (x1*A),
  346. X2 = (x2*A),
  347. X3 = (x3*A),
  348. X4 = (x4*A),
  349. X5 = (x5*A),
  350. X0 = (x0*B + x0tilde*A),
  351. P = (b*B),
  352. T1 = (b*X1),
  353. T2 = (b*X2),
  354. T4 = (b*X4),
  355. T1 = (t1*A),
  356. T2 = (t2*A),
  357. T4 = (t4*A),
  358. EncQ0 = (s*B + t1*Encm1B0 + t2*Encm2B0 + t4*Encm4B0),
  359. EncQ1 = (s*D + t1*Encm1B1 + t2*Encm2B1 + t4*Encm4B1 +
  360. x0*P + x3*P3 + x5*P5)
  361. }
  362. pub fn request(
  363. m1: &Scalar,
  364. m2: &Scalar,
  365. m3: &Scalar,
  366. m4: &Scalar,
  367. m5: &Scalar,
  368. ) -> (CredentialRequest, CredentialRequestState) {
  369. let B: &RistrettoPoint = &CMZ_B;
  370. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  371. // Pick an ElGamal keypair
  372. let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
  373. let d: Scalar = Scalar::random(&mut rng);
  374. let D: RistrettoPoint = &d * Btable;
  375. // Encrypt the attributes to be blinded (each times the
  376. // basepoint B) to the public key we just created
  377. let e1: Scalar = Scalar::random(&mut rng);
  378. let e2: Scalar = Scalar::random(&mut rng);
  379. let e4: Scalar = Scalar::random(&mut rng);
  380. let Encm1B = (&e1 * Btable, m1 * Btable + e1 * D);
  381. let Encm2B = (&e2 * Btable, m2 * Btable + e2 * D);
  382. let Encm4B = (&e4 * Btable, m4 * Btable + e4 * D);
  383. let mut transcript = Transcript::new(b"Blind124 5 userblind proof");
  384. let piUserBlinding: CompactProof = userblinding::prove_compact(
  385. &mut transcript,
  386. userblinding::ProveAssignments {
  387. B: &B,
  388. Encm1B0: &Encm1B.0,
  389. Encm1B1: &Encm1B.1,
  390. Encm2B0: &Encm2B.0,
  391. Encm2B1: &Encm2B.1,
  392. Encm4B0: &Encm4B.0,
  393. Encm4B1: &Encm4B.1,
  394. D: &D,
  395. d: &d,
  396. e1: &e1,
  397. e2: &e2,
  398. e4: &e4,
  399. m1: &m1,
  400. m2: &m2,
  401. m4: &m4,
  402. },
  403. )
  404. .0;
  405. (
  406. CredentialRequest {
  407. D,
  408. Encm1B,
  409. Encm2B,
  410. Encm4B,
  411. piUserBlinding,
  412. m3: *m3,
  413. m5: *m5,
  414. },
  415. CredentialRequestState {
  416. d,
  417. D,
  418. Encm1B,
  419. Encm2B,
  420. Encm4B,
  421. m1: *m1,
  422. m2: *m2,
  423. m3: *m3,
  424. m4: *m4,
  425. m5: *m5,
  426. },
  427. )
  428. }
  429. impl Issuer {
  430. // Issue a credential with 5 attributes, of which attributes 1, 2,
  431. // and 4 are blinded from the issuer, and 3 and 5 are visible.
  432. pub fn issue_blind124_5(
  433. &self,
  434. req: CredentialRequest,
  435. ) -> Result<CredentialResponse, ProofError> {
  436. let A: &RistrettoPoint = &CMZ_A;
  437. let B: &RistrettoPoint = &CMZ_B;
  438. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  439. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  440. // First check the proof in the request
  441. let mut transcript = Transcript::new(b"Blind124 5 userblind proof");
  442. userblinding::verify_compact(
  443. &req.piUserBlinding,
  444. &mut transcript,
  445. userblinding::VerifyAssignments {
  446. B: &B.compress(),
  447. Encm1B0: &req.Encm1B.0.compress(),
  448. Encm1B1: &req.Encm1B.1.compress(),
  449. Encm2B0: &req.Encm2B.0.compress(),
  450. Encm2B1: &req.Encm2B.1.compress(),
  451. Encm4B0: &req.Encm4B.0.compress(),
  452. Encm4B1: &req.Encm4B.1.compress(),
  453. D: &req.D.compress(),
  454. },
  455. )?;
  456. // Compute the MAC on the visible attributes
  457. let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
  458. let b: Scalar = Scalar::random(&mut rng);
  459. let P: RistrettoPoint = &b * Btable;
  460. let QHc: RistrettoPoint =
  461. (self.privkey.x[0] + (self.privkey.x[3] * req.m3 + self.privkey.x[5] * req.m5)) * P;
  462. // El Gamal encrypt it to the public key req.D
  463. let s: Scalar = Scalar::random(&mut rng);
  464. let EncQHc = (&s * Btable, QHc + s * req.D);
  465. // Homomorphically compute the part of the MAC corresponding to
  466. // the blinded attributes
  467. let t1 = self.privkey.x[1] * b;
  468. let T1 = &t1 * Atable;
  469. let EncQ1 = (t1 * req.Encm1B.0, t1 * req.Encm1B.1);
  470. let t2 = self.privkey.x[2] * b;
  471. let T2 = &t2 * Atable;
  472. let EncQ2 = (t2 * req.Encm2B.0, t2 * req.Encm2B.1);
  473. let t4 = self.privkey.x[4] * b;
  474. let T4 = &t4 * Atable;
  475. let EncQ4 = (t4 * req.Encm4B.0, t4 * req.Encm4B.1);
  476. let EncQ = (
  477. EncQHc.0 + EncQ1.0 + EncQ2.0 + EncQ4.0,
  478. EncQHc.1 + EncQ1.1 + EncQ2.1 + EncQ4.1,
  479. );
  480. let mut transcript = Transcript::new(b"Blind124 5 issuing proof");
  481. let piBlindIssue: CompactProof = blindissue::prove_compact(
  482. &mut transcript,
  483. blindissue::ProveAssignments {
  484. A: &A,
  485. B: &B,
  486. P: &P,
  487. EncQ0: &EncQ.0,
  488. EncQ1: &EncQ.1,
  489. X0: &self.pubkey.X[0],
  490. X1: &self.pubkey.X[1],
  491. X2: &self.pubkey.X[2],
  492. X3: &self.pubkey.X[3],
  493. X4: &self.pubkey.X[4],
  494. X5: &self.pubkey.X[5],
  495. P3: &(req.m3 * P),
  496. P5: &(req.m5 * P),
  497. T1: &T1,
  498. T2: &T2,
  499. T4: &T4,
  500. D: &req.D,
  501. Encm1B0: &req.Encm1B.0,
  502. Encm1B1: &req.Encm1B.1,
  503. Encm2B0: &req.Encm2B.0,
  504. Encm2B1: &req.Encm2B.1,
  505. Encm4B0: &req.Encm4B.0,
  506. Encm4B1: &req.Encm4B.1,
  507. x0: &self.privkey.x[0],
  508. x0tilde: &self.privkey.x0tilde,
  509. x1: &self.privkey.x[1],
  510. x2: &self.privkey.x[2],
  511. x3: &self.privkey.x[3],
  512. x4: &self.privkey.x[4],
  513. x5: &self.privkey.x[5],
  514. s: &s,
  515. b: &b,
  516. t1: &t1,
  517. t2: &t2,
  518. t4: &t4,
  519. },
  520. )
  521. .0;
  522. Ok(CredentialResponse {
  523. P,
  524. EncQ,
  525. T1,
  526. T2,
  527. T4,
  528. piBlindIssue,
  529. })
  530. }
  531. }
  532. pub fn verify(
  533. state: CredentialRequestState,
  534. resp: CredentialResponse,
  535. pubkey: &IssuerPubKey,
  536. ) -> Result<Credential, ProofError> {
  537. let A: &RistrettoPoint = &CMZ_A;
  538. let B: &RistrettoPoint = &CMZ_B;
  539. if resp.P.is_identity() {
  540. return Err(ProofError::VerificationFailure);
  541. }
  542. let mut transcript = Transcript::new(b"Blind124 5 issuing proof");
  543. blindissue::verify_compact(
  544. &resp.piBlindIssue,
  545. &mut transcript,
  546. blindissue::VerifyAssignments {
  547. A: &A.compress(),
  548. B: &B.compress(),
  549. P: &resp.P.compress(),
  550. EncQ0: &resp.EncQ.0.compress(),
  551. EncQ1: &resp.EncQ.1.compress(),
  552. X0: &pubkey.X[0].compress(),
  553. X1: &pubkey.X[1].compress(),
  554. X2: &pubkey.X[2].compress(),
  555. X3: &pubkey.X[3].compress(),
  556. X4: &pubkey.X[4].compress(),
  557. X5: &pubkey.X[5].compress(),
  558. P3: &(state.m3 * resp.P).compress(),
  559. P5: &(state.m5 * resp.P).compress(),
  560. T1: &resp.T1.compress(),
  561. T2: &resp.T2.compress(),
  562. T4: &resp.T4.compress(),
  563. D: &state.D.compress(),
  564. Encm1B0: &state.Encm1B.0.compress(),
  565. Encm1B1: &state.Encm1B.1.compress(),
  566. Encm2B0: &state.Encm2B.0.compress(),
  567. Encm2B1: &state.Encm2B.1.compress(),
  568. Encm4B0: &state.Encm4B.0.compress(),
  569. Encm4B1: &state.Encm4B.1.compress(),
  570. },
  571. )?;
  572. // Decrypt EncQ
  573. let Q = resp.EncQ.1 - (state.d * resp.EncQ.0);
  574. Ok(Credential {
  575. P: resp.P,
  576. Q,
  577. m: vec![
  578. Scalar::zero(),
  579. state.m1,
  580. state.m2,
  581. state.m3,
  582. state.m4,
  583. state.m5,
  584. ],
  585. })
  586. }
  587. }
  588. // A submodule for showing credentials with 5 attributes, blinding
  589. // attributes 3, 4, and 5, and displaying attributes 1 and 2. As above,
  590. // this could possibly be generated by a Rust macro in the future.
  591. pub mod show_blind345_5 {
  592. use curve25519_dalek::ristretto::RistrettoBasepointTable;
  593. use curve25519_dalek::ristretto::RistrettoPoint;
  594. use curve25519_dalek::scalar::Scalar;
  595. use curve25519_dalek::traits::IsIdentity;
  596. use zkp::CompactProof;
  597. use zkp::ProofError;
  598. use zkp::Transcript;
  599. use super::{Credential, Issuer, IssuerPubKey, CMZ_A, CMZ_A_TABLE};
  600. // A typo in the Hyphae paper (Section 4.4): P must also be sent to
  601. // the issuer in the credential presentation message.
  602. pub struct ShowMessage {
  603. P: RistrettoPoint,
  604. m1: Scalar,
  605. m2: Scalar,
  606. Cm3: RistrettoPoint,
  607. Cm4: RistrettoPoint,
  608. Cm5: RistrettoPoint,
  609. CQ: RistrettoPoint,
  610. piCredShow: CompactProof,
  611. }
  612. #[derive(Debug)]
  613. pub struct VerifiedCredential {
  614. m1: Scalar,
  615. m2: Scalar,
  616. Cm3: RistrettoPoint,
  617. Cm4: RistrettoPoint,
  618. Cm5: RistrettoPoint,
  619. }
  620. // If you want to prove additional statements about the blinded
  621. // attributes when showing them, this is the place to add those
  622. // statements (and also the code that creates and verifies this
  623. // proof).
  624. define_proof! {
  625. show,
  626. "Blind345 5 showing proof",
  627. (m3, m4, m5, z3, z4, z5, negzQ),
  628. (P, Cm3, Cm4, Cm5, V, X3, X4, X5),
  629. (A) :
  630. Cm3 = (m3*P + z3*A),
  631. Cm4 = (m4*P + z4*A),
  632. Cm5 = (m5*P + z5*A),
  633. V = (z3*X3 + z4*X4 + z5*X5 + negzQ*A)
  634. }
  635. pub fn show(cred: &Credential, pubkey: &IssuerPubKey) -> ShowMessage {
  636. let A: &RistrettoPoint = &CMZ_A;
  637. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  638. // Reblind P and Q
  639. let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
  640. let t: Scalar = Scalar::random(&mut rng);
  641. let P: RistrettoPoint = t * cred.P;
  642. let Q: RistrettoPoint = t * cred.Q;
  643. // Form Pedersen commitments to the blinded attributes
  644. let z3: Scalar = Scalar::random(&mut rng);
  645. let z4: Scalar = Scalar::random(&mut rng);
  646. let z5: Scalar = Scalar::random(&mut rng);
  647. let Cm3: RistrettoPoint = cred.m[3] * P + &z3 * Atable;
  648. let Cm4: RistrettoPoint = cred.m[4] * P + &z4 * Atable;
  649. let Cm5: RistrettoPoint = cred.m[5] * P + &z5 * Atable;
  650. // Form a Pedersen commitment to the MAC Q
  651. // We flip the sign of zQ from that of the Hyphae paper so that
  652. // the ZKP has a "+" instead of a "-", as that's what the zkp
  653. // macro supports.
  654. let negzQ: Scalar = Scalar::random(&mut rng);
  655. let CQ: RistrettoPoint = Q - &negzQ * Atable;
  656. // Compute the "error factor"
  657. let V: RistrettoPoint =
  658. z3 * pubkey.X[3] + z4 * pubkey.X[4] + z5 * pubkey.X[5] + &negzQ * Atable;
  659. // Create the ZKP
  660. let mut transcript = Transcript::new(b"Blind345 5 showing proof");
  661. let piCredShow: CompactProof = show::prove_compact(
  662. &mut transcript,
  663. show::ProveAssignments {
  664. A: &A,
  665. P: &P,
  666. Cm3: &Cm3,
  667. Cm4: &Cm4,
  668. Cm5: &Cm5,
  669. V: &V,
  670. X3: &pubkey.X[3],
  671. X4: &pubkey.X[4],
  672. X5: &pubkey.X[5],
  673. m3: &cred.m[3],
  674. m4: &cred.m[4],
  675. m5: &cred.m[5],
  676. z3: &z3,
  677. z4: &z4,
  678. z5: &z5,
  679. negzQ: &negzQ,
  680. },
  681. )
  682. .0;
  683. ShowMessage {
  684. P,
  685. m1: cred.m[1],
  686. m2: cred.m[2],
  687. Cm3,
  688. Cm4,
  689. Cm5,
  690. CQ,
  691. piCredShow,
  692. }
  693. }
  694. impl Issuer {
  695. // Verify a showing of an attribute from a user to the issuer
  696. // with 5 credentials, of which attributes 3, 4, and 5 are
  697. // blinded, and attributes 1 and 2 are revealed. The issuer
  698. // will end up with verified Pedersen commitments Cm3, Cm4, Cm5
  699. // to the blinded attributes, so that additional things can be
  700. // proved about those attributes in zero knowledge if desired.
  701. pub fn verify_blind345_5(
  702. &self,
  703. showmsg: ShowMessage,
  704. ) -> Result<VerifiedCredential, ProofError> {
  705. let A: &RistrettoPoint = &CMZ_A;
  706. if showmsg.P.is_identity() {
  707. return Err(ProofError::VerificationFailure);
  708. }
  709. // Recompute the "error factor" using knowledge of our own
  710. // (the issuer's) private key instead of knowledge of the
  711. // hidden attributes
  712. let Vprime: RistrettoPoint = (self.privkey.x[0]
  713. + (self.privkey.x[1] * showmsg.m1 + self.privkey.x[2] * showmsg.m2))
  714. * showmsg.P
  715. + self.privkey.x[3] * showmsg.Cm3
  716. + self.privkey.x[4] * showmsg.Cm4
  717. + self.privkey.x[5] * showmsg.Cm5
  718. - showmsg.CQ;
  719. // Verify the ZKP using Vprime
  720. let mut transcript = Transcript::new(b"Blind345 5 showing proof");
  721. show::verify_compact(
  722. &showmsg.piCredShow,
  723. &mut transcript,
  724. show::VerifyAssignments {
  725. A: &A.compress(),
  726. P: &showmsg.P.compress(),
  727. Cm3: &showmsg.Cm3.compress(),
  728. Cm4: &showmsg.Cm4.compress(),
  729. Cm5: &showmsg.Cm5.compress(),
  730. V: &Vprime.compress(),
  731. X3: &self.pubkey.X[3].compress(),
  732. X4: &self.pubkey.X[4].compress(),
  733. X5: &self.pubkey.X[5].compress(),
  734. },
  735. )?;
  736. Ok(VerifiedCredential {
  737. m1: showmsg.m1,
  738. m2: showmsg.m2,
  739. Cm3: showmsg.Cm3,
  740. Cm4: showmsg.Cm4,
  741. Cm5: showmsg.Cm5,
  742. })
  743. }
  744. }
  745. }