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