diff --git a/noir-projects/aztec-nr/address-note/src/address_note.nr b/noir-projects/aztec-nr/address-note/src/address_note.nr index 32b13d774025..7478b59994c4 100644 --- a/noir-projects/aztec-nr/address-note/src/address_note.nr +++ b/noir-projects/aztec-nr/address-note/src/address_note.nr @@ -49,7 +49,6 @@ impl NoteInterface for AddressNote { context.encrypt_and_emit_note( (*context).this_address(), slot, - Self::get_note_type_id(), ovpk_m, ivpk_m, self, diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index b6c2c12271eb..25570b7c9de8 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -1,3 +1,5 @@ +use crate::encrypted_logs::{payload::compute_encrypted_note_log}; + use crate::{ context::{inputs::PrivateContextInputs, packed_returns::PackedReturns}, messaging::process_l1_to_l2_message, @@ -272,7 +274,10 @@ impl PrivateContext { // --> might be a better approach to force devs to make a public function call that emits the log if needed then // it would be less easy to accidentally leak information. // If we decide to keep this function around would make sense to wait for traits and then merge it with emit_unencrypted_log. - pub fn emit_unencrypted_log(&mut self, log: T) where T: ToBytesForUnencryptedLog { + pub fn emit_unencrypted_log( + &mut self, + log: T + ) where T: ToBytesForUnencryptedLog { let event_selector = 5; // TODO: compute actual event selector. let contract_address = self.this_address(); let counter = self.next_counter(); @@ -317,7 +322,7 @@ impl PrivateContext { ) where [Field; N]: LensForEncryptedLog { let ovsk_app = self.request_ovsk_app(ovpk_m.hash()); - // We are currently just encrypting it EXACTLY the same way as if it was a note. + // We are currently just encrypting it unconstrained, but otherwise the same way as if it was a note. let counter = self.next_counter(); let encrypted_log: [u8; M] = compute_encrypted_log( contract_address, @@ -339,7 +344,6 @@ impl PrivateContext { &mut self, contract_address: AztecAddress, storage_slot: Field, - note_type_id: Field, ovpk_m: GrumpkinPoint, ivpk_m: GrumpkinPoint, note: Note @@ -352,23 +356,11 @@ impl PrivateContext { assert( note_exists_index != MAX_NEW_NOTE_HASHES_PER_CALL, "Can only emit a note log for an existing note." ); - let preimage = note.serialize_content(); + let counter = self.next_counter(); let ovsk_app = self.request_ovsk_app(ovpk_m.hash()); - // TODO(#1139 | #6408): perform encryption in the circuit - let encrypted_log: [u8; M] = compute_encrypted_log( - contract_address, - storage_slot, - note_type_id, - ovsk_app, - ovpk_m, - ivpk_m, - preimage - ); - emit_encrypted_note_log(note_hash_counter, encrypted_log, counter); - // Current unoptimized size of the encrypted log // incoming_tag (32 bytes) // outgoing_tag (32 bytes) @@ -378,8 +370,11 @@ impl PrivateContext { // outgoing_body (176 bytes) // incoming_body_fixed (64 bytes) // incoming_body_variable (N * 32 bytes + 16 bytes padding) + let encrypted_log: [u8; M] = compute_encrypted_note_log(contract_address, storage_slot, ovsk_app, ovpk_m, ivpk_m, note); + emit_encrypted_note_log(note_hash_counter, encrypted_log, counter); + // len of processed log (4 bytes) - let len = 32 + 32 + 64 + 48 + 48 + 176 + 64 + (preimage.len() as Field * 32) + 16 + 4; + let len = encrypted_log.len() as Field + 4; let log_hash = sha256_to_field(encrypted_log); let side_effect = NoteLogHash { value: log_hash, counter, length: len, note_hash_counter }; diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs.nr index 61cb92154d4a..d1df9c64e419 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs.nr @@ -1,3 +1,4 @@ mod header; mod incoming_body; mod outgoing_body; +mod payload; \ No newline at end of file diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr index a96a1666e5ef..98c2d42d5b9c 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr @@ -13,7 +13,6 @@ impl EncryptedLogHeader { EncryptedLogHeader { address } } - // @todo Issue(#5901) Figure out if we return the bytes or fields for the log fn compute_ciphertext(self, secret: GrumpkinPrivateKey, point: GrumpkinPoint) -> [u8; 48] { let full_key = point_to_symmetric_key(secret, point); let mut sym_key = [0; 16]; diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr new file mode 100644 index 000000000000..7606d6f010d1 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr @@ -0,0 +1,101 @@ +use dep::protocol_types::{ + address::AztecAddress, grumpkin_private_key::GrumpkinPrivateKey, grumpkin_point::GrumpkinPoint, + constants::{GENERATOR_INDEX__IVSK_M, GENERATOR_INDEX__OVSK_M}, hash::poseidon2_hash +}; + +use dep::std::{embedded_curve_ops::{embedded_curve_add, EmbeddedCurvePoint}, field::bytes32_to_field}; + +use crate::oracle::unsafe_rand::unsafe_rand; + +use crate::note::note_interface::NoteInterface; + +use crate::encrypted_logs::{ + header::EncryptedLogHeader, incoming_body::EncryptedLogIncomingBody, + outgoing_body::EncryptedLogOutgoingBody +}; + +pub fn compute_encrypted_note_log( + contract_address: AztecAddress, + storage_slot: Field, + ovsk_app: Field, + ovpk: GrumpkinPoint, + ivpk: GrumpkinPoint, + note: Note +) -> [u8; M] where Note: NoteInterface { + // @todo Need to draw randomness from the full domain of Fq not only Fr + let eph_sk: GrumpkinPrivateKey = fr_to_private_key(unsafe_rand()); + let eph_pk = eph_sk.derive_public_key(); + + // @todo This value needs to be populated! + let recipient = AztecAddress::from_field(0); + + let ivpk_app = compute_ivpk_app(ivpk, contract_address); + + let header = EncryptedLogHeader::new(contract_address); + + let incoming_header_ciphertext: [u8; 48] = header.compute_ciphertext(eph_sk, ivpk); + let outgoing_Header_ciphertext: [u8; 48] = header.compute_ciphertext(eph_sk, ovpk); + let incoming_body_ciphertext = EncryptedLogIncomingBody::from_note(note, storage_slot).compute_ciphertext(eph_sk, ivpk_app); + let outgoing_body_ciphertext: [u8; 176] = EncryptedLogOutgoingBody::new(eph_sk, recipient, ivpk_app).compute_ciphertext(fr_to_private_key(ovsk_app), eph_pk); + + let mut encrypted_bytes: [u8; M] = [0; M]; + // @todo We ignore the tags for now + + let eph_pk_bytes = eph_pk.to_be_bytes(); + for i in 0..64 { + encrypted_bytes[64 + i] = eph_pk_bytes[i]; + } + for i in 0..48 { + encrypted_bytes[128 + i] = incoming_header_ciphertext[i]; + encrypted_bytes[176 + i] = outgoing_Header_ciphertext[i]; + } + for i in 0..176 { + encrypted_bytes[224 + i] = outgoing_body_ciphertext[i]; + } + // Then we fill in the rest as the incoming body ciphertext + let size = M - 400; + assert_eq(size, incoming_body_ciphertext.len(), "ciphertext length mismatch"); + for i in 0..size { + encrypted_bytes[400 + i] = incoming_body_ciphertext[i]; + } + + encrypted_bytes +} + +fn fr_to_private_key(r: Field) -> GrumpkinPrivateKey { + let r_bytes = r.to_be_bytes(32); + + let mut high_bytes = [0; 32]; + let mut low_bytes = [0; 32]; + + for i in 0..16 { + high_bytes[16 + i] = r_bytes[i]; + low_bytes[16 + i] = r_bytes[i + 16]; + } + + let low = bytes32_to_field(low_bytes); + let high = bytes32_to_field(high_bytes); + + GrumpkinPrivateKey::new(high, low) +} + +fn compute_ivpk_app(ivpk: GrumpkinPoint, contract_address: AztecAddress) -> GrumpkinPoint { + // It is useless to compute this, it brings no value to derive fully. + // Issue(#6955) + ivpk + + /* + // @todo Just setting infinite to false, but it should be checked. + // for example user could define ivpk = infinity using the registry + assert((ivpk.x != 0) & (ivpk.y != 0), "ivpk is infinite"); + + let i = fr_to_private_key(poseidon2_hash([contract_address.to_field(), ivpk.x, ivpk.y, GENERATOR_INDEX__IVSK_M])); + let I = i.derive_public_key(); + + let embed_I = EmbeddedCurvePoint { x: I.x, y: I.y, is_infinite: false }; + let embed_ivpk = EmbeddedCurvePoint { x: ivpk.x, y: ivpk.y, is_infinite: false }; + + let embed_result = embedded_curve_add(embed_I, embed_ivpk); + + GrumpkinPoint::new(embed_result.x, embed_result.y)*/ +} diff --git a/noir-projects/aztec-nr/value-note/src/value_note.nr b/noir-projects/aztec-nr/value-note/src/value_note.nr index 73fe20b3c7a7..042144d76ae1 100644 --- a/noir-projects/aztec-nr/value-note/src/value_note.nr +++ b/noir-projects/aztec-nr/value-note/src/value_note.nr @@ -51,7 +51,6 @@ impl NoteInterface for ValueNote { context.encrypt_and_emit_note( (*context).this_address(), slot, - Self::get_note_type_id(), ovpk_m, ivpk_m, self, diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr index c3fc0feb7b1e..aebe782e4c67 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr @@ -44,7 +44,6 @@ impl NoteInterface for Subsc context.encrypt_and_emit_note( (*context).this_address(), slot, - Self::get_note_type_id(), ovpk_m, ivpk_m, self, diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr index 16392f5b18d7..ecef44ab3552 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr @@ -55,7 +55,6 @@ impl NoteInterface for CardNote { context.encrypt_and_emit_note( (*context).this_address(), slot, - Self::get_note_type_id(), ovpk_m, ivpk_m, self, diff --git a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr index 0a73ba517596..93583b8b67d6 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr @@ -90,7 +90,6 @@ impl NoteInterface f context.encrypt_and_emit_note( (*context).this_address(), slot, - Self::get_note_type_id(), ovpk_m, ivpk_m, self, diff --git a/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr b/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr index 2168a93fd93d..7d672df14608 100644 --- a/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr @@ -138,7 +138,6 @@ contract PendingNoteHashes { context.encrypt_and_emit_note( context.this_address(), note.get_header().storage_slot, - ValueNote::get_note_type_id(), outgoing_viewer_ovpk_m, owner_ivpk_m, note @@ -372,7 +371,6 @@ contract PendingNoteHashes { context.encrypt_and_emit_note( context.this_address(), existing_note_header.storage_slot, - ValueNote::get_note_type_id(), outgoing_viewer_ovpk_m, owner_ivpk_m, bad_note diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr index 1282c27e800d..819a3a3916c4 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr @@ -44,7 +44,6 @@ impl NoteInterface for PublicKey context.encrypt_and_emit_note( (*context).this_address(), slot, - Self::get_note_type_id(), ovpk_m, ivpk_m, self, diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index c27d292625ea..29f2112959d0 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -56,7 +56,6 @@ impl NoteInterface for TokenNote { context.encrypt_and_emit_note( (*context).this_address(), slot, - Self::get_note_type_id(), ovpk_m, ivpk_m, self, diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr index c27d292625ea..29f2112959d0 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr @@ -56,7 +56,6 @@ impl NoteInterface for TokenNote { context.encrypt_and_emit_note( (*context).this_address(), slot, - Self::get_note_type_id(), ovpk_m, ivpk_m, self, diff --git a/yarn-project/circuits.js/src/keys/derivation.ts b/yarn-project/circuits.js/src/keys/derivation.ts index c867bebd61a2..4d5e5ba3915b 100644 --- a/yarn-project/circuits.js/src/keys/derivation.ts +++ b/yarn-project/circuits.js/src/keys/derivation.ts @@ -22,11 +22,17 @@ export function computeAppSecretKey(skM: GrumpkinPrivateKey, app: AztecAddress, } export function computeIvpkApp(ivpk: PublicKey, address: AztecAddress) { + return ivpk; + // Computing the siloed key is actually useless because we can derive the master key from it + // Issue(#6955) const I = Fq.fromBuffer(poseidon2Hash([address.toField(), ivpk.x, ivpk.y, GeneratorIndex.IVSK_M]).toBuffer()); return curve.add(curve.mul(Grumpkin.generator, I), ivpk); } export function computeIvskApp(ivsk: GrumpkinPrivateKey, address: AztecAddress) { + return ivsk; + // Computing the siloed key is actually useless because we can derive the master key from it + // Issue(#6955) const ivpk = curve.mul(Grumpkin.generator, ivsk); // Here we are intentionally converting Fr (output of poseidon) to Fq. This is fine even though a distribution of // P = s * G will not be uniform because 2 * (q - r) / q is small. diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index f3a731eab2c3..538bacf920f7 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -55,7 +55,10 @@ describe('e2e_block_building', () => { const ownerAddress = owner.getCompleteAddress().address; const outgoingViewer = ownerAddress; - const methods = times(TX_COUNT, i => deployer.deploy(ownerAddress, outgoingViewer, i)); + // Need to have value > 0, so adding + 1 + // We need to do so, because noir currently will fail if the multiscalarmul is in an `if` + // that we DO NOT enter. This should be fixed by https://github.com/noir-lang/noir/issues/5045. + const methods = times(TX_COUNT, i => deployer.deploy(ownerAddress, outgoingViewer, i + 1)); for (let i = 0; i < TX_COUNT; i++) { await methods[i].create({ contractAddressSalt: new Fr(BigInt(i + 1)), diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 0e30f7fe9c00..1309dc26323e 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -383,6 +383,9 @@ export class ClientExecutionContext extends ViewDataOracle { const ephSk = GrumpkinScalar.random(); + // @todo This should be populated properly. + // Note that this encryption function SHOULD not be used, but is currently used + // as oracle for encrypted event logs. const recipient = AztecAddress.random(); return taggedNote.encrypt(ephSk, recipient, ivpkM, ovKeys);