From edf9f8d270f8687589dfdc475907c642e645cfc7 Mon Sep 17 00:00:00 2001 From: iAmMichaelConnor Date: Tue, 28 Jan 2025 11:49:08 +0000 Subject: [PATCH 1/9] address nico's pr comments --- .../default_aes128/event.nr | 87 +++++----- .../default_aes128/note.nr | 158 +++++++----------- .../default_aes128/partial_note.nr | 73 ++++---- .../aztec-nr/aztec/src/keys/ephemeral.nr | 2 +- .../aztec-nr/aztec/src/utils/bytes.nr | 28 ++-- .../crates/types/src/utils/arrays.nr | 48 ++++++ 6 files changed, 201 insertions(+), 195 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr index 942f371fa26b..e7e9fcba2f3e 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr @@ -3,8 +3,7 @@ use crate::{ encrypted_logs::{ encrypt::aes128::derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret_using_sha256, log_assembly_strategies::default_aes128::note::{ - get_arr_of_size__log_bytes__from_PT, get_arr_of_size__log_bytes_padding__from_PT, - HEADER_CIPHERTEXT_SIZE_IN_BYTES, + get_arr_of_size__log_bytes_padding__from_plaintext, HEADER_CIPHERTEXT_SIZE_IN_BYTES, }, }, event::event_interface::EventInterface, @@ -19,7 +18,10 @@ use crate::{ utils::{bytes::{be_bytes_31_to_fields, get_random_bytes}, point::get_sign_of_point}, }; use dep::protocol_types::{ - address::AztecAddress, constants::PRIVATE_LOG_SIZE_IN_FIELDS, traits::Serialize, + address::AztecAddress, + constants::PRIVATE_LOG_SIZE_IN_FIELDS, + traits::Serialize, + utils::arrays::{array_concat, array_concat_3, array_concat_4}, }; use std::aes128::aes128_encrypt; @@ -147,14 +149,12 @@ where let contract_address = context.this_address(); let contract_address_bytes = contract_address.to_field().to_be_bytes::<32>(); - let mut header_plaintext: [u8; 32 + 2] = [0; 32 + 2]; - for i in 0..32 { - header_plaintext[i] = contract_address_bytes[i]; - } - let offset = 32; let ciphertext_bytes_length = ciphertext_bytes.len(); - header_plaintext[offset] = (ciphertext_bytes_length >> 8) as u8; - header_plaintext[offset + 1] = ciphertext_bytes_length as u8; + let encoding_of_ciphertext_size_in_bytes = + [(ciphertext_bytes_length >> 8) as u8, ciphertext_bytes_length as u8]; + + let header_plaintext = + array_concat(contract_address_bytes, encoding_of_ciphertext_size_in_bytes); // TODO: this is insecure and wasteful: // "Insecure", because the esk shouldn't be used twice (once for the header, @@ -170,9 +170,8 @@ where // Note: the aes128_encrypt builtin fn automatically appends bytes to the // input, according to pkcs#7; hence why the output `header_ciphertext_bytes` is 16 // bytes larger than the input in this case. - let header_ciphertext_bytes = aes128_encrypt(header_plaintext, iv, sym_key); - // I recall that converting a slice to an array incurs constraints, so I'll check the length this way instead: - assert(header_ciphertext_bytes.len() == HEADER_CIPHERTEXT_SIZE_IN_BYTES); + let header_ciphertext_bytes: [u8; HEADER_CIPHERTEXT_SIZE_IN_BYTES] = + aes128_encrypt(header_plaintext, iv, sym_key); // ***************************************************************************** // Prepend / append more bytes of data to the ciphertext, before converting back @@ -180,26 +179,17 @@ where // ***************************************************************************** let mut log_bytes_padding_to_mult_31 = - get_arr_of_size__log_bytes_padding__from_PT::<(N * 32) + 32>(); + get_arr_of_size__log_bytes_padding__from_plaintext(final_plaintext); + /// Safety: this randomness won't be constrained to be random. It's in the + /// interest of the executor of this fn to encrypt with random bytes. log_bytes_padding_to_mult_31 = unsafe { get_random_bytes() }; - let mut log_bytes = get_arr_of_size__log_bytes__from_PT::<(N * 32) + 32>(); - - log_bytes[0] = eph_pk_sign_byte; - let mut offset = 1; - for i in 0..header_ciphertext_bytes.len() { - log_bytes[offset + i] = header_ciphertext_bytes[i]; - } - offset += header_ciphertext_bytes.len(); - - for i in 0..ciphertext_bytes.len() { - log_bytes[offset + i] = ciphertext_bytes[i]; - } - offset += ciphertext_bytes.len(); - - for i in 0..log_bytes_padding_to_mult_31.len() { - log_bytes[offset + i] = log_bytes_padding_to_mult_31[i]; - } + let log_bytes = array_concat_4( + [eph_pk_sign_byte], + header_ciphertext_bytes, + ciphertext_bytes, + log_bytes_padding_to_mult_31, + ); // ***************************************************************************** // Convert bytes back to fields @@ -219,18 +209,29 @@ where let tag = unsafe { get_app_tag_as_sender(sender, recipient) }; increment_app_tagging_secret_index_as_sender(sender, recipient); - let mut final_log: [Field; PRIVATE_LOG_SIZE_IN_FIELDS] = [0; PRIVATE_LOG_SIZE_IN_FIELDS]; - - final_log[0] = tag; - final_log[1] = eph_pk.x; - - let mut offset = 2; - for i in 0..log_bytes_as_fields.len() { - final_log[offset + i] = log_bytes_as_fields[i]; + // We pad any unused fields of the final log with random fields, so that all + // logs on the network are indistinguishable. + // TODO(#8977): consider introducing various sizes of log privacy set, so that + // we're not broadcasting so many wasteful bytes for all apps. + // + // Note: this tidier approach did not work: it broke the compiler with some kind of + // stack too deep error, maybe as a result of the huge arithmetic generic statements + // it needed to evaluate to get the fields_padding array size. + // let final_log_random_fields_padding = get_arr_of_size__fields_padding__from_plaintext(final_plaintext_bytes); + // for i in 0..final_log_random_fields_padding.len() { + // /// Safety: randomness cannot be constrained. + // final_log[i] = unsafe { random() }; + // } + // let final_log = array_concat_3([tag, eph_pk.x], log_bytes_as_fields, final_log_random_fields_padding); + + let final_log_without_padding = array_concat([tag, eph_pk.x], log_bytes_as_fields); + let mut final_log = [0; PRIVATE_LOG_SIZE_IN_FIELDS]; + for i in 0..final_log_without_padding.len() { + final_log[i] = final_log_without_padding[i]; } - offset += log_bytes_as_fields.len(); - - for i in offset..PRIVATE_LOG_SIZE_IN_FIELDS { + let offset = final_log_without_padding.len(); + for i in offset..final_log.len() { + /// Safety: randomness cannot be constrained. final_log[i] = unsafe { random() }; } @@ -265,7 +266,7 @@ where // Important note: this function -- although called "unconstrained" -- the // function is not labelled as `unconstrained`, because we pass a reference to the -// context. +// context. Pushing something to the context must always be constrained. pub fn encode_and_encrypt_event_unconstrained( context: &mut PrivateContext, recipient: AztecAddress, diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr index eb368074aed1..2386ef8dd547 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr @@ -13,12 +13,24 @@ use crate::{ utils::{bytes::{be_bytes_31_to_fields, get_random_bytes}, point::get_sign_of_point}, }; use dep::protocol_types::{ - abis::note_hash::NoteHash, address::AztecAddress, constants::PRIVATE_LOG_SIZE_IN_FIELDS, + abis::note_hash::NoteHash, + address::AztecAddress, + constants::PRIVATE_LOG_SIZE_IN_FIELDS, + utils::arrays::{array_concat, array_concat_3, array_concat_4}, }; use std::aes128::aes128_encrypt; +pub(crate) global TAG_SIZE_IN_FIELDS: u32 = 1; +pub(crate) global EPH_PK_X_SIZE_IN_FIELDS: u32 = 1; + +pub(crate) global EPH_PK_Y_SIGN_SIZE_IN_BYTES: u32 = 1; // Technically, this only needs to occupy 1 bit, but we fill one byte here, for ease. pub(crate) global HEADER_CIPHERTEXT_SIZE_IN_BYTES: u32 = 48; // contract_address (32) + ciphertext_length (2) + 16 bytes pkcs#7 AES padding. +pub(crate) global CONTRACT_ADDRESS_SIZE_IN_BYTES: u32 = 32; + +// Difficult to name: the number of bytes needed to convey the size (in bytes) of the ciphertext +pub(crate) global SIZE_OF_ENCODING_OF_CIPHERTEXT_SIZE_IN_BYTES: u32 = 2; + /* * WHY IS THERE LOTS OF CODE DUPLICATION BETWEEN event.nr and note.nr? * It's because there are a few more optimisations that can be done for notes, @@ -54,9 +66,11 @@ pub(crate) global HEADER_CIPHERTEXT_SIZE_IN_BYTES: u32 = 48; // contract_address * * Long explanation: * tag: Field - * epk: [Field, u8] - * header_ct: [[u8; 32], [u8; 2], [u8; 16]] - * ct: [[u8; 2], [u8; x], [u8; y]] + * epk: [Field, u8] // the sign of the y-coord occupies that u8. + * header_ct: [[u8; 32], [u8; 2], [u8; 14]] + * ct: [[u8; x], [u8; 16 - (x % 16)]] + * padding_to_mult_31: [u8; p] + * fields_padding: [Field; whatever's left] * * More precisely (in pseudocode): * @@ -121,8 +135,9 @@ pub(crate) global HEADER_CIPHERTEXT_SIZE_IN_BYTES: u32 = 48; // contract_address // can be audited and understood. Now, we can't do arithmetic on generics in the body // of a function, so we abusing functions in the following way: -// |full_pt| = |pt| = (N * 32) + 64 -fn get_arr_of_size__full_plaintext() -> [u8; PT] { +// Instantiates an array that can be fed into the subsequent functions, to compute the +// more-complex numeric generics. +fn get_arr_of_size__full_plaintext(_plaintext: [u8; PT]) -> [u8; PT] { [0; PT] } @@ -145,6 +160,7 @@ fn get_arr_of_size__ciphertext( // eph_pk_sign, header_ciphertext, ciphertext: // Let lbwop = 1 + 48 + |ct| // aka log bytes without padding fn get_arr_of_size__log_bytes_without_padding(_ct: [u8; CT]) -> [u8; 1 + 48 + CT] { + // 1 + 48 + CT [0; 1 + 48 + CT] } @@ -161,22 +177,13 @@ fn get_arr_of_size__log_bytes_padding( [0; (31 * ((LBWOP + 30) / 31)) - LBWOP] } -// |log_bytes| = 1 + 48 + |ct| + p // aka log bytes (with padding) -// Recall: -// lbwop := 1 + 48 + |ct| -// p is the padding -fn get_arr_of_size__log_bytes( - _lbwop: [u8; LBWOP], - _p: [u8; P], -) -> [u8; LBWOP + P] { - [0; LBWOP + P] -} - // The return type is pasted from the LSP's expectation, because it was too difficult // to match its weird way of doing algebra. It doesn't know all rules of arithmetic. // PT is the plaintext length. -pub(crate) fn get_arr_of_size__log_bytes_padding__from_PT() -> [u8; ((((((PT + (16 - (PT % 16))) + 49) + 30) / 31) * 31) - ((PT + (16 - (PT % 16))) + 49))] { - let full_pt = get_arr_of_size__full_plaintext::(); +pub(crate) fn get_arr_of_size__log_bytes_padding__from_plaintext( + plaintext: [u8; PT], +) -> [u8; ((((((PT + (16 - (PT % 16))) + 49) + 30) / 31) * 31) - ((PT + (16 - (PT % 16))) + 49))] { + let full_pt = get_arr_of_size__full_plaintext(plaintext); let pt_aes_padding = get_arr_of_size__plaintext_aes_padding(full_pt); let ct = get_arr_of_size__ciphertext(full_pt, pt_aes_padding); let lbwop = get_arr_of_size__log_bytes_without_padding(ct); @@ -184,18 +191,6 @@ pub(crate) fn get_arr_of_size__log_bytes_padding__from_PT() -> [u8; p } -// The return type is pasted from the LSP's expectation, because it was too difficult -// to match its weird way of doing algebra. It doesn't know all rules of arithmetic. -pub(crate) fn get_arr_of_size__log_bytes__from_PT() -> [u8; (((PT + (16 - (PT % 16))) + 49) + ((((((PT + (16 - (PT % 16))) + 49) + 30) / 31) * 31) - ((PT + (16 - (PT % 16))) + 49)))] { - let full_pt = get_arr_of_size__full_plaintext::(); - let pt_aes_padding = get_arr_of_size__plaintext_aes_padding(full_pt); - let ct = get_arr_of_size__ciphertext(full_pt, pt_aes_padding); - let lbwop = get_arr_of_size__log_bytes_without_padding(ct); - let p = get_arr_of_size__log_bytes_padding(lbwop); - let log_bytes = get_arr_of_size__log_bytes(lbwop, p); - log_bytes -} - /********************************************************/ // End of disgusting arithmetic on generics /********************************************************/ @@ -290,16 +285,6 @@ where let ciphertext_bytes = aes128_encrypt(final_plaintext_bytes, iv, sym_key); - // |full_pt| = |pt_length| + |pt| - // |pt_aes_padding| = 16 - (|full_pt| % 16) - // or... since a % b is the same as a - b * (a // b) (integer division), so: - // |pt_aes_padding| = 16 - (|full_pt| - 16 * (|full_pt| // 16)) - // |ct| = |full_pt| + |pt_aes_padding| - // = |full_pt| + 16 - (|full_pt| - 16 * (|full_pt| // 16)) - // = 16 + 16 * (|full_pt| // 16) - // = 16 * (1 + |full_pt| // 16) - assert(ciphertext_bytes.len() == 16 * (1 + ((N * 32) + 64) / 16)); - // ***************************************************************************** // Compute the header ciphertext // ***************************************************************************** @@ -307,14 +292,12 @@ where let contract_address = context.this_address(); let contract_address_bytes = contract_address.to_field().to_be_bytes::<32>(); - let mut header_plaintext: [u8; 32 + 2] = [0; 32 + 2]; - for i in 0..32 { - header_plaintext[i] = contract_address_bytes[i]; - } - let offset = 32; let ciphertext_bytes_length = ciphertext_bytes.len(); - header_plaintext[offset] = (ciphertext_bytes_length >> 8) as u8; - header_plaintext[offset + 1] = ciphertext_bytes_length as u8; + let encoding_of_ciphertext_size_in_bytes = + [(ciphertext_bytes_length >> 8) as u8, ciphertext_bytes_length as u8]; + + let header_plaintext = + array_concat(contract_address_bytes, encoding_of_ciphertext_size_in_bytes); // TODO: this is insecure and wasteful: // "Insecure", because the esk shouldn't be used twice (once for the header, @@ -330,9 +313,8 @@ where // Note: the aes128_encrypt builtin fn automatically appends bytes to the // input, according to pkcs#7; hence why the output `header_ciphertext_bytes` is 16 // bytes larger than the input in this case. - let header_ciphertext_bytes = aes128_encrypt(header_plaintext, iv, sym_key); - // I recall that converting a slice to an array incurs constraints, so I'll check the length this way instead: - assert(header_ciphertext_bytes.len() == HEADER_CIPHERTEXT_SIZE_IN_BYTES); + let header_ciphertext_bytes: [u8; HEADER_CIPHERTEXT_SIZE_IN_BYTES] = + aes128_encrypt(header_plaintext, iv, sym_key); // ***************************************************************************** // Prepend / append more bytes of data to the ciphertext, before converting back @@ -340,37 +322,16 @@ where // ***************************************************************************** let mut log_bytes_padding_to_mult_31 = - get_arr_of_size__log_bytes_padding__from_PT::<(N * 32) + 64>(); + get_arr_of_size__log_bytes_padding__from_plaintext(final_plaintext_bytes); /// Safety: this randomness won't be constrained to be random. It's in the /// interest of the executor of this fn to encrypt with random bytes. log_bytes_padding_to_mult_31 = unsafe { get_random_bytes() }; - let mut log_bytes = get_arr_of_size__log_bytes__from_PT::<(N * 32) + 64>(); - - assert( - log_bytes.len() % 31 == 0, - "Unexpected error: log_bytes.len() should be divisible by 31, by construction.", - ); - - log_bytes[0] = eph_pk_sign_byte; - let mut offset = 1; - for i in 0..header_ciphertext_bytes.len() { - log_bytes[offset + i] = header_ciphertext_bytes[i]; - } - offset += header_ciphertext_bytes.len(); - - for i in 0..ciphertext_bytes.len() { - log_bytes[offset + i] = ciphertext_bytes[i]; - } - offset += ciphertext_bytes.len(); - - for i in 0..log_bytes_padding_to_mult_31.len() { - log_bytes[offset + i] = log_bytes_padding_to_mult_31[i]; - } - - assert( - offset + log_bytes_padding_to_mult_31.len() == log_bytes.len(), - "Something has gone wrong", + let log_bytes = array_concat_4( + [eph_pk_sign_byte], + header_ciphertext_bytes, + ciphertext_bytes, + log_bytes_padding_to_mult_31, ); // ***************************************************************************** @@ -385,24 +346,34 @@ where // In this strategy, we prepend [tag, eph_pk.x] - // We assume that the sender wants for the recipient to find the tagged note, - // and therefore that they will cooperate and use the correct tag. Usage of a bad - // tag will result in the recipient not being able to find the note automatically. + /// Safety: We assume that the sender wants for the recipient to find the tagged note, + /// and therefore that they will cooperate and use the correct tag. Usage of a bad + /// tag will result in the recipient not being able to find the note automatically. let tag = unsafe { get_app_tag_as_sender(sender, recipient) }; increment_app_tagging_secret_index_as_sender(sender, recipient); - let mut final_log: [Field; PRIVATE_LOG_SIZE_IN_FIELDS] = [0; PRIVATE_LOG_SIZE_IN_FIELDS]; - - final_log[0] = tag; - final_log[1] = eph_pk.x; - - let mut offset = 2; - for i in 0..log_bytes_as_fields.len() { - final_log[offset + i] = log_bytes_as_fields[i]; + // We pad any unused fields of the final log with random fields, so that all + // logs on the network are indistinguishable. + // TODO(#8977): consider introducing various sizes of log privacy set, so that + // we're not broadcasting so many wasteful bytes for all apps. + // + // Note: this tidier approach did not work: it broke the compiler with some kind of + // stack too deep error, maybe as a result of the huge arithmetic generic statements + // it needed to evaluate to get the fields_padding array size. + // let final_log_random_fields_padding = get_arr_of_size__fields_padding__from_plaintext(final_plaintext_bytes); + // for i in 0..final_log_random_fields_padding.len() { + // /// Safety: randomness cannot be constrained. + // final_log[i] = unsafe { random() }; + // } + // let final_log = array_concat_3([tag, eph_pk.x], log_bytes_as_fields, final_log_random_fields_padding); + + let final_log_without_padding = array_concat([tag, eph_pk.x], log_bytes_as_fields); + let mut final_log = [0; PRIVATE_LOG_SIZE_IN_FIELDS]; + for i in 0..final_log_without_padding.len() { + final_log[i] = final_log_without_padding[i]; } - offset += log_bytes_as_fields.len(); - - for i in offset..PRIVATE_LOG_SIZE_IN_FIELDS { + let offset = final_log_without_padding.len(); + for i in offset..final_log.len() { /// Safety: randomness cannot be constrained. final_log[i] = unsafe { random() }; } @@ -422,9 +393,6 @@ where compute_log(context, note, recipient, sender) } -// This function seems to be affected by the following Noir bug: -// https://github.com/noir-lang/noir/issues/5771 -// If you get weird behavior it might be because of it. pub fn encode_and_encrypt_note( context: &mut PrivateContext, recipient: AztecAddress, diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/partial_note.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/partial_note.nr index 74fcd4b140c0..e2f400320329 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/partial_note.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/partial_note.nr @@ -5,8 +5,7 @@ use crate::{ encrypted_logs::{ encrypt::aes128::derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret_using_sha256, log_assembly_strategies::default_aes128::note::{ - get_arr_of_size__log_bytes__from_PT, get_arr_of_size__log_bytes_padding__from_PT, - HEADER_CIPHERTEXT_SIZE_IN_BYTES, + get_arr_of_size__log_bytes_padding__from_plaintext, HEADER_CIPHERTEXT_SIZE_IN_BYTES, }, }, keys::{ @@ -16,7 +15,11 @@ use crate::{ oracle::notes::{get_app_tag_as_sender, increment_app_tagging_secret_index_as_sender}, utils::{bytes::{be_bytes_31_to_fields, get_random_bytes}, point::get_sign_of_point}, }; -use dep::protocol_types::{address::{aztec_address::ToField, AztecAddress}, hash::poseidon2_hash}; +use dep::protocol_types::{ + address::{aztec_address::ToField, AztecAddress}, + hash::poseidon2_hash, + utils::arrays::{array_concat, array_concat_4}, +}; use std::aes128::aes128_encrypt; pub fn compute_partial_public_log_payload( @@ -38,12 +41,10 @@ pub fn compute_partial_public_log_payload( // TODO: also use this shared secret for deriving note randomness. // ***************************************************************************** - // Prepend/append extra bytes + // Compute the plaintext // ***************************************************************************** - // "Proper" meaning the main meaty stuff that we care about. - let proper_plaintext: [u8; N] = plaintext; - let final_plaintext = proper_plaintext; + let final_plaintext = plaintext; // ***************************************************************************** // Convert the plaintext into whatever format the encryption function expects @@ -52,7 +53,7 @@ pub fn compute_partial_public_log_payload( // Already done for this strategy: AES expects bytes. // ***************************************************************************** - // Encrypt + // Encrypt the plaintext // ***************************************************************************** let (sym_key, iv) = derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret_using_sha256( @@ -61,23 +62,18 @@ pub fn compute_partial_public_log_payload( let ciphertext_bytes = aes128_encrypt(final_plaintext, iv, sym_key); - assert(ciphertext_bytes.len() == 16 * (1 + (2 + N) / 16)); - // ***************************************************************************** // Compute the header ciphertext // ***************************************************************************** let contract_address_bytes = contract_address.to_field().to_be_bytes::<32>(); - let mut header_plaintext: [u8; 32 + 2] = [0; 32 + 2]; - for i in 0..32 { - header_plaintext[i] = contract_address_bytes[i]; - } - let offset = contract_address_bytes.len(); - let ciphertext_bytes_length = ciphertext_bytes.len(); - header_plaintext[offset] = (ciphertext_bytes_length >> 8) as u8; - header_plaintext[offset + 1] = ciphertext_bytes_length as u8; + let encoding_of_ciphertext_size_in_bytes = + [(ciphertext_bytes_length >> 8) as u8, ciphertext_bytes_length as u8]; + + let header_plaintext = + array_concat(contract_address_bytes, encoding_of_ciphertext_size_in_bytes); // TODO: this is insecure and wasteful: // "Insecure", because the esk shouldn't be used twice (once for the header, @@ -93,35 +89,26 @@ pub fn compute_partial_public_log_payload( // Note: the aes128_encrypt builtin fn automatically appends bytes to the // input, according to pkcs#7; hence why the output `header_ciphertext_bytes` is 16 // bytes larger than the input in this case. - let header_ciphertext_bytes = aes128_encrypt(header_plaintext, iv, sym_key); - // I recall that converting a slice to an array incurs constraints, so I'll check the length this way instead: - assert(header_ciphertext_bytes.len() == HEADER_CIPHERTEXT_SIZE_IN_BYTES); + let header_ciphertext_bytes: [u8; HEADER_CIPHERTEXT_SIZE_IN_BYTES] = + aes128_encrypt(header_plaintext, iv, sym_key); // ***************************************************************************** // Prepend / append more bytes of data to the ciphertext, before converting back // to fields. // ***************************************************************************** - let mut log_bytes_padding_to_mult_31 = get_arr_of_size__log_bytes_padding__from_PT::<2 + N>(); + let mut log_bytes_padding_to_mult_31 = + get_arr_of_size__log_bytes_padding__from_plaintext(final_plaintext); + /// Safety: this randomness won't be constrained to be random. It's in the + /// interest of the executor of this fn to encrypt with random bytes. log_bytes_padding_to_mult_31 = unsafe { get_random_bytes() }; - let mut log_bytes = get_arr_of_size__log_bytes__from_PT::<2 + N>(); - - log_bytes[0] = eph_pk_sign_byte; - let mut offset = 1; - for i in 0..header_ciphertext_bytes.len() { - log_bytes[offset + i] = header_ciphertext_bytes[i]; - } - offset += header_ciphertext_bytes.len(); - - for i in 0..ciphertext_bytes.len() { - log_bytes[offset + i] = ciphertext_bytes[i]; - } - offset += ciphertext_bytes.len(); - - for i in 0..log_bytes_padding_to_mult_31.len() { - log_bytes[offset + i] = log_bytes_padding_to_mult_31[i]; - } + let log_bytes = array_concat_4( + [eph_pk_sign_byte], + header_ciphertext_bytes, + ciphertext_bytes, + log_bytes_padding_to_mult_31, + ); // ***************************************************************************** // Convert bytes back to fields @@ -152,9 +139,6 @@ pub fn compute_partial_public_log_payload( // TODO(#10273) This should be done by the AVM when it's processing the raw logs instead of their hashes. let siloed_tag = poseidon2_hash([contract_address.to_field(), tag]); - // Temporary hack so that the partial public log remains the same format. - // It should return field array and make the tag the first field as compute_private_log_payload does. - let mut final_log: [Field; M] = [0; M]; final_log[0] = siloed_tag; @@ -165,5 +149,10 @@ pub fn compute_partial_public_log_payload( final_log[offset + i] = log_bytes_as_fields[i]; } + // Interestingly, this more-succinct version of the above doesn't work: + // the compiler can't figure out that the concatenation of these fields + // results in length M. + // let final_log: [Field; M] = array_concat_3([siloed_tag], [eph_pk.x], log_bytes_as_fields); + final_log } diff --git a/noir-projects/aztec-nr/aztec/src/keys/ephemeral.nr b/noir-projects/aztec-nr/aztec/src/keys/ephemeral.nr index 99b1f84989c3..e5cc8f3bdd28 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/ephemeral.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/ephemeral.nr @@ -8,7 +8,7 @@ use dep::protocol_types::{point::Point, scalar::Scalar}; use crate::oracle::random::random; /// Converts a base field element to scalar field element. -/// This is fine because modulus of the base field is smaller than the modulus of the scalar field. +/// This is lossless because the modulus of the base field is smaller than the modulus of the scalar field. fn fr_to_fq(r: Field) -> Scalar { let (lo, hi) = decompose(r); diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index 02d1764c204b..55f7899610d4 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -300,7 +300,7 @@ mod test { // BE BYTES #[test] - fn test_be_bytes_31_to_1_field() { + fn be_bytes_31_to_1_field() { let input = [ 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, @@ -361,7 +361,7 @@ mod test { } #[test] - fn test_be_bytes_31_to_2_fields() { + fn be_bytes_31_to_2_fields() { let input = [ 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, @@ -392,7 +392,7 @@ mod test { } #[test] - fn test_large_random_be_bytes_31_input_to_fields_and_back(input: [u8; 128]) { + fn large_random_be_bytes_31_input_to_fields_and_back(input: [u8; 128]) { let output = be_bytes_31_to_fields(input); let input_back: [u8; 128] = fields_to_be_bytes_31(output); @@ -403,7 +403,7 @@ mod test { // operation is not supported on a Field (to do field % 2^248), I will take multiple smaller values and combine // them to get a value lower than 2^248. #[test] - fn test_large_random_input_to_be_bytes_31_and_back( + fn large_random_input_to_be_bytes_31_and_back( input1: [u64; 5], input2: [u64; 5], input3: [u64; 5], @@ -428,7 +428,7 @@ mod test { } #[test(should_fail_with = "call to assert_max_bit_size")] - fn test_too_few_destination_be_bytes() { + fn too_few_destination_be_bytes() { // We should get an error here because first field gets converted to 31 bytes and the second field needs // at least 2 bytes but we provide it with 1. let input = [1, 256]; @@ -436,13 +436,13 @@ mod test { } #[test(should_fail_with = "call to assert_max_bit_size")] - fn test_fields_to_be_bytes_31_value_too_large() { + fn fields_to_be_bytes_31_value_too_large() { let input = [2.pow_32(248)]; // this number is 1 bit larger than 31 bytes. let _ignored_result: [u8; 31] = fields_to_be_bytes_31(input); } #[test] - fn test_fields_to_be_bytes_31_max_value() { + fn fields_to_be_bytes_31_max_value() { let input = [2.pow_32(248) - 1]; let result: [u8; 31] = fields_to_be_bytes_31(input); @@ -455,7 +455,7 @@ mod test { // LE BYTES #[test] - fn test_le_bytes_31_to_1_field() { + fn le_bytes_31_to_1_field() { let input = [ 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, @@ -515,7 +515,7 @@ mod test { } #[test] - fn test_le_bytes_31_to_2_fields() { + fn le_bytes_31_to_2_fields() { let input = [ 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, @@ -546,7 +546,7 @@ mod test { } #[test] - fn test_large_random_le_bytes_input_to_fields_and_back(input: [u8; 128]) { + fn large_random_le_bytes_input_to_fields_and_back(input: [u8; 128]) { let output = le_bytes_31_to_fields(input); let input_back: [u8; 128] = fields_to_le_bytes_31(output); @@ -557,7 +557,7 @@ mod test { // operation is not supported on a Field (to do field % 2^248), I will take multiple smaller values and combine // them to get a value lower than 2^248. #[test] - fn test_large_random_input_to_le_bytes_and_back( + fn large_random_input_to_le_bytes_and_back( input1: [u64; 5], input2: [u64; 5], input3: [u64; 5], @@ -582,7 +582,7 @@ mod test { } #[test(should_fail_with = "call to assert_max_bit_size")] - fn test_too_few_destination_le_bytes() { + fn too_few_destination_le_bytes() { // We should get an error here because first field gets converted to 31 bytes and the second field needs // at least 2 bytes but we provide it with 1. let input = [1, 256]; @@ -590,13 +590,13 @@ mod test { } #[test(should_fail_with = "call to assert_max_bit_size")] - fn test_fields_to_le_bytes_31_value_too_large() { + fn fields_to_le_bytes_31_value_too_large() { let input = [2.pow_32(248)]; let _ignored_result: [u8; 31] = fields_to_le_bytes_31(input); } #[test] - fn test_fields_to_le_bytes_31_max_value() { + fn fields_to_le_bytes_31_max_value() { let input = [2.pow_32(248) - 1]; let result: [u8; 31] = fields_to_le_bytes_31(input); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr index 44a76b077121..e76c19def886 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr @@ -136,6 +136,54 @@ pub fn array_concat(array1: [T; N], array2: [T; M]) - result } +/// Similar to the standard array concat, but it concatenates 3 arrays. +/// Within an unconstrained function, this is more efficient than using `array_concat` +/// twice, since this doesn't have redundant `for` loops. It is uglier, though. +pub fn array_concat_3( + array1: [T; N], + array2: [T; M], + array3: [T; L], +) -> [T; N + M + L] { + let mut result = [array1[0]; N + M + L]; + for i in 1..N { + result[i] = array1[i]; + } + let mut offset = N; + for i in 0..M { + result[offset + i] = array2[i]; + } + offset += M; + for i in 0..L { + result[offset + i] = array3[i]; + } + result +} + +pub fn array_concat_4( + array1: [T; N], + array2: [T; M], + array3: [T; L], + array4: [T; K], +) -> [T; N + M + L + K] { + let mut result = [array1[0]; N + M + L + K]; + for i in 1..N { + result[i] = array1[i]; + } + let mut offset = N; + for i in 0..M { + result[offset + i] = array2[i]; + } + offset += M; + for i in 0..L { + result[offset + i] = array3[i]; + } + offset += L; + for i in 0..K { + result[offset + i] = array4[i]; + } + result +} + pub fn array_merge(array1: [T; N], array2: [T; N]) -> [T; N] where T: Empty + Eq, From 0530777bacc09a255d2482babd2b6da192f8f3b5 Mon Sep 17 00:00:00 2001 From: iAmMichaelConnor Date: Tue, 28 Jan 2025 11:56:19 +0000 Subject: [PATCH 2/9] more changes --- .../default_aes128/event.nr | 2 +- .../default_aes128/note.nr | 21 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr index e7e9fcba2f3e..bef923f04f2a 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr @@ -21,7 +21,7 @@ use dep::protocol_types::{ address::AztecAddress, constants::PRIVATE_LOG_SIZE_IN_FIELDS, traits::Serialize, - utils::arrays::{array_concat, array_concat_3, array_concat_4}, + utils::arrays::{array_concat, array_concat_4}, }; use std::aes128::aes128_encrypt; diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr index 2386ef8dd547..9675ca76a737 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr @@ -16,21 +16,13 @@ use dep::protocol_types::{ abis::note_hash::NoteHash, address::AztecAddress, constants::PRIVATE_LOG_SIZE_IN_FIELDS, - utils::arrays::{array_concat, array_concat_3, array_concat_4}, + utils::arrays::{array_concat, array_concat_4}, }; use std::aes128::aes128_encrypt; -pub(crate) global TAG_SIZE_IN_FIELDS: u32 = 1; -pub(crate) global EPH_PK_X_SIZE_IN_FIELDS: u32 = 1; - pub(crate) global EPH_PK_Y_SIGN_SIZE_IN_BYTES: u32 = 1; // Technically, this only needs to occupy 1 bit, but we fill one byte here, for ease. pub(crate) global HEADER_CIPHERTEXT_SIZE_IN_BYTES: u32 = 48; // contract_address (32) + ciphertext_length (2) + 16 bytes pkcs#7 AES padding. -pub(crate) global CONTRACT_ADDRESS_SIZE_IN_BYTES: u32 = 32; - -// Difficult to name: the number of bytes needed to convey the size (in bytes) of the ciphertext -pub(crate) global SIZE_OF_ENCODING_OF_CIPHERTEXT_SIZE_IN_BYTES: u32 = 2; - /* * WHY IS THERE LOTS OF CODE DUPLICATION BETWEEN event.nr and note.nr? * It's because there are a few more optimisations that can be done for notes, @@ -159,9 +151,11 @@ fn get_arr_of_size__ciphertext( // Ok, so we have the following bytes: // eph_pk_sign, header_ciphertext, ciphertext: // Let lbwop = 1 + 48 + |ct| // aka log bytes without padding -fn get_arr_of_size__log_bytes_without_padding(_ct: [u8; CT]) -> [u8; 1 + 48 + CT] { +fn get_arr_of_size__log_bytes_without_padding( + _ct: [u8; CT], +) -> [u8; EPH_PK_Y_SIGN_SIZE_IN_BYTES + HEADER_CIPHERTEXT_SIZE_IN_BYTES + CT] { // 1 + 48 + CT - [0; 1 + 48 + CT] + [0; EPH_PK_Y_SIGN_SIZE_IN_BYTES + HEADER_CIPHERTEXT_SIZE_IN_BYTES + CT] } // Recall: @@ -174,6 +168,11 @@ fn get_arr_of_size__log_bytes_without_padding(_ct: [u8; CT]) -> [u8 fn get_arr_of_size__log_bytes_padding( _lbwop: [u8; LBWOP], ) -> [u8; (31 * ((LBWOP + 30) / 31)) - LBWOP] { + // This static assertion is trivially true, but leaving it here to help people understand. + std::static_assert( + (LBWOP + (31 * ((LBWOP + 30) / 31)) - LBWOP) % 31 == 0, + "Padded log bytes should be divisible by 31", + ); [0; (31 * ((LBWOP + 30) / 31)) - LBWOP] } From 11db2d4639f96e8f9fc5fa6eb379b6e5699f8ec9 Mon Sep 17 00:00:00 2001 From: iAmMichaelConnor Date: Tue, 28 Jan 2025 12:05:57 +0000 Subject: [PATCH 3/9] a todo --- .../crates/types/src/utils/arrays.nr | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr index e76c19def886..712f364e9421 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr @@ -125,6 +125,7 @@ where length } +// TODO(#11555): replace with `.concat`. pub fn array_concat(array1: [T; N], array2: [T; M]) -> [T; N + M] { let mut result = [array1[0]; N + M]; for i in 1..N { @@ -322,6 +323,14 @@ fn test_array_concat() { assert_eq(concatenated, [1, 2, 3, 4, 5]); } +#[test] +fn test_array_concat_3() { + let array0 = [1, 2, 3]; + let array1 = [4, 5]; + let concatenated = array_concat(array0, array1); + assert_eq(concatenated, [1, 2, 3, 4, 5]); +} + #[test] fn check_permutation_basic_test() { let original_array = [1, 2, 3]; From 322f67f6d36bad852751596388608dcc31afec48 Mon Sep 17 00:00:00 2001 From: iAmMichaelConnor Date: Tue, 28 Jan 2025 12:10:00 +0000 Subject: [PATCH 4/9] array tests --- .../crates/types/src/utils/arrays.nr | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr index 712f364e9421..c6ad596e5261 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr @@ -327,8 +327,19 @@ fn test_array_concat() { fn test_array_concat_3() { let array0 = [1, 2, 3]; let array1 = [4, 5]; - let concatenated = array_concat(array0, array1); - assert_eq(concatenated, [1, 2, 3, 4, 5]); + let array2 = [6, 7, 8, 9]; + let concatenated = array_concat_3(array0, array1, array2); + assert_eq(concatenated, [1, 2, 3, 4, 5, 6, 7, 8, 9]); +} + +#[test] +fn test_array_concat_4() { + let array0 = [1]; + let array1 = [2, 3]; + let array2 = [4, 5, 6, 7]; + let array3 = [8, 9, 10]; + let concatenated = array_concat_4(array0, array1, array2, array3); + assert_eq(concatenated, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); } #[test] From 4bd0c0f2250942fa90724e5d1c1a35ed079008e8 Mon Sep 17 00:00:00 2001 From: Michael Connor Date: Tue, 28 Jan 2025 15:34:05 +0000 Subject: [PATCH 5/9] Update noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolás Venturo --- .../log_assembly_strategies/default_aes128/event.nr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr index bef923f04f2a..9a50b6870bfa 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr @@ -231,7 +231,8 @@ where } let offset = final_log_without_padding.len(); for i in offset..final_log.len() { - /// Safety: randomness cannot be constrained. + /// Safety: These values preserve the privacy of the caller, so it's in their interest to make them actually + /// random. final_log[i] = unsafe { random() }; } From 2894b14c24ed9a6cd611a5af79c99a3020545807 Mon Sep 17 00:00:00 2001 From: Michael Connor Date: Tue, 28 Jan 2025 15:34:37 +0000 Subject: [PATCH 6/9] Update noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolás Venturo --- .../log_assembly_strategies/default_aes128/event.nr | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr index 9a50b6870bfa..2dda14cd12f8 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr @@ -180,8 +180,7 @@ where let mut log_bytes_padding_to_mult_31 = get_arr_of_size__log_bytes_padding__from_plaintext(final_plaintext); - /// Safety: this randomness won't be constrained to be random. It's in the - /// interest of the executor of this fn to encrypt with random bytes. + /// Safety: These values preserve the privacy of the caller, so it's in their interest to make them actually random. log_bytes_padding_to_mult_31 = unsafe { get_random_bytes() }; let log_bytes = array_concat_4( From d4583464e6f2be030909229db2b018af7868427f Mon Sep 17 00:00:00 2001 From: Michael Connor Date: Tue, 28 Jan 2025 15:58:00 +0000 Subject: [PATCH 7/9] Update noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr --- .../log_assembly_strategies/default_aes128/event.nr | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr index 2dda14cd12f8..6ea0b3c6d7ce 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr @@ -266,7 +266,11 @@ where // Important note: this function -- although called "unconstrained" -- the // function is not labelled as `unconstrained`, because we pass a reference to the -// context. Pushing something to the context must always be constrained. +// context. +/// This is the same as `encode_and_encrypt_event`, except the encryption and therefore +/// contents of the events are not constrained, meaning the caller can place arbitrary content +/// there. Whether this is acceptable for your app will depend on whether the sender +// is incentivised (somehow) to act honestly and provide the correct event log or not. pub fn encode_and_encrypt_event_unconstrained( context: &mut PrivateContext, recipient: AztecAddress, From 2bf2d1fcdb46ba85aee1725ef4c3b0e666c6ee12 Mon Sep 17 00:00:00 2001 From: iAmMichaelConnor Date: Fri, 31 Jan 2025 16:54:47 +0000 Subject: [PATCH 8/9] jan comments --- .../default_aes128/event.nr | 18 +++++++++++------- .../default_aes128/note.nr | 5 +++++ .../default_aes128/partial_note.nr | 15 +++++++-------- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr index 6ea0b3c6d7ce..c563be9006d5 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr @@ -119,9 +119,7 @@ where // Prepend/append extra bytes // ***************************************************************************** - // "Proper" meaning the main meaty stuff that we care about. - let proper_plaintext = event_bytes; - let final_plaintext = proper_plaintext; + let final_plaintext = event_bytes; // ***************************************************************************** // Convert the plaintext into whatever format the encryption function expects @@ -150,6 +148,11 @@ where let contract_address_bytes = contract_address.to_field().to_be_bytes::<32>(); let ciphertext_bytes_length = ciphertext_bytes.len(); + // We take the u32 length and convert it to two u8's; i.e. two 8-bit big-endian limbs, + // So an 8-bit right-shift gives us the big limb (by discarding the little limb). + // The little limb is got by casting to a u8 (which discards higher bits). + // The capacity of two u8's is plenty: that allows for 2^16 = 65,536 bytes of + // ciphertext, which is more than enough space. let encoding_of_ciphertext_size_in_bytes = [(ciphertext_bytes_length >> 8) as u8, ciphertext_bytes_length as u8]; @@ -264,13 +267,14 @@ where } } -// Important note: this function -- although called "unconstrained" -- the -// function is not labelled as `unconstrained`, because we pass a reference to the -// context. /// This is the same as `encode_and_encrypt_event`, except the encryption and therefore /// contents of the events are not constrained, meaning the caller can place arbitrary content /// there. Whether this is acceptable for your app will depend on whether the sender -// is incentivised (somehow) to act honestly and provide the correct event log or not. +/// is incentivised (somehow) to act honestly and provide the correct event log or not. +/// +/// Important note: this function -- although called "unconstrained" -- the +/// function is not labelled as `unconstrained`, because we pass a reference to the +/// context. pub fn encode_and_encrypt_event_unconstrained( context: &mut PrivateContext, recipient: AztecAddress, diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr index f5bcb28ec4c4..e0cadc0c550c 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr @@ -292,6 +292,11 @@ where let contract_address_bytes = contract_address.to_field().to_be_bytes::<32>(); let ciphertext_bytes_length = ciphertext_bytes.len(); + // We take the u32 length and convert it to two u8's; i.e. two 8-bit big-endian limbs, + // So an 8-bit right-shift gives us the big limb (by discarding the little limb). + // The little limb is got by casting to a u8 (which discards higher bits). + // The capacity of two u8's is plenty: that allows for 2^16 = 65,536 bytes of + // ciphertext, which is more than enough space. let encoding_of_ciphertext_size_in_bytes = [(ciphertext_bytes_length >> 8) as u8, ciphertext_bytes_length as u8]; diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/partial_note.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/partial_note.nr index e2f400320329..540fccd3e159 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/partial_note.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/partial_note.nr @@ -40,12 +40,6 @@ pub fn compute_partial_public_log_payload( // TODO: also use this shared secret for deriving note randomness. - // ***************************************************************************** - // Compute the plaintext - // ***************************************************************************** - - let final_plaintext = plaintext; - // ***************************************************************************** // Convert the plaintext into whatever format the encryption function expects // ***************************************************************************** @@ -60,7 +54,7 @@ pub fn compute_partial_public_log_payload( ciphertext_shared_secret, ); - let ciphertext_bytes = aes128_encrypt(final_plaintext, iv, sym_key); + let ciphertext_bytes = aes128_encrypt(plaintext, iv, sym_key); // ***************************************************************************** // Compute the header ciphertext @@ -69,6 +63,11 @@ pub fn compute_partial_public_log_payload( let contract_address_bytes = contract_address.to_field().to_be_bytes::<32>(); let ciphertext_bytes_length = ciphertext_bytes.len(); + // We take the u32 length and convert it to two u8's; i.e. two 8-bit big-endian limbs, + // So an 8-bit right-shift gives us the big limb (by discarding the little limb). + // The little limb is got by casting to a u8 (which discards higher bits). + // The capacity of two u8's is plenty: that allows for 2^16 = 65,536 bytes of + // ciphertext, which is more than enough space. let encoding_of_ciphertext_size_in_bytes = [(ciphertext_bytes_length >> 8) as u8, ciphertext_bytes_length as u8]; @@ -98,7 +97,7 @@ pub fn compute_partial_public_log_payload( // ***************************************************************************** let mut log_bytes_padding_to_mult_31 = - get_arr_of_size__log_bytes_padding__from_plaintext(final_plaintext); + get_arr_of_size__log_bytes_padding__from_plaintext(plaintext); /// Safety: this randomness won't be constrained to be random. It's in the /// interest of the executor of this fn to encrypt with random bytes. log_bytes_padding_to_mult_31 = unsafe { get_random_bytes() }; From 654231981b8be597930611c303948816c4ea9bc0 Mon Sep 17 00:00:00 2001 From: iAmMichaelConnor Date: Fri, 31 Jan 2025 19:08:06 +0000 Subject: [PATCH 9/9] rm unnecessary assert --- .../log_assembly_strategies/default_aes128/event.nr | 2 -- 1 file changed, 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr index c563be9006d5..49c3cecd7681 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/event.nr @@ -137,8 +137,6 @@ where let ciphertext_bytes = aes128_encrypt(final_plaintext, iv, sym_key); - assert(ciphertext_bytes.len() == 16 * (1 + ((N * 32) + 32) / 16)); - // ***************************************************************************** // Compute the header ciphertext // *****************************************************************************