decrypt.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the alphavm library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 use super::*; 17 18 impl<N: Network> Record<N, Ciphertext<N>> { 19 /// Decrypts `self` into plaintext using the given view key and checks that the owner matches the view key. 20 pub fn decrypt(&self, view_key: &ViewKey<N>) -> Result<Record<N, Plaintext<N>>> { 21 // Compute the record view key. 22 let record_view_key = (self.nonce * **view_key).to_x_coordinate(); 23 // Decrypt the record. 24 let record = self.decrypt_symmetric_unchecked(&record_view_key)?; 25 // Ensure the record owner matches the view key. 26 match view_key.to_address() == **record.owner() { 27 true => Ok(record), 28 false => bail!("Illegal operation: Record::decrypt() view key does not correspond to the record owner."), 29 } 30 } 31 32 /// Decrypts `self` into plaintext using the given record view key. 33 /// Note: This method does not check that the record view key corresponds to the record owner. 34 /// Use `Self::decrypt` for the checked variant. 35 pub fn decrypt_symmetric_unchecked(&self, record_view_key: &Field<N>) -> Result<Record<N, Plaintext<N>>> { 36 // Determine the number of randomizers needed to encrypt the record. 37 let num_randomizers = self.num_randomizers()?; 38 // Prepare a randomizer for each field element. 39 let randomizers = N::hash_many_psd8(&[N::encryption_domain(), *record_view_key], num_randomizers); 40 // Decrypt the record. 41 self.decrypt_with_randomizers(&randomizers) 42 } 43 44 /// Decrypts `self` into plaintext using the given randomizers. 45 fn decrypt_with_randomizers(&self, randomizers: &[Field<N>]) -> Result<Record<N, Plaintext<N>>> { 46 // Initialize an index to keep track of the randomizer index. 47 let mut index: usize = 0; 48 49 // Decrypt the owner. 50 let owner = match self.owner.is_public() { 51 true => self.owner.decrypt_with_randomizer(&[])?, 52 false => self.owner.decrypt_with_randomizer(&[randomizers[index]])?, 53 }; 54 55 // Increment the index if the owner is private. 56 if owner.is_private() { 57 index += 1; 58 } 59 60 // Decrypt the program data. 61 let mut decrypted_data = IndexMap::with_capacity(self.data.len()); 62 for (id, entry, num_randomizers) in self.data.iter().map(|(id, entry)| (id, entry, entry.num_randomizers())) { 63 // Retrieve the result for `num_randomizers`. 64 let num_randomizers = num_randomizers? as usize; 65 // Retrieve the randomizers for this entry. 66 let randomizers = &randomizers[index..index + num_randomizers]; 67 // Decrypt the entry. 68 let entry = match entry { 69 // Constant entries do not need to be decrypted. 70 Entry::Constant(plaintext) => Entry::Constant(plaintext.clone()), 71 // Public entries do not need to be decrypted. 72 Entry::Public(plaintext) => Entry::Public(plaintext.clone()), 73 // Private entries are decrypted with the given randomizers. 74 Entry::Private(private) => Entry::Private(Plaintext::from_fields( 75 &private 76 .iter() 77 .zip_eq(randomizers) 78 .map(|(ciphertext, randomizer)| *ciphertext - randomizer) 79 .collect::<Vec<_>>(), 80 )?), 81 }; 82 // Insert the decrypted entry. 83 if decrypted_data.insert(*id, entry).is_some() { 84 bail!("Duplicate identifier in record: {id}"); 85 } 86 // Increment the index. 87 index += num_randomizers; 88 } 89 90 // Return the decrypted record. 91 Self::from_plaintext(owner, decrypted_data, self.nonce, self.version) 92 } 93 } 94 95 #[cfg(test)] 96 mod tests { 97 use super::*; 98 use crate::Literal; 99 use alphavm_console_account::PrivateKey; 100 use alphavm_console_network::MainnetV0; 101 use alphavm_console_types::Field; 102 103 type CurrentNetwork = MainnetV0; 104 105 const ITERATIONS: u64 = 1000; 106 107 fn check_encrypt_and_decrypt<N: Network>( 108 view_key: ViewKey<N>, 109 owner: Owner<N, Plaintext<N>>, 110 rng: &mut TestRng, 111 ) -> Result<()> { 112 // Prepare the record. 113 let randomizer = Scalar::rand(rng); 114 let record = Record { 115 owner, 116 data: IndexMap::from_iter(vec![ 117 (Identifier::from_str("a")?, Entry::Private(Plaintext::from(Literal::Field(Field::rand(rng))))), 118 (Identifier::from_str("b")?, Entry::Private(Plaintext::from(Literal::Scalar(Scalar::rand(rng))))), 119 ]), 120 nonce: N::g_scalar_multiply(&randomizer), 121 version: U8::rand(rng), 122 }; 123 // Encrypt the record. 124 let ciphertext = record.encrypt(randomizer)?; 125 // Decrypt the record. 126 assert_eq!(record, ciphertext.decrypt(&view_key)?); 127 128 // Generate a new random private key. 129 let incorrect_private_key = PrivateKey::<N>::new(rng)?; 130 // Generate a new view key. 131 let incorrect_view_key = ViewKey::try_from(&incorrect_private_key)?; 132 // Ensure that decrypting with the incorrect view key fails. 133 assert!(ciphertext.decrypt(&incorrect_view_key).is_err()); 134 135 Ok(()) 136 } 137 138 #[test] 139 fn test_encrypt_and_decrypt() -> Result<()> { 140 let mut rng = TestRng::default(); 141 142 for _ in 0..ITERATIONS { 143 // Sample a view key and address. 144 let private_key = PrivateKey::<CurrentNetwork>::new(&mut rng)?; 145 let view_key = ViewKey::try_from(&private_key)?; 146 let address = Address::try_from(&private_key)?; 147 148 // Public owner. 149 let owner = Owner::Public(address); 150 check_encrypt_and_decrypt::<CurrentNetwork>(view_key, owner, &mut rng)?; 151 152 // Private owner. 153 let owner = Owner::Private(Plaintext::from(Literal::Address(address))); 154 check_encrypt_and_decrypt::<CurrentNetwork>(view_key, owner, &mut rng)?; 155 } 156 Ok(()) 157 } 158 }