snarkos_cli/commands/developer/
decrypt.rs1use snarkvm::{
17 console::{
18 network::{CanaryV0, MainnetV0, Network, TestnetV0},
19 program::Ciphertext,
20 },
21 prelude::{Record, ViewKey},
22};
23
24use anyhow::{Result, bail};
25use clap::Parser;
26use std::str::FromStr;
27use zeroize::Zeroize;
28
29#[derive(Debug, Parser, Zeroize)]
31pub struct Decrypt {
32 #[clap(default_value = "0", long = "network")]
34 pub network: u16,
35 #[clap(short, long)]
37 pub ciphertext: String,
38 #[clap(short, long)]
40 pub view_key: String,
41}
42
43impl Decrypt {
44 pub fn parse(self) -> Result<String> {
45 match self.network {
47 MainnetV0::ID => Self::decrypt_ciphertext::<MainnetV0>(&self.ciphertext, &self.view_key),
48 TestnetV0::ID => Self::decrypt_ciphertext::<TestnetV0>(&self.ciphertext, &self.view_key),
49 CanaryV0::ID => Self::decrypt_ciphertext::<CanaryV0>(&self.ciphertext, &self.view_key),
50 unknown_id => bail!("Unknown network ID ({unknown_id})"),
51 }
52 }
53
54 fn decrypt_ciphertext<N: Network>(ciphertext: &str, view_key: &str) -> Result<String> {
56 let ciphertext_record = Record::<N, Ciphertext<N>>::from_str(ciphertext)?;
58
59 let view_key = ViewKey::<N>::from_str(view_key)?;
61
62 match ciphertext_record.decrypt(&view_key) {
63 Ok(plaintext_record) => Ok(plaintext_record.to_string()),
64 Err(_) => bail!("Invalid view key for the provided record ciphertext"),
65 }
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72 use snarkvm::prelude::{
73 Address,
74 Entry,
75 Field,
76 Identifier,
77 Literal,
78 Network,
79 One,
80 Owner,
81 Plaintext,
82 PrivateKey,
83 Scalar,
84 TestRng,
85 U8,
86 Uniform,
87 ViewKey,
88 Zero,
89 };
90
91 use indexmap::IndexMap;
92 use rand::Rng;
93
94 type CurrentNetwork = MainnetV0;
95
96 const ITERATIONS: usize = 1000;
97
98 fn construct_ciphertext<N: Network>(
99 view_key: ViewKey<N>,
100 owner: Owner<N, Plaintext<N>>,
101 rng: &mut TestRng,
102 ) -> Result<Record<N, Ciphertext<N>>> {
103 let randomizer = Scalar::rand(rng);
105 let version = match rng.r#gen() {
106 true => U8::<N>::one(),
107 false => U8::<N>::zero(),
108 };
109 let record = Record::<N, Plaintext<N>>::from_plaintext(
110 owner,
111 IndexMap::from_iter(
112 vec![
113 (Identifier::from_str("a")?, Entry::Private(Plaintext::from(Literal::Field(Field::rand(rng))))),
114 (Identifier::from_str("b")?, Entry::Private(Plaintext::from(Literal::Scalar(Scalar::rand(rng))))),
115 ]
116 .into_iter(),
117 ),
118 N::g_scalar_multiply(&randomizer),
119 version,
120 )?;
121 let ciphertext = record.encrypt(randomizer)?;
123 assert_eq!(record, ciphertext.decrypt(&view_key)?);
125
126 Ok(ciphertext)
127 }
128
129 #[test]
130 fn test_decryption() {
131 let mut rng = TestRng::default();
132
133 for _ in 0..ITERATIONS {
134 let private_key = PrivateKey::<CurrentNetwork>::new(&mut rng).unwrap();
135 let view_key = ViewKey::try_from(private_key).unwrap();
136 let address = Address::try_from(private_key).unwrap();
137
138 let owner = Owner::Private(Plaintext::from(Literal::Address(address)));
140 let ciphertext = construct_ciphertext(view_key, owner, &mut rng).unwrap();
141
142 let expected_plaintext = ciphertext.decrypt(&view_key).unwrap();
144
145 let decrypt = Decrypt { network: 0, ciphertext: ciphertext.to_string(), view_key: view_key.to_string() };
146 let plaintext = decrypt.parse().unwrap();
147
148 assert_eq!(plaintext, expected_plaintext.to_string());
150 }
151 }
152
153 #[test]
154 fn test_failed_decryption() {
155 let mut rng = TestRng::default();
156
157 let incorrect_private_key = PrivateKey::<CurrentNetwork>::new(&mut rng).unwrap();
159 let incorrect_view_key = ViewKey::try_from(incorrect_private_key).unwrap();
160
161 for _ in 0..ITERATIONS {
162 let private_key = PrivateKey::<CurrentNetwork>::new(&mut rng).unwrap();
163 let view_key = ViewKey::try_from(private_key).unwrap();
164 let address = Address::try_from(private_key).unwrap();
165
166 let owner = Owner::Private(Plaintext::from(Literal::Address(address)));
168 let ciphertext = construct_ciphertext::<CurrentNetwork>(view_key, owner, &mut rng).unwrap();
169
170 let decrypt =
172 Decrypt { network: 0, ciphertext: ciphertext.to_string(), view_key: incorrect_view_key.to_string() };
173 assert!(decrypt.parse().is_err());
174 }
175 }
176}