use super::*;
impl<A: Aleo> Record<A, Ciphertext<A>> {
pub fn decrypt(&self, view_key: &ViewKey<A>) -> Record<A, Plaintext<A>> {
let record_view_key = (&**view_key * &self.nonce).to_x_coordinate();
let record = self.decrypt_symmetric_unchecked(record_view_key);
A::assert_eq(view_key.to_address(), record.owner().deref()).expect("View key must match record owner");
record
}
pub fn decrypt_symmetric_unchecked(&self, record_view_key: Field<A>) -> Record<A, Plaintext<A>> {
let num_randomizers = self.num_randomizers();
let randomizers = A::hash_many_psd8(&[A::encryption_domain(), record_view_key], num_randomizers);
self.decrypt_with_randomizers(&randomizers)
}
fn decrypt_with_randomizers(&self, randomizers: &[Field<A>]) -> Record<A, Plaintext<A>> {
let mut index: usize = 0;
let owner = match self.owner.is_public().eject_value() {
true => self.owner.decrypt(&[]),
false => self.owner.decrypt(&[randomizers[index].clone()]),
};
if owner.is_private().eject_value() {
index += 1;
}
let mut decrypted_data = IndexMap::with_capacity(self.data.len());
for (id, entry, num_randomizers) in self.data.iter().map(|(id, entry)| (id, entry, entry.num_randomizers())) {
let randomizers = &randomizers[index..index + num_randomizers as usize];
let entry = match entry {
Entry::Constant(plaintext) => Entry::Constant(plaintext.clone()),
Entry::Public(plaintext) => Entry::Public(plaintext.clone()),
Entry::Private(private) => Entry::Private(private.decrypt_with_randomizers(randomizers)),
};
if decrypted_data.insert(id.clone(), entry).is_some() {
A::halt(format!("Duplicate identifier in record: {id}"))
}
index += num_randomizers as usize;
}
Record { owner, data: decrypted_data, nonce: self.nonce.clone(), version: self.version.clone() }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Circuit, Literal};
use snarkvm_circuit_types::{Address, Field};
use snarkvm_utilities::{TestRng, Uniform};
use anyhow::Result;
const ITERATIONS: u64 = 10;
fn check_encrypt_and_decrypt<A: Aleo>(
view_key: &ViewKey<A>,
owner: Owner<A, Plaintext<A>>,
rng: &mut TestRng,
) -> Result<()> {
let randomizer = Scalar::new(Mode::Private, Uniform::rand(rng));
let record = Record {
owner,
data: IndexMap::from_iter(vec![
(
Identifier::from_str("a")?,
Entry::Private(Plaintext::from(Literal::Field(Field::new(Mode::Private, Uniform::rand(rng))))),
),
(
Identifier::from_str("b")?,
Entry::Private(Plaintext::from(Literal::Scalar(Scalar::new(Mode::Private, Uniform::rand(rng))))),
),
]),
nonce: A::g_scalar_multiply(&randomizer),
version: U8::new(Mode::Private, Uniform::rand(rng)),
};
let ciphertext = record.encrypt(&randomizer);
assert_eq!(record.eject(), ciphertext.decrypt(view_key).eject());
Ok(())
}
#[test]
fn test_encrypt_and_decrypt() -> Result<()> {
let mut rng = TestRng::default();
for _ in 0..ITERATIONS {
let private_key = snarkvm_console_account::PrivateKey::<<Circuit as Environment>::Network>::new(&mut rng)?;
let view_key = snarkvm_console_account::ViewKey::try_from(private_key)?;
let address = snarkvm_console_account::Address::try_from(private_key)?;
let view_key = ViewKey::<Circuit>::new(Mode::Private, view_key);
let owner = address;
{
let owner = Owner::Public(Address::<Circuit>::new(Mode::Public, owner));
check_encrypt_and_decrypt::<Circuit>(&view_key, owner, &mut rng)?;
}
{
let owner =
Owner::Private(Plaintext::from(Literal::Address(Address::<Circuit>::new(Mode::Private, owner))));
check_encrypt_and_decrypt::<Circuit>(&view_key, owner, &mut rng)?;
}
}
Ok(())
}
}