1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the snarkVM library.
// The snarkVM library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The snarkVM library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the snarkVM library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
impl<N: Network> Record<N, Ciphertext<N>> {
/// Decrypts `self` into plaintext using the given view key.
pub fn is_owner(&self, view_key: &ViewKey<N>) -> bool {
// Compute the address.
let address = view_key.to_address();
// Check if the address is the owner.
self.is_owner_with_address_x_coordinate(view_key, &address.to_x_coordinate())
}
/// Decrypts `self` into plaintext using the x-coordinate of the address corresponding to the given view key.
pub fn is_owner_with_address_x_coordinate(&self, view_key: &ViewKey<N>, address_x_coordinate: &Field<N>) -> bool {
// In debug mode, check that the address corresponds to the given view key.
debug_assert_eq!(
&view_key.to_address().to_x_coordinate(),
address_x_coordinate,
"Failed to check record - view key and address do not match"
);
match &self.owner {
// If the owner is public, check if the address is the owner.
Owner::Public(owner) => &owner.to_x_coordinate() == address_x_coordinate,
// If the owner is private, decrypt the owner to check if it matches the address.
Owner::Private(ciphertext) => {
// Compute the record view key.
let record_view_key = (self.nonce * **view_key).to_x_coordinate();
// Compute the 0th randomizer.
let randomizer = N::hash_many_psd8(&[N::encryption_domain(), record_view_key], 1);
// Decrypt the owner.
let owner_x = ciphertext[0] - randomizer[0];
// Compare the x coordinates of computed and supplied addresses.
// We can skip recomputing the address from `owner_x` due to the following reasoning.
// First, the transaction SNARK that generated the ciphertext would have checked that the ciphertext encrypts a valid address.
// Now, since a valid address is an element of the prime-order subgroup of the curve, we know that the encrypted x-coordinate corresponds to a prime-order element.
// Finally, since the SNARK + hybrid encryption
// together are an authenticated encryption scheme, we know that the ciphertext has not been malleated.
// Thus overall we know that if the x-coordinate matches that of `address`, then the underlying `address`es must also match.
// Therefore we can skip recomputing the address from `owner_x` and instead compare the x-coordinates directly.
&owner_x == address_x_coordinate
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Literal;
use snarkvm_console_account::PrivateKey;
use snarkvm_console_network::Testnet3;
use snarkvm_console_types::Field;
type CurrentNetwork = Testnet3;
const ITERATIONS: u64 = 1_000;
fn check_is_owner<N: Network>(
view_key: ViewKey<N>,
owner: Owner<N, Plaintext<N>>,
gates: Balance<N, Plaintext<N>>,
rng: &mut TestRng,
) -> Result<()> {
// Prepare the record.
let randomizer = Scalar::rand(rng);
let record = Record {
owner,
gates,
data: 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(),
),
nonce: N::g_scalar_multiply(&randomizer),
};
// Encrypt the record.
let ciphertext = record.encrypt(randomizer)?;
// Ensure the record belongs to the owner.
assert!(ciphertext.is_owner(&view_key));
// Sample a random view key and address.
let private_key = PrivateKey::<N>::new(rng)?;
let view_key = ViewKey::try_from(&private_key)?;
// Ensure the random address is not the owner.
assert!(!ciphertext.is_owner(&view_key));
Ok(())
}
#[test]
fn test_is_owner() -> Result<()> {
let mut rng = TestRng::default();
for _ in 0..ITERATIONS {
// Sample a view key and address.
let private_key = PrivateKey::<CurrentNetwork>::new(&mut rng)?;
let view_key = ViewKey::try_from(&private_key)?;
let address = Address::try_from(&private_key)?;
// Public owner and public gates.
let owner = Owner::Public(address);
let gates = Balance::Public(U64::new(u64::rand(&mut rng) >> 12));
check_is_owner::<CurrentNetwork>(view_key, owner, gates, &mut rng)?;
// Private owner and public gates.
let owner = Owner::Private(Plaintext::from(Literal::Address(address)));
let gates = Balance::Public(U64::new(u64::rand(&mut rng) >> 12));
check_is_owner::<CurrentNetwork>(view_key, owner, gates, &mut rng)?;
// Public owner and private gates.
let owner = Owner::Public(address);
let gates = Balance::Private(Plaintext::from(Literal::U64(U64::new(u64::rand(&mut rng) >> 12))));
check_is_owner::<CurrentNetwork>(view_key, owner, gates, &mut rng)?;
// Private owner and private gates.
let owner = Owner::Private(Plaintext::from(Literal::Address(address)));
let gates = Balance::Private(Plaintext::from(Literal::U64(U64::new(u64::rand(&mut rng) >> 12))));
check_is_owner::<CurrentNetwork>(view_key, owner, gates, &mut rng)?;
}
Ok(())
}
}