use aes::Aes256;
use ctr::Ctr64BE;
use ctr::cipher::{KeyIvInit, StreamCipher};
use hkdf::Hkdf;
use num_bigint::BigUint;
use sha2::{Digest, Sha256};
use crate::encoding::biguint_to_be_32;
use crate::field::{Fr, fr_from_be_bytes_mod};
const NOTE_KEY_SALT: &[u8] = b"curvy/agg-note/v1";
const NOTE_KEY_INFO: &[u8] = b"curvy/agg-note/v1:amount+token";
const KEYSTREAM_BYTES: usize = 64;
type Aes256Ctr64BE = Ctr64BE<Aes256>;
fn derive_note_key(shared_secret: &BigUint) -> [u8; 32] {
let hk = Hkdf::<Sha256>::new(Some(NOTE_KEY_SALT), &biguint_to_be_32(shared_secret));
let mut okm = [0u8; 32];
hk.expand(NOTE_KEY_INFO, &mut okm)
.expect("hkdf expand to 32 bytes");
okm
}
fn derive_counter_block(ephemeral_key: (&BigUint, &BigUint)) -> [u8; 16] {
let mut h = Sha256::new();
h.update(biguint_to_be_32(ephemeral_key.0));
h.update(biguint_to_be_32(ephemeral_key.1));
let digest = h.finalize();
let mut counter = [0u8; 16];
counter.copy_from_slice(&digest[0..16]);
counter
}
fn ctr_keystream_fields(shared_secret: &BigUint, ephemeral_key: (&BigUint, &BigUint)) -> (Fr, Fr) {
let key = derive_note_key(shared_secret);
let counter = derive_counter_block(ephemeral_key);
let mut ks = [0u8; KEYSTREAM_BYTES];
let mut cipher =
Aes256Ctr64BE::new_from_slices(&key, &counter).expect("valid AES-256 key + 16-byte IV");
cipher.apply_keystream(&mut ks);
(
fr_from_be_bytes_mod(&ks[0..32]),
fr_from_be_bytes_mod(&ks[32..64]),
)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EncryptedAmountToken {
pub encrypted_amount: Fr,
pub encrypted_token: Fr,
}
pub fn encrypt_amount_token(
amount: Fr,
token: Fr,
shared_secret: &BigUint,
ephemeral_key: (&BigUint, &BigUint),
) -> EncryptedAmountToken {
let (ks_amount, ks_token) = ctr_keystream_fields(shared_secret, ephemeral_key);
EncryptedAmountToken {
encrypted_amount: amount + ks_amount,
encrypted_token: token + ks_token,
}
}
pub fn decrypt_amount_token(
encrypted_amount: Fr,
encrypted_token: Fr,
shared_secret: &BigUint,
ephemeral_key: (&BigUint, &BigUint),
) -> (Fr, Fr) {
let (ks_amount, ks_token) = ctr_keystream_fields(shared_secret, ephemeral_key);
(encrypted_amount - ks_amount, encrypted_token - ks_token)
}