1use aes::Aes256;
20use ctr::Ctr64BE;
21use ctr::cipher::{KeyIvInit, StreamCipher};
22use hkdf::Hkdf;
23use num_bigint::BigUint;
24use sha2::{Digest, Sha256};
25
26use crate::encoding::biguint_to_be_32;
27use crate::field::{Fr, fr_from_be_bytes_mod};
28
29const NOTE_KEY_SALT: &[u8] = b"curvy/agg-note/v1";
30const NOTE_KEY_INFO: &[u8] = b"curvy/agg-note/v1:amount+token";
31const KEYSTREAM_BYTES: usize = 64;
32
33type Aes256Ctr64BE = Ctr64BE<Aes256>;
34
35fn derive_note_key(shared_secret: &BigUint) -> [u8; 32] {
41 let hk = Hkdf::<Sha256>::new(Some(NOTE_KEY_SALT), &biguint_to_be_32(shared_secret));
42 let mut okm = [0u8; 32];
43 hk.expand(NOTE_KEY_INFO, &mut okm)
44 .expect("hkdf expand to 32 bytes");
45 okm
46}
47
48fn derive_counter_block(ephemeral_key: (&BigUint, &BigUint)) -> [u8; 16] {
49 let mut h = Sha256::new();
50 h.update(biguint_to_be_32(ephemeral_key.0));
51 h.update(biguint_to_be_32(ephemeral_key.1));
52 let digest = h.finalize();
53 let mut counter = [0u8; 16];
54 counter.copy_from_slice(&digest[0..16]);
55 counter
56}
57
58fn ctr_keystream_fields(shared_secret: &BigUint, ephemeral_key: (&BigUint, &BigUint)) -> (Fr, Fr) {
60 let key = derive_note_key(shared_secret);
61 let counter = derive_counter_block(ephemeral_key);
62
63 let mut ks = [0u8; KEYSTREAM_BYTES];
64 let mut cipher =
65 Aes256Ctr64BE::new_from_slices(&key, &counter).expect("valid AES-256 key + 16-byte IV");
66 cipher.apply_keystream(&mut ks);
67
68 (
69 fr_from_be_bytes_mod(&ks[0..32]),
70 fr_from_be_bytes_mod(&ks[32..64]),
71 )
72}
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
76pub struct EncryptedAmountToken {
77 pub encrypted_amount: Fr,
78 pub encrypted_token: Fr,
79}
80
81pub fn encrypt_amount_token(
85 amount: Fr,
86 token: Fr,
87 shared_secret: &BigUint,
88 ephemeral_key: (&BigUint, &BigUint),
89) -> EncryptedAmountToken {
90 let (ks_amount, ks_token) = ctr_keystream_fields(shared_secret, ephemeral_key);
91 EncryptedAmountToken {
92 encrypted_amount: amount + ks_amount,
93 encrypted_token: token + ks_token,
94 }
95}
96
97pub fn decrypt_amount_token(
100 encrypted_amount: Fr,
101 encrypted_token: Fr,
102 shared_secret: &BigUint,
103 ephemeral_key: (&BigUint, &BigUint),
104) -> (Fr, Fr) {
105 let (ks_amount, ks_token) = ctr_keystream_fields(shared_secret, ephemeral_key);
106 (encrypted_amount - ks_amount, encrypted_token - ks_token)
107}