diff --git a/noir-projects/aztec-nr/aztec/src/keys.nr b/noir-projects/aztec-nr/aztec/src/keys.nr index 0a79c4a024a5..999bd984de00 100644 --- a/noir-projects/aztec-nr/aztec/src/keys.nr +++ b/noir-projects/aztec-nr/aztec/src/keys.nr @@ -1,4 +1,4 @@ mod getters; mod point_to_symmetric_key; -use crate::keys::getters::get_fresh_nullifier_public_key_hash; +use crate::keys::getters::{get_npk_m, get_ivpk_m, get_ovpk_m, get_tpk_m}; diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters.nr b/noir-projects/aztec-nr/aztec/src/keys/getters.nr index bbc0eb2dda85..171f2d61579f 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters.nr @@ -1,79 +1,90 @@ -use dep::protocol_types::{ - address::{ - AztecAddress, - PartialAddress - }, - constants::{ - GENERATOR_INDEX__PUBLIC_KEYS_HASH, - GENERATOR_INDEX__CONTRACT_ADDRESS_V1, - CANONICAL_KEY_REGISTRY_ADDRESS - }, - grumpkin_point::GrumpkinPoint, +use dep::protocol_types::{address::AztecAddress, constants::CANONICAL_KEY_REGISTRY_ADDRESS, grumpkin_point::GrumpkinPoint}; +use crate::{ + context::PrivateContext, hash::poseidon2_hash, oracle::keys::get_public_keys_and_partial_address, + state_vars::{ + map::derive_storage_slot_in_map, + shared_mutable::shared_mutable_private_getter::SharedMutablePrivateGetter +} }; -use crate::context::PrivateContext; -use crate::hash::{ - pedersen_hash, - poseidon2_hash, -}; -use crate::oracle::keys::get_public_keys_and_partial_address; -use crate::state_vars::{ - map::derive_storage_slot_in_map, - shared_mutable::shared_mutable_private_getter::SharedMutablePrivateGetter, -}; +// Note: In fetch_hash_from_registry we expect that shared mutable slow is index + 1 (e.g. NULLIFIER_INDEX + 1), +// this changes the function will need to be refactored +global NULLIFIER_INDEX = 0; +global INCOMING_INDEX = 1; +global OUTGOING_INDEX = 2; +global TAGGING_INDEX = 3; -struct PublicKeyTypeEnum { - NULLIFIER: u8, +global DELAY = 5; + +pub fn get_npk_m(context: &mut PrivateContext, address: AztecAddress) -> GrumpkinPoint { + get_master_key(context, address, NULLIFIER_INDEX) } -global PublicKeyType = PublicKeyTypeEnum { - NULLIFIER: 0, -}; +pub fn get_ivpk_m(context: &mut PrivateContext, address: AztecAddress) -> GrumpkinPoint { + get_master_key(context, address, INCOMING_INDEX) +} -pub fn get_fresh_nullifier_public_key_hash( - context: &mut PrivateContext, - address: AztecAddress, -) -> Field { - // This is the storage slot of the nullifier_public_key inside the key registry contract - // TODO: (#6133) We should have this be directly imported from the other contract if possible, or at least this should not be this brittle - let storage_slot_of_nullifier_public_key = 1; +pub fn get_ovpk_m(context: &mut PrivateContext, address: AztecAddress) -> GrumpkinPoint { + get_master_key(context, address, OUTGOING_INDEX) +} - let derived_slot = derive_storage_slot_in_map(storage_slot_of_nullifier_public_key, address); +pub fn get_tpk_m(context: &mut PrivateContext, address: AztecAddress) -> GrumpkinPoint { + get_master_key(context, address, TAGGING_INDEX) +} - // We read from the canonical Key Registry - // TODO: (#6134) It's a bit wonky because we need to know the delay for get_current_value_in_private to work correctly. - // We should allow for this usecase without needing to hard code it here. - let registry_private_getter: SharedMutablePrivateGetter = SharedMutablePrivateGetter::new(*context, AztecAddress::from_field(CANONICAL_KEY_REGISTRY_ADDRESS), derived_slot); - let nullifier_public_key_hash_in_registry = registry_private_getter.get_current_value_in_private(); +fn get_master_key( + context: &mut PrivateContext, + address: AztecAddress, + key_index: Field +) -> GrumpkinPoint { + // 1. Get key hash from registry + let key_hash = fetch_hash_from_registry(context, key_index, address); + if key_hash == 0 { + // i. Keys were not registered yet --> fetch address preimage and check that the hash matches + fetch_and_constrain_keys(address)[key_index] + } else { + // ii. Keys were registered --> we fetch the key from oracle, hash it and check that it matches the hash in the registry + let (keys, _) = get_public_keys_and_partial_address(address); + let key = keys[key_index]; + let key_hash_from_oracle = poseidon2_hash(key.serialize()); + assert(key_hash_from_oracle == key_hash); + key + } +} - let nullifier_public_key_hash = if nullifier_public_key_hash_in_registry == 0 { - let keys = get_original_public_keys_internal(address); - poseidon2_hash(keys[PublicKeyType.NULLIFIER].serialize()) - } else { - nullifier_public_key_hash_in_registry - }; +fn fetch_hash_from_registry( + context: &mut PrivateContext, + key_index: Field, + address: AztecAddress +) -> Field { + let derived_slot = derive_storage_slot_in_map(key_index + 1, address); - nullifier_public_key_hash + let registry_private_getter: SharedMutablePrivateGetter = SharedMutablePrivateGetter::new( + *context, + AztecAddress::from_field(CANONICAL_KEY_REGISTRY_ADDRESS), + derived_slot + ); + registry_private_getter.get_current_value_in_private() } -// This constraint only works on keys that have not been rotated, otherwise this call will fail as the public keys are not constrained -fn get_original_public_keys_internal(address: AztecAddress) -> [GrumpkinPoint; 4] { - let (public_keys, partial_address) = get_public_keys_and_partial_address(address); +// Passes only when keys were not rotated - is expected to be called only when keys were not registered yet +fn fetch_and_constrain_keys(address: AztecAddress) -> [GrumpkinPoint; 4] { + let (public_keys, partial_address) = get_public_keys_and_partial_address(address); - let nullifier_pub_key = public_keys[0]; - let incoming_pub_key = public_keys[1]; - let outgoing_pub_key = public_keys[2]; - let tagging_pub_key = public_keys[3]; + let nullifier_pub_key = public_keys[0]; + let incoming_pub_key = public_keys[1]; + let outgoing_pub_key = public_keys[2]; + let tagging_pub_key = public_keys[3]; - let computed_address = AztecAddress::compute_from_public_keys_and_partial_address( - nullifier_pub_key, - incoming_pub_key, - outgoing_pub_key, - tagging_pub_key, - partial_address, - ); + let computed_address = AztecAddress::compute_from_public_keys_and_partial_address( + nullifier_pub_key, + incoming_pub_key, + outgoing_pub_key, + tagging_pub_key, + partial_address + ); - assert(computed_address.eq(address)); + assert(computed_address.eq(address)); - [nullifier_pub_key, incoming_pub_key, outgoing_pub_key, tagging_pub_key] + [nullifier_pub_key, incoming_pub_key, outgoing_pub_key, tagging_pub_key] } diff --git a/noir-projects/noir-contracts/contracts/key_registry_contract/src/main.nr b/noir-projects/noir-contracts/contracts/key_registry_contract/src/main.nr index 2d2111d07ddc..1b60d2d2bd79 100644 --- a/noir-projects/noir-contracts/contracts/key_registry_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/key_registry_contract/src/main.nr @@ -1,104 +1,81 @@ contract KeyRegistry { - use dep::authwit::auth::assert_current_call_valid_authwit_public; + use dep::authwit::auth::assert_current_call_valid_authwit_public; - use dep::aztec::{ - state_vars::{ - SharedMutable, - Map - }, - protocol_types::{ - grumpkin_point::GrumpkinPoint, - address::{ - AztecAddress, - PublicKeysHash, - PartialAddress, - }, - constants::{ - GENERATOR_INDEX__CONTRACT_ADDRESS_V1, - GENERATOR_INDEX__PUBLIC_KEYS_HASH - }, - hash::poseidon2_hash, - }, - }; + use dep::aztec::{ + state_vars::{SharedMutable, Map}, + protocol_types::{ + grumpkin_point::GrumpkinPoint, address::{AztecAddress, PartialAddress}, + hash::poseidon2_hash + } + }; - global KEY_ROTATION_DELAY = 5; + global KEY_ROTATION_DELAY = 5; - #[aztec(storage)] + #[aztec(storage)] struct Storage { - //! This should stay at storage slot 1. If you change this, make sure you change the hardcoded value in keys/assert_public_key_freshness. - //! We use this hardcoded storage slot with derive_storage_slot_in_map and the SharedMutablePrivateGetter to directly read the value at an address in this contract. - nullifier_public_key_hash_registry: Map>, - - // We are not supporting rotating / changing keys other than the nullifier public in the registry at the moment, but will in the future. - // Uncomment lines below to enable that functionality - // incoming_public_key_registry: Map>, - // outgoing_public_key_registry: Map>, - // tagging_public_key_registry: Map>, + // The following stores a hash of individual master public keys + // If you change slots of vars bellow, you must update the slot in `SharedMutablePrivateGetter` in aztec-nr keys. + nullifier_registry: Map>, + incoming_registry: Map>, + outgoing_registry: Map>, + tagging_registry: Map>, } - #[aztec(public)] + #[aztec(public)] fn rotate_nullifier_public_key( - address: AztecAddress, - new_nullifier_public_key: GrumpkinPoint, - nonce: Field, - ) { - assert( - !new_nullifier_public_key.is_zero(), - "New nullifier public key must be non-zero" - ); - - // TODO: (#6137) - if (!address.eq(context.msg_sender())) { - assert_current_call_valid_authwit_public(&mut context, address); - } else { - assert(nonce == 0, "invalid nonce"); - } + address: AztecAddress, + new_nullifier_public_key: GrumpkinPoint, + nonce: Field + ) { + assert(!new_nullifier_public_key.is_zero(), "New nullifier public key must be non-zero"); - let nullifier_key_registry = storage.nullifier_public_key_hash_registry.at(address); + // TODO: (#6137) + if (!address.eq(context.msg_sender())) { + assert_current_call_valid_authwit_public(&mut context, address); + } else { + assert(nonce == 0, "invalid nonce"); + } - nullifier_key_registry.schedule_value_change(poseidon2_hash(new_nullifier_public_key.serialize())); - } + let nullifier_registry = storage.nullifier_registry.at(address); + nullifier_registry.schedule_value_change(poseidon2_hash(new_nullifier_public_key.serialize())); + } - #[aztec(public)] + #[aztec(public)] fn register( - address: AztecAddress, - partial_address: PartialAddress, - nullifier_public_key: GrumpkinPoint, - incoming_public_key: GrumpkinPoint, - outgoing_public_key: GrumpkinPoint, - tagging_public_key: GrumpkinPoint, - ) { - assert( - !partial_address.is_zero() & - !nullifier_public_key.is_zero() & - !incoming_public_key.is_zero() & - !outgoing_public_key.is_zero() & - !tagging_public_key.is_zero(), - "All public keys must be non-zero" - ); + address: AztecAddress, + partial_address: PartialAddress, + nullifier_public_key: GrumpkinPoint, + incoming_public_key: GrumpkinPoint, + outgoing_public_key: GrumpkinPoint, + tagging_public_key: GrumpkinPoint + ) { + assert( + !partial_address.is_zero() + & !nullifier_public_key.is_zero() + & !incoming_public_key.is_zero() + & !outgoing_public_key.is_zero() + & !tagging_public_key.is_zero(), "All public keys must be non-zero" + ); - // We could also pass in original_public_keys_hash instead of computing it here, if all we need the original one is for being able to prove ownership of address - let computed_address = AztecAddress::compute_from_public_keys_and_partial_address( - nullifier_public_key, - incoming_public_key, - outgoing_public_key, - tagging_public_key, - partial_address, - ); + // We could also pass in original_public_keys_hash instead of computing it here, if all we need the original one is for being able to prove ownership of address + let computed_address = AztecAddress::compute_from_public_keys_and_partial_address( + nullifier_public_key, + incoming_public_key, + outgoing_public_key, + tagging_public_key, + partial_address + ); - assert(computed_address.eq(address), "Computed address does not match supplied address"); + assert(computed_address.eq(address), "Computed address does not match supplied address"); - let nullifier_key_hash_registry = storage.nullifier_public_key_hash_registry.at(address); - // We are not supporting rotating / changing keys other than the nullifier public in the registry at the moment, but will in the future. - // Uncomment lines below to enable that functionality - // let incoming_key_registry = storage.incoming_public_key_registry.at(address); - // let outgoing_key_registry = storage.outgoing_public_key_registry.at(address); - // let tagging_key_registry = storage.taggin_public_key_registry.at(address); + let nullifier_registry = storage.nullifier_registry.at(address); + let incoming_registry = storage.incoming_registry.at(address); + let outgoing_registry = storage.outgoing_registry.at(address); + let tagging_registry = storage.tagging_registry.at(address); - nullifier_key_hash_registry.schedule_value_change(poseidon2_hash(nullifier_public_key.serialize())); - // We are not supporting rotating / changing keys other than the nullifier public in the registry at the moment, but will in the future. - // Uncomment lines below to enable that functionality // incoming_key_registry.schedule_value_change(new_incoming_public_key); - // outgoing_key_registry.schedule_value_change(new_outgoing_public_key); - // tagging_key_registry.schedule_value_change(new_tagging_public_key); - } + nullifier_registry.schedule_value_change(poseidon2_hash(nullifier_public_key.serialize())); + incoming_registry.schedule_value_change(poseidon2_hash(incoming_public_key.serialize())); + outgoing_registry.schedule_value_change(poseidon2_hash(outgoing_public_key.serialize())); + tagging_registry.schedule_value_change(poseidon2_hash(tagging_public_key.serialize())); + } } diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index b73da67a097f..ac43a92203ad 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -19,7 +19,7 @@ contract Test { use dep::aztec::state_vars::{shared_mutable::SharedMutablePrivateGetter, map::derive_storage_slot_in_map}; use dep::aztec::{ - keys::getters::get_fresh_nullifier_public_key_hash, + keys::getters::get_npk_m, context::{Context, inputs::private_context_inputs::PrivateContextInputs}, hash::{pedersen_hash, poseidon2_hash, compute_secret_hash, ArgsHasher}, note::{ @@ -435,7 +435,7 @@ contract Test { address: AztecAddress, public_nullifying_key: GrumpkinPoint, ) { - assert_eq(get_fresh_nullifier_public_key_hash(&mut context, address), poseidon2_hash(public_nullifying_key.serialize())); + assert_eq(get_npk_m(&mut context, address), poseidon2_hash(public_nullifying_key.serialize())); } #[aztec(public)]