note.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. use crypto_api_chachapoly::ChachaPolyIetf;
  2. use rand::rngs::OsRng;
  3. use crate::{
  4. crypto::{
  5. diffie_hellman::{kdf_sapling, sapling_ka_agree},
  6. keypair::{PublicKey, SecretKey},
  7. types::{DrkCoinBlind, DrkSerial, DrkTokenId, DrkValueBlind},
  8. },
  9. util::serial::{Decodable, Encodable, SerialDecodable, SerialEncodable},
  10. Error, Result,
  11. };
  12. /// Plaintext size is serial + value + token_id + coin_blind + value_blind
  13. pub const NOTE_PLAINTEXT_SIZE: usize = 32 + 8 + 32 + 32 + 32 + 32;
  14. pub const AEAD_TAG_SIZE: usize = 16;
  15. pub const ENC_CIPHERTEXT_SIZE: usize = NOTE_PLAINTEXT_SIZE + AEAD_TAG_SIZE;
  16. #[derive(Copy, Clone, Debug, PartialEq, Eq, SerialEncodable, SerialDecodable)]
  17. pub struct Note {
  18. pub serial: DrkSerial,
  19. pub value: u64,
  20. pub token_id: DrkTokenId,
  21. pub coin_blind: DrkCoinBlind,
  22. pub value_blind: DrkValueBlind,
  23. pub token_blind: DrkValueBlind,
  24. }
  25. impl Note {
  26. pub fn encrypt(&self, public: &PublicKey) -> Result<EncryptedNote> {
  27. let ephem_secret = SecretKey::random(&mut OsRng);
  28. let ephem_public = PublicKey::from_secret(ephem_secret);
  29. let shared_secret = sapling_ka_agree(&ephem_secret, public);
  30. let key = kdf_sapling(&shared_secret, &ephem_public);
  31. let mut input = Vec::new();
  32. self.encode(&mut input)?;
  33. let mut ciphertext = [0u8; ENC_CIPHERTEXT_SIZE];
  34. assert_eq!(
  35. ChachaPolyIetf::aead_cipher()
  36. .seal_to(&mut ciphertext, &input, &[], key.as_ref(), &[0u8; 12])
  37. .unwrap(),
  38. ENC_CIPHERTEXT_SIZE
  39. );
  40. Ok(EncryptedNote { ciphertext, ephem_public })
  41. }
  42. }
  43. #[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
  44. pub struct EncryptedNote {
  45. ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
  46. ephem_public: PublicKey,
  47. }
  48. impl EncryptedNote {
  49. pub fn decrypt(&self, secret: &SecretKey) -> Result<Note> {
  50. let shared_secret = sapling_ka_agree(secret, &self.ephem_public);
  51. let key = kdf_sapling(&shared_secret, &self.ephem_public);
  52. let mut plaintext = [0; ENC_CIPHERTEXT_SIZE];
  53. assert_eq!(
  54. ChachaPolyIetf::aead_cipher()
  55. .open_to(&mut plaintext, &self.ciphertext, &[], key.as_ref(), &[0u8; 12])
  56. .map_err(|_| Error::NoteDecryptionFailed)?,
  57. NOTE_PLAINTEXT_SIZE
  58. );
  59. Note::decode(&plaintext[..])
  60. }
  61. }
  62. #[cfg(test)]
  63. mod tests {
  64. use super::*;
  65. use crate::crypto::keypair::Keypair;
  66. use group::ff::Field;
  67. #[test]
  68. fn test_note_encdec() {
  69. let note = Note {
  70. serial: DrkSerial::random(&mut OsRng),
  71. value: 110,
  72. token_id: DrkTokenId::random(&mut OsRng),
  73. coin_blind: DrkCoinBlind::random(&mut OsRng),
  74. value_blind: DrkValueBlind::random(&mut OsRng),
  75. token_blind: DrkValueBlind::random(&mut OsRng),
  76. };
  77. let keypair = Keypair::random(&mut OsRng);
  78. let encrypted_note = note.encrypt(&keypair.public).unwrap();
  79. let note2 = encrypted_note.decrypt(&keypair.secret).unwrap();
  80. assert_eq!(note.value, note2.value);
  81. assert_eq!(note.token_id, note2.token_id);
  82. assert_eq!(note.token_blind, note2.token_blind);
  83. }
  84. }