decrypt.rs
1 // Copyright (c) 2025-2026 ACDC Network 2 // This file is part of the alphavm library. 3 // 4 // Alpha Chain | Delta Chain Protocol 5 // International Monetary Graphite. 6 // 7 // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com). 8 // They built world-class ZK infrastructure. We installed the EASY button. 9 // Their cryptography: elegant. Our modifications: bureaucracy-compatible. 10 // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours. 11 // 12 // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0 13 // All modifications and new work: CC0 1.0 Universal Public Domain Dedication. 14 // No rights reserved. No permission required. No warranty. No refunds. 15 // 16 // https://creativecommons.org/publicdomain/zero/1.0/ 17 // SPDX-License-Identifier: CC0-1.0 18 19 use super::*; 20 21 impl<N: Network> Record<N, Ciphertext<N>> { 22 /// Decrypts `self` into plaintext using the given view key and checks that the owner matches the view key. 23 pub fn decrypt(&self, view_key: &ViewKey<N>) -> Result<Record<N, Plaintext<N>>> { 24 // Compute the record view key. 25 let record_view_key = (self.nonce * **view_key).to_x_coordinate(); 26 // Decrypt the record. 27 let record = self.decrypt_symmetric_unchecked(&record_view_key)?; 28 // Ensure the record owner matches the view key. 29 match view_key.to_address() == **record.owner() { 30 true => Ok(record), 31 false => bail!("Illegal operation: Record::decrypt() view key does not correspond to the record owner."), 32 } 33 } 34 35 /// Decrypts `self` into plaintext using the given record view key. 36 /// Note: This method does not check that the record view key corresponds to the record owner. 37 /// Use `Self::decrypt` for the checked variant. 38 pub fn decrypt_symmetric_unchecked(&self, record_view_key: &Field<N>) -> Result<Record<N, Plaintext<N>>> { 39 // Determine the number of randomizers needed to encrypt the record. 40 let num_randomizers = self.num_randomizers()?; 41 // Prepare a randomizer for each field element. 42 let randomizers = N::hash_many_psd8(&[N::encryption_domain(), *record_view_key], num_randomizers); 43 // Decrypt the record. 44 self.decrypt_with_randomizers(&randomizers) 45 } 46 47 /// Decrypts `self` into plaintext using the given randomizers. 48 fn decrypt_with_randomizers(&self, randomizers: &[Field<N>]) -> Result<Record<N, Plaintext<N>>> { 49 // Initialize an index to keep track of the randomizer index. 50 let mut index: usize = 0; 51 52 // Decrypt the owner. 53 let owner = match self.owner.is_public() { 54 true => self.owner.decrypt_with_randomizer(&[])?, 55 false => self.owner.decrypt_with_randomizer(&[randomizers[index]])?, 56 }; 57 58 // Increment the index if the owner is private. 59 if owner.is_private() { 60 index += 1; 61 } 62 63 // Decrypt the program data. 64 let mut decrypted_data = IndexMap::with_capacity(self.data.len()); 65 for (id, entry, num_randomizers) in self.data.iter().map(|(id, entry)| (id, entry, entry.num_randomizers())) { 66 // Retrieve the result for `num_randomizers`. 67 let num_randomizers = num_randomizers? as usize; 68 // Retrieve the randomizers for this entry. 69 let randomizers = &randomizers[index..index + num_randomizers]; 70 // Decrypt the entry. 71 let entry = match entry { 72 // Constant entries do not need to be decrypted. 73 Entry::Constant(plaintext) => Entry::Constant(plaintext.clone()), 74 // Public entries do not need to be decrypted. 75 Entry::Public(plaintext) => Entry::Public(plaintext.clone()), 76 // Private entries are decrypted with the given randomizers. 77 Entry::Private(private) => Entry::Private(Plaintext::from_fields( 78 &private 79 .iter() 80 .zip_eq(randomizers) 81 .map(|(ciphertext, randomizer)| *ciphertext - randomizer) 82 .collect::<Vec<_>>(), 83 )?), 84 }; 85 // Insert the decrypted entry. 86 if decrypted_data.insert(*id, entry).is_some() { 87 bail!("Duplicate identifier in record: {id}"); 88 } 89 // Increment the index. 90 index += num_randomizers; 91 } 92 93 // Return the decrypted record. 94 Self::from_plaintext(owner, decrypted_data, self.nonce, self.version) 95 } 96 } 97 98 #[cfg(test)] 99 mod tests { 100 use super::*; 101 use crate::Literal; 102 use alphavm_console_account::PrivateKey; 103 use alphavm_console_network::MainnetV0; 104 use alphavm_console_types::Field; 105 106 type CurrentNetwork = MainnetV0; 107 108 const ITERATIONS: u64 = 1000; 109 110 fn check_encrypt_and_decrypt<N: Network>( 111 view_key: ViewKey<N>, 112 owner: Owner<N, Plaintext<N>>, 113 rng: &mut TestRng, 114 ) -> Result<()> { 115 // Prepare the record. 116 let randomizer = Scalar::rand(rng); 117 let record = Record { 118 owner, 119 data: IndexMap::from_iter(vec![ 120 (Identifier::from_str("a")?, Entry::Private(Plaintext::from(Literal::Field(Field::rand(rng))))), 121 (Identifier::from_str("b")?, Entry::Private(Plaintext::from(Literal::Scalar(Scalar::rand(rng))))), 122 ]), 123 nonce: N::g_scalar_multiply(&randomizer), 124 version: U8::rand(rng), 125 }; 126 // Encrypt the record. 127 let ciphertext = record.encrypt(randomizer)?; 128 // Decrypt the record. 129 assert_eq!(record, ciphertext.decrypt(&view_key)?); 130 131 // Generate a new random private key. 132 let incorrect_private_key = PrivateKey::<N>::new(rng)?; 133 // Generate a new view key. 134 let incorrect_view_key = ViewKey::try_from(&incorrect_private_key)?; 135 // Ensure that decrypting with the incorrect view key fails. 136 assert!(ciphertext.decrypt(&incorrect_view_key).is_err()); 137 138 Ok(()) 139 } 140 141 #[test] 142 fn test_encrypt_and_decrypt() -> Result<()> { 143 let mut rng = TestRng::default(); 144 145 for _ in 0..ITERATIONS { 146 // Sample a view key and address. 147 let private_key = PrivateKey::<CurrentNetwork>::new(&mut rng)?; 148 let view_key = ViewKey::try_from(&private_key)?; 149 let address = Address::try_from(&private_key)?; 150 151 // Public owner. 152 let owner = Owner::Public(address); 153 check_encrypt_and_decrypt::<CurrentNetwork>(view_key, owner, &mut rng)?; 154 155 // Private owner. 156 let owner = Owner::Private(Plaintext::from(Literal::Address(address))); 157 check_encrypt_and_decrypt::<CurrentNetwork>(view_key, owner, &mut rng)?; 158 } 159 Ok(()) 160 } 161 }