snarkos_cli/commands/developer/
decrypt.rsuse snarkvm::{
console::{
network::{CanaryV0, MainnetV0, Network, TestnetV0},
program::Ciphertext,
},
prelude::{Record, ViewKey},
};
use anyhow::{Result, bail};
use clap::Parser;
use std::str::FromStr;
use zeroize::Zeroize;
#[derive(Debug, Parser, Zeroize)]
pub struct Decrypt {
#[clap(default_value = "0", long = "network")]
pub network: u16,
#[clap(short, long)]
pub ciphertext: String,
#[clap(short, long)]
pub view_key: String,
}
impl Decrypt {
pub fn parse(self) -> Result<String> {
match self.network {
MainnetV0::ID => Self::decrypt_ciphertext::<MainnetV0>(&self.ciphertext, &self.view_key),
TestnetV0::ID => Self::decrypt_ciphertext::<TestnetV0>(&self.ciphertext, &self.view_key),
CanaryV0::ID => Self::decrypt_ciphertext::<CanaryV0>(&self.ciphertext, &self.view_key),
unknown_id => bail!("Unknown network ID ({unknown_id})"),
}
}
fn decrypt_ciphertext<N: Network>(ciphertext: &str, view_key: &str) -> Result<String> {
let ciphertext_record = Record::<N, Ciphertext<N>>::from_str(ciphertext)?;
let view_key = ViewKey::<N>::from_str(view_key)?;
match ciphertext_record.decrypt(&view_key) {
Ok(plaintext_record) => Ok(plaintext_record.to_string()),
Err(_) => bail!("Invalid view key for the provided record ciphertext"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use indexmap::IndexMap;
use snarkvm::prelude::{
Address,
Entry,
Field,
Identifier,
Literal,
Network,
Owner,
Plaintext,
PrivateKey,
Scalar,
TestRng,
Uniform,
ViewKey,
};
type CurrentNetwork = MainnetV0;
const ITERATIONS: usize = 1000;
fn construct_ciphertext<N: Network>(
view_key: ViewKey<N>,
owner: Owner<N, Plaintext<N>>,
rng: &mut TestRng,
) -> Result<Record<N, Ciphertext<N>>> {
let randomizer = Scalar::rand(rng);
let record = Record::<N, Plaintext<N>>::from_plaintext(
owner,
IndexMap::from_iter(
vec![
(Identifier::from_str("a")?, Entry::Private(Plaintext::from(Literal::Field(Field::rand(rng))))),
(Identifier::from_str("b")?, Entry::Private(Plaintext::from(Literal::Scalar(Scalar::rand(rng))))),
]
.into_iter(),
),
N::g_scalar_multiply(&randomizer),
)?;
let ciphertext = record.encrypt(randomizer)?;
assert_eq!(record, ciphertext.decrypt(&view_key)?);
Ok(ciphertext)
}
#[test]
fn test_decryption() {
let mut rng = TestRng::default();
for _ in 0..ITERATIONS {
let private_key = PrivateKey::<CurrentNetwork>::new(&mut rng).unwrap();
let view_key = ViewKey::try_from(private_key).unwrap();
let address = Address::try_from(private_key).unwrap();
let owner = Owner::Private(Plaintext::from(Literal::Address(address)));
let ciphertext = construct_ciphertext(view_key, owner, &mut rng).unwrap();
let expected_plaintext = ciphertext.decrypt(&view_key).unwrap();
let decrypt = Decrypt { network: 0, ciphertext: ciphertext.to_string(), view_key: view_key.to_string() };
let plaintext = decrypt.parse().unwrap();
assert_eq!(plaintext, expected_plaintext.to_string());
}
}
#[test]
fn test_failed_decryption() {
let mut rng = TestRng::default();
let incorrect_private_key = PrivateKey::<CurrentNetwork>::new(&mut rng).unwrap();
let incorrect_view_key = ViewKey::try_from(incorrect_private_key).unwrap();
for _ in 0..ITERATIONS {
let private_key = PrivateKey::<CurrentNetwork>::new(&mut rng).unwrap();
let view_key = ViewKey::try_from(private_key).unwrap();
let address = Address::try_from(private_key).unwrap();
let owner = Owner::Private(Plaintext::from(Literal::Address(address)));
let ciphertext = construct_ciphertext::<CurrentNetwork>(view_key, owner, &mut rng).unwrap();
let decrypt =
Decrypt { network: 0, ciphertext: ciphertext.to_string(), view_key: incorrect_view_key.to_string() };
assert!(decrypt.parse().is_err());
}
}
}