note.rs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. use crypto_api_chachapoly::ChachaPolyIetf;
  2. use ff::Field;
  3. use std::io;
  4. use rand::rngs::OsRng;
  5. use crate::serial::{Encodable, Decodable, ReadExt, WriteExt};
  6. use crate::error::{Error, Result};
  7. use super::diffie_hellman::{sapling_ka_agree, kdf_sapling};
  8. pub const NOTE_PLAINTEXT_SIZE: usize =
  9. 32 + // serial
  10. 8 + // value
  11. 32 + // coin_blind
  12. 32; // valcom_blind
  13. pub const AEAD_TAG_SIZE: usize = 16;
  14. pub const ENC_CIPHERTEXT_SIZE: usize = NOTE_PLAINTEXT_SIZE + AEAD_TAG_SIZE;
  15. pub struct Note {
  16. pub serial: jubjub::Fr,
  17. pub value: u64,
  18. pub coin_blind: jubjub::Fr,
  19. pub valcom_blind: jubjub::Fr,
  20. }
  21. impl Encodable for Note {
  22. fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
  23. let mut len = 0;
  24. len += self.serial.encode(&mut s)?;
  25. len += self.value.encode(&mut s)?;
  26. len += self.coin_blind.encode(&mut s)?;
  27. len += self.valcom_blind.encode(&mut s)?;
  28. Ok(len)
  29. }
  30. }
  31. impl Decodable for Note {
  32. fn decode<D: io::Read>(mut d: D) -> Result<Self> {
  33. Ok(Self {
  34. serial: Decodable::decode(&mut d)?,
  35. value: Decodable::decode(&mut d)?,
  36. coin_blind: Decodable::decode(&mut d)?,
  37. valcom_blind: Decodable::decode(d)?
  38. })
  39. }
  40. }
  41. impl Note {
  42. pub fn encrypt(&self, public: &jubjub::SubgroupPoint) -> Result<EncryptedNote> {
  43. let ephem_secret = jubjub::Fr::random(&mut OsRng);
  44. let ephem_public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * ephem_secret;
  45. let shared_secret = sapling_ka_agree(&ephem_secret, public.into());
  46. let key = kdf_sapling(shared_secret, &ephem_public.into());
  47. let mut input = Vec::new();
  48. self.encode(&mut input)?;
  49. let mut ciphertext = [0u8; ENC_CIPHERTEXT_SIZE];
  50. assert_eq!(
  51. ChachaPolyIetf::aead_cipher()
  52. .seal_to(&mut ciphertext, &input, &[], key.as_ref(), &[0u8; 12])
  53. .unwrap(),
  54. ENC_CIPHERTEXT_SIZE
  55. );
  56. Ok(EncryptedNote {
  57. ciphertext,
  58. ephem_public
  59. })
  60. }
  61. }
  62. pub struct EncryptedNote {
  63. ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
  64. ephem_public: jubjub::SubgroupPoint
  65. }
  66. impl EncryptedNote {
  67. pub fn decrypt(&self, secret: &jubjub::Fr) -> Result<Note> {
  68. let shared_secret = sapling_ka_agree(&secret, &self.ephem_public.into());
  69. let key = kdf_sapling(shared_secret, &self.ephem_public.into());
  70. let mut plaintext = [0; ENC_CIPHERTEXT_SIZE];
  71. assert_eq!(
  72. ChachaPolyIetf::aead_cipher()
  73. .open_to(
  74. &mut plaintext,
  75. &self.ciphertext,
  76. &[],
  77. key.as_ref(),
  78. &[0u8; 12]
  79. )
  80. .map_err(|_| Error::NoteDecryptionFailed)?,
  81. NOTE_PLAINTEXT_SIZE
  82. );
  83. Note::decode(&plaintext[..])
  84. }
  85. }
  86. #[test]
  87. fn test_note_encdec() {
  88. let note = Note {
  89. serial: jubjub::Fr::random(&mut OsRng),
  90. value: 110,
  91. coin_blind: jubjub::Fr::random(&mut OsRng),
  92. valcom_blind: jubjub::Fr::random(&mut OsRng),
  93. };
  94. let secret = jubjub::Fr::random(&mut OsRng);
  95. let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
  96. let encrypted_note = note.encrypt(&public).unwrap();
  97. let note2 = encrypted_note.decrypt(&secret).unwrap();
  98. assert_eq!(note.value, note2.value);
  99. }