wallet.rs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // We want Scalars to be lowercase letters, and Points and credentials
  2. // to be capital letters
  3. #![allow(non_snake_case)]
  4. use cmz::*;
  5. use curve25519_dalek::ristretto::RistrettoPoint as G;
  6. use group::Group;
  7. use rand::{CryptoRng, RngCore};
  8. CMZ! { Wallet: randid, balance }
  9. CMZ! { Item: serialno, price }
  10. // Define two versions of each protocol and test: one for CMZ14 and one for µCMZ
  11. macro_rules! protos_def {
  12. ($proto_macro:tt, $wallet_issue:ident, $item_issue:ident,
  13. $wallet_spend:ident, $wallet_spend_with_fee:ident,
  14. $gen_keys:ident, $test_wallet: ident) => {
  15. $proto_macro! { $wallet_issue,
  16. ,
  17. W: Wallet { randid: J, balance: S },
  18. }
  19. $proto_macro! { $item_issue,
  20. ,
  21. I: Item { serialno: S, price: S },
  22. }
  23. $proto_macro! { $wallet_spend,
  24. [ W: Wallet { randid: R, balance: H },
  25. I: Item { serialno: H, price: H } ],
  26. N: Wallet { randid: J, balance: H },
  27. (0..=100000000).contains(N.balance),
  28. W.balance = N.balance + I.price
  29. }
  30. $proto_macro! { $wallet_spend_with_fee<fee>,
  31. [ W: Wallet { randid: R, balance: H },
  32. I: Item { serialno: H, price: H } ],
  33. N: Wallet { randid: J, balance: H },
  34. (0..=100000000).contains(N.balance),
  35. W.balance = N.balance + I.price + fee
  36. }
  37. #[test]
  38. fn $test_wallet() -> Result<(), CMZError> {
  39. // The issuer runs this on its own to create an Item credential for a
  40. // particular item (specified by a serial number) with a given price.
  41. fn issue_item(
  42. rng: &mut (impl CryptoRng + RngCore),
  43. serialno: u128,
  44. price: u128,
  45. privkey: &CMZPrivkey<G>,
  46. public: &CMZPubkey<G>,
  47. ) -> Result<Item, CMZError> {
  48. let (request, state) =
  49. $item_issue::prepare(&mut *rng, b"issue_item", Item::using_pubkey(public))?;
  50. let (reply, _) = $item_issue::handle(
  51. &mut *rng,
  52. b"issue_item",
  53. request,
  54. |I: &mut Item| {
  55. I.set_privkey(privkey);
  56. I.serialno = Some(serialno.into());
  57. I.price = Some(price.into());
  58. Ok(())
  59. },
  60. |_I: &Item| Ok(()),
  61. )?;
  62. let res = state.finalize(reply);
  63. match res {
  64. Ok(c) => Ok(c),
  65. Err((err, _state)) => Err(err),
  66. }
  67. }
  68. // The issuer runs this on its own to create an initial wallet loaded
  69. // with funds, to sent to a client. The issuer will presumably charge
  70. // the client out of band for this loaded wallet.
  71. fn issue_wallet(
  72. rng: &mut (impl CryptoRng + RngCore),
  73. balance: u128,
  74. privkey: &CMZPrivkey<G>,
  75. public: &CMZPubkey<G>,
  76. ) -> Result<Wallet, CMZError> {
  77. let (request, state) = $wallet_issue::prepare(
  78. &mut *rng,
  79. b"issue_wallet",
  80. Wallet::using_pubkey(public),
  81. )?;
  82. let (reply, _) = $wallet_issue::handle(
  83. &mut *rng,
  84. b"issue_wallet",
  85. request,
  86. |W: &mut Wallet| {
  87. W.set_privkey(privkey);
  88. W.balance = Some(balance.into());
  89. Ok(())
  90. },
  91. |_W: &Wallet| Ok(()),
  92. )?;
  93. let res = state.finalize(reply);
  94. match res {
  95. Ok(c) => Ok(c),
  96. Err((err, _state)) => Err(err),
  97. }
  98. }
  99. let mut rng = rand::thread_rng();
  100. // Issuer: generate private and public keys for each type of
  101. // credential. (The client gets a copy of the public keys.)
  102. let (wallet_priv, wallet_pub) = Wallet::$gen_keys(&mut rng);
  103. let (item_priv, item_pub) = Item::$gen_keys(&mut rng);
  104. // The issuer makes some Item credentials for various items by just
  105. // executing the issuing protocol with itself
  106. let ebook_item = issue_item(&mut rng, 100, 2995, &item_priv, &item_pub)?;
  107. let album_item = issue_item(&mut rng, 200, 995, &item_priv, &item_pub)?;
  108. // The verify_MAC function is only usable for debugging, since the
  109. // client will not have the private key and the issuer will
  110. // typically not have the complete credential. But we'll use it
  111. // here to check that the credentials we get back are correct.
  112. ebook_item.verify_MAC(&item_priv).unwrap();
  113. album_item.verify_MAC(&item_priv).unwrap();
  114. // In exchange for out of band funds, the issuer generates a loaded
  115. // wallet and sends it to the client.
  116. let initial_wallet = issue_wallet(&mut rng, 10000, &wallet_priv, &wallet_pub)?;
  117. initial_wallet.verify_MAC(&wallet_priv).unwrap();
  118. // Buy an item (no fee version)
  119. // client actions
  120. let mut N = Wallet::using_pubkey(&wallet_pub);
  121. N.balance = Some(initial_wallet.balance.unwrap() - ebook_item.price.unwrap());
  122. let (request, state) =
  123. $wallet_spend::prepare(&mut rng, b"test_wallet", &initial_wallet, &ebook_item, N)?;
  124. let reqbytes = request.as_bytes();
  125. // issuer actions
  126. let recvreq = $wallet_spend::Request::try_from(&reqbytes[..]).unwrap();
  127. let (reply, (_W_issuer, _I_issuer, _N_issuer)) = $wallet_spend::handle(
  128. &mut rng,
  129. b"test_wallet",
  130. recvreq,
  131. |W: &mut Wallet, I: &mut Item, N: &mut Wallet| {
  132. W.set_privkey(&wallet_priv);
  133. I.set_privkey(&item_priv);
  134. N.set_privkey(&wallet_priv);
  135. Ok(())
  136. },
  137. // This callback should test that W.randid has never been seen
  138. // before, but it doesn't currently do that.
  139. |_W: &Wallet, _I: &Item, _N: &Wallet| Ok(()),
  140. )?;
  141. let replybytes = reply.as_bytes();
  142. // client actions
  143. let recvreply = $wallet_spend::Reply::try_from(&replybytes[..]).unwrap();
  144. let W_issued = state.finalize(recvreply).unwrap();
  145. W_issued.verify_MAC(&wallet_priv).unwrap();
  146. // The version of the protocol parameterized by a fee. The client
  147. // and issue must agree on the params.
  148. let params = $wallet_spend_with_fee::Params { fee: 5u128.into() };
  149. // client actions
  150. let mut N_fee = Wallet::using_pubkey(&wallet_pub);
  151. N_fee.balance =
  152. Some(W_issued.balance.unwrap() - album_item.price.unwrap() - params.fee);
  153. let (request_fee, state_fee) = $wallet_spend_with_fee::prepare(
  154. &mut rng,
  155. b"test_wallet_fee",
  156. &W_issued,
  157. &album_item,
  158. N_fee,
  159. &params,
  160. )?;
  161. let reqbytes_fee = request_fee.as_bytes();
  162. // issuer actions
  163. let recvreq_fee = $wallet_spend_with_fee::Request::try_from(&reqbytes_fee[..]).unwrap();
  164. let (reply_fee, (_W_fee_issuer, _I_fee_isser, _N_fee_issuer)) =
  165. $wallet_spend_with_fee::handle(
  166. &mut rng,
  167. b"test_wallet_fee",
  168. recvreq_fee,
  169. |W: &mut Wallet, I: &mut Item, N: &mut Wallet| {
  170. W.set_privkey(&wallet_priv);
  171. I.set_privkey(&item_priv);
  172. N.set_privkey(&wallet_priv);
  173. Ok($wallet_spend_with_fee::Params { fee: params.fee })
  174. },
  175. // This callback should test that W.randid has never been seen
  176. // before, but it doesn't currently do that.
  177. |_W: &Wallet, _I: &Item, _N: &Wallet| Ok(()),
  178. )?;
  179. let replybytes_fee = reply_fee.as_bytes();
  180. // client actions
  181. let recvreply_fee =
  182. $wallet_spend_with_fee::Reply::try_from(&replybytes_fee[..]).unwrap();
  183. let W_issued_fee = state_fee.finalize(recvreply_fee).unwrap();
  184. W_issued_fee.verify_MAC(&wallet_priv).unwrap();
  185. Ok(())
  186. }
  187. };
  188. }
  189. protos_def! {CMZ14Protocol, cmz14_wallet_issue, cmz14_item_issue, cmz14_wallet_spend,
  190. cmz14_wallet_spend_with_fee, cmz14_gen_keys, test_cmz14_wallet}
  191. protos_def! {muCMZProtocol, mu_wallet_issue, mu_item_issue,
  192. mu_wallet_spend, mu_wallet_spend_with_fee, mucmz_gen_keys, test_mucmz_wallet}