From 2a5ee4f4a5a31ba1582dceecee0a3d79a0ac31d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Wed, 17 Jul 2024 16:33:24 +0200 Subject: [PATCH] refactor: optimizing DA cost with new point compression (#7473) --- .../aztec/src/encrypted_logs/header.nr | 2 +- .../aztec/src/encrypted_logs/incoming_body.nr | 4 +- .../aztec/src/encrypted_logs/outgoing_body.nr | 17 ++--- .../aztec/src/encrypted_logs/payload.nr | 59 +++++++------- .../aztec/src/keys/point_to_symmetric_key.nr | 10 +-- .../aztec-nr/aztec/src/oracle/logs_traits.nr | 76 +++++++++---------- .../aztec-nr/aztec/src/utils/point.nr | 73 +++++++++++++++--- .../contracts/test_contract/src/main.nr | 2 +- .../crates/types/src/point.nr | 1 - .../src/logs/encrypted_l2_log.ts | 4 +- .../src/logs/encrypted_l2_note_log.ts | 4 +- .../src/logs/l1_payload/encrypt_buffer.ts | 6 +- .../l1_payload/encrypted_log_outgoing_body.ts | 6 +- .../src/logs/l1_payload/encryption_utils.ts | 2 +- .../src/logs/l1_payload/l1_payload.ts | 12 +-- .../src/logs/l1_payload/tagged_log.test.ts | 2 +- .../src/logs/l1_payload/tagged_log.ts | 4 +- .../foundation/src/fields/point.test.ts | 43 +++++++++++ yarn-project/foundation/src/fields/point.ts | 10 +-- 19 files changed, 216 insertions(+), 121 deletions(-) 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 943a9636acb..386e2338a6f 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr @@ -45,7 +45,7 @@ fn test_encrypted_log_header() { let ciphertext = header.compute_ciphertext(secret, point); let expected_header_ciphertext = [ - 228, 9, 65, 81, 62, 59, 249, 207, 90, 196, 206, 72, 39, 199, 82, 196, 23, 131, 32, 226, 26, 176, 43, 39, 239, 177, 177, 192, 85, 216, 17, 15, 18, 187, 35, 225, 135, 192, 63, 88, 29, 173, 232, 46, 72, 82, 187, 139 + 166, 212, 106, 246, 139, 59, 228, 9, 133, 152, 127, 172, 141, 166, 237, 199, 55, 203, 226, 19, 114, 103, 58, 237, 108, 231, 35, 198, 54, 61, 190, 255, 241, 225, 151, 180, 6, 163, 124, 27, 151, 78, 237, 65, 120, 106, 255, 236 ]; assert_eq(ciphertext, expected_header_ciphertext); diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr index d6a86136c9e..81fb2d1848b 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr @@ -133,7 +133,7 @@ mod test { let ciphertext = body.compute_ciphertext(eph_sk, ivpk); let expected_note_body_ciphertext = [ - 228, 9, 65, 81, 62, 59, 249, 207, 90, 196, 206, 72, 39, 199, 82, 196, 63, 127, 188, 251, 150, 188, 238, 205, 3, 86, 102, 164, 175, 12, 137, 158, 163, 111, 205, 10, 229, 230, 46, 202, 110, 107, 156, 180, 67, 192, 161, 201, 48, 153, 169, 1, 25, 182, 93, 39, 39, 207, 251, 218, 234, 147, 156, 13, 110, 180, 190, 199, 41, 6, 211, 203, 176, 110, 165, 186, 110, 127, 199, 22, 201, 149, 92, 249, 219, 68, 145, 68, 179, 29, 233, 34, 98, 123, 197, 234, 169, 53, 44, 14, 81, 60, 92, 27, 250, 134, 49, 248, 57, 119, 236, 118, 158, 104, 82, 243, 98, 164, 60, 72, 74, 27, 177, 194, 221, 225, 193, 150, 67, 235, 205, 106, 150, 24, 126, 186, 220, 178, 199, 189, 113, 54, 181, 55, 46, 15, 236, 236, 9, 159, 5, 172, 237, 154, 110, 50, 241, 64, 92, 13, 37, 53, 20, 140, 42, 146, 229, 63, 97, 25, 159, 63, 235, 104, 68, 100 + 166, 212, 106, 246, 139, 59, 228, 9, 133, 152, 127, 172, 141, 166, 237, 199, 195, 85, 255, 81, 66, 72, 192, 192, 96, 10, 54, 139, 136, 153, 252, 114, 248, 128, 253, 66, 249, 16, 71, 45, 2, 213, 250, 193, 241, 75, 90, 70, 39, 26, 104, 139, 20, 45, 1, 1, 166, 72, 133, 55, 247, 142, 150, 215, 217, 224, 84, 23, 245, 71, 207, 166, 136, 34, 221, 76, 90, 166, 44, 217, 246, 98, 157, 34, 198, 164, 99, 117, 15, 185, 145, 231, 189, 140, 201, 241, 135, 94, 71, 131, 156, 86, 144, 131, 248, 242, 83, 101, 18, 189, 1, 94, 25, 238, 76, 106, 85, 205, 4, 70, 21, 9, 64, 63, 27, 164, 73, 181, 75, 199, 86, 255, 105, 239, 216, 34, 217, 184, 154, 76, 67, 1, 210, 251, 23, 185, 114, 146, 195, 28, 76, 219, 150, 175, 37, 76, 144, 227, 99, 243, 123, 161, 66, 171, 148, 181, 162, 2, 196, 53, 207, 154, 114, 166, 155, 166 ]; assert_eq(expected_note_body_ciphertext.len(), ciphertext.len()); @@ -235,7 +235,7 @@ mod test { let ciphertext = body.compute_ciphertext(eph_sk, ivpk); let expected_event_body_ciphertext = [ - 228, 9, 65, 81, 62, 59, 249, 207, 90, 196, 206, 72, 39, 199, 82, 196, 63, 127, 188, 251, 150, 188, 238, 205, 3, 86, 102, 164, 175, 12, 137, 158, 163, 111, 205, 10, 229, 230, 46, 202, 110, 107, 156, 180, 67, 192, 161, 201, 66, 122, 29, 35, 42, 33, 153, 216, 199, 208, 103, 207, 126, 153, 189, 136, 19, 220, 238, 15, 169, 29, 255, 11, 123, 107, 70, 192, 53, 40, 36, 93, 187, 32, 123, 136, 104, 23, 229, 245, 152, 90, 84, 2, 136, 112, 42, 27, 82, 214, 104, 14, 250, 48, 199, 245, 88, 22, 200, 77, 38, 51, 127, 56, 138, 255, 16, 46, 179, 129, 215, 185, 185, 116, 148, 16, 133, 62, 56, 180, 10, 132, 109, 77, 206, 199, 21, 167, 7, 163, 171, 158, 244, 23, 18, 121, 108, 42, 107, 7, 48, 84, 212, 104, 39, 16, 109, 7, 108, 129, 60, 80, 112, 241, 223, 140, 186, 158, 38, 74, 230, 213, 159, 175, 142, 228, 128, 160 + 166, 212, 106, 246, 139, 59, 228, 9, 133, 152, 127, 172, 141, 166, 237, 199, 195, 85, 255, 81, 66, 72, 192, 192, 96, 10, 54, 139, 136, 153, 252, 114, 248, 128, 253, 66, 249, 16, 71, 45, 2, 213, 250, 193, 241, 75, 90, 70, 19, 153, 62, 117, 71, 55, 48, 114, 160, 232, 97, 118, 93, 53, 145, 92, 0, 225, 51, 81, 156, 69, 72, 224, 10, 89, 32, 121, 167, 197, 84, 245, 188, 235, 143, 202, 179, 197, 164, 121, 11, 105, 116, 239, 46, 222, 50, 138, 112, 237, 97, 8, 176, 199, 1, 151, 89, 218, 60, 45, 91, 85, 16, 38, 195, 127, 157, 182, 0, 10, 232, 184, 148, 76, 244, 63, 40, 222, 219, 139, 236, 169, 213, 17, 32, 210, 50, 6, 5, 83, 80, 1, 111, 246, 197, 83, 166, 71, 31, 246, 234, 75, 12, 151, 227, 247, 143, 229, 95, 219, 159, 75, 174, 232, 64, 7, 102, 76, 207, 45, 143, 208, 101, 113, 175, 37, 83, 166 ]; assert_eq(expected_event_body_ciphertext.len(), ciphertext.len()); diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr index 64379d6f5bc..59b6268a816 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr @@ -4,7 +4,7 @@ use dep::protocol_types::{ }; use std::aes128::aes128_encrypt; -use crate::keys::point_to_symmetric_key::point_to_symmetric_key; +use crate::{keys::point_to_symmetric_key::point_to_symmetric_key, utils::point::point_to_bytes}; struct EncryptedLogOutgoingBody { eph_sk: Scalar, @@ -17,26 +17,25 @@ impl EncryptedLogOutgoingBody { Self { eph_sk, recipient, recipient_ivpk } } - pub fn compute_ciphertext(self, ovsk_app: Scalar, eph_pk: Point) -> [u8; 176] { + pub fn compute_ciphertext(self, ovsk_app: Scalar, eph_pk: Point) -> [u8; 144] { // Again, we could compute `eph_pk` here, but we keep the interface more similar // and also make it easier to optimise it later as we just pass it along - let mut buffer: [u8; 160] = [0; 160]; + let mut buffer = [0 as u8; 128]; let serialized_eph_sk_high = self.eph_sk.hi.to_be_bytes(32); let serialized_eph_sk_low = self.eph_sk.lo.to_be_bytes(32); let address_bytes = self.recipient.to_field().to_be_bytes(32); - let serialized_recipient_ivpk = self.recipient_ivpk.serialize(); - let serialized_recipient_ivpk_x = serialized_recipient_ivpk[0].to_be_bytes(32); - let serialized_recipient_ivpk_y = serialized_recipient_ivpk[1].to_be_bytes(32); + let serialized_recipient_ivpk = point_to_bytes(self.recipient_ivpk); for i in 0..32 { buffer[i] = serialized_eph_sk_high[i]; buffer[i + 32] = serialized_eph_sk_low[i]; buffer[i + 64] = address_bytes[i]; - buffer[i + 96] = serialized_recipient_ivpk_x[i]; - buffer[i + 128] = serialized_recipient_ivpk_y[i]; + } + for i in 0..32 { + buffer[i + 96] = serialized_recipient_ivpk[i]; } // We compute the symmetric key using poseidon. @@ -93,7 +92,7 @@ mod test { let ciphertext = body.compute_ciphertext(sender_ovsk_app, eph_pk); let expected_outgoing_body_ciphertext = [ - 127, 84, 96, 176, 101, 107, 236, 57, 68, 8, 53, 202, 138, 74, 186, 54, 74, 193, 245, 7, 109, 59, 218, 33, 1, 31, 205, 225, 241, 209, 64, 222, 94, 245, 4, 150, 47, 241, 187, 64, 152, 20, 102, 158, 200, 217, 213, 82, 1, 240, 170, 185, 51, 80, 27, 109, 63, 231, 235, 120, 174, 44, 133, 248, 10, 97, 60, 40, 222, 190, 147, 76, 187, 48, 91, 206, 48, 106, 56, 118, 38, 127, 82, 4, 182, 188, 44, 224, 31, 129, 47, 107, 134, 252, 20, 25, 122, 191, 158, 69, 35, 255, 215, 171, 196, 45, 91, 184, 83, 80, 238, 201, 1, 233, 235, 159, 171, 130, 158, 64, 176, 165, 132, 30, 84, 81, 71, 195, 145, 47, 82, 247, 210, 192, 23, 4, 220, 90, 56, 109, 46, 105, 79, 251, 165, 141, 185, 233, 191, 118, 219, 153, 191, 162, 99, 238, 241, 249, 9, 74, 210, 241, 54, 28, 126, 226, 85, 235, 174, 75, 239, 207, 100, 184, 248, 194 + 127, 84, 96, 176, 101, 107, 236, 57, 68, 8, 53, 202, 138, 74, 186, 54, 74, 193, 245, 7, 109, 59, 218, 33, 1, 31, 205, 225, 241, 209, 64, 222, 94, 245, 4, 150, 47, 241, 187, 64, 152, 20, 102, 158, 200, 217, 213, 82, 1, 240, 170, 185, 51, 80, 27, 109, 63, 231, 235, 120, 174, 44, 133, 248, 10, 97, 60, 40, 222, 190, 147, 76, 187, 48, 91, 206, 48, 106, 56, 118, 38, 127, 82, 4, 182, 188, 44, 224, 31, 129, 47, 107, 134, 252, 20, 25, 249, 193, 215, 137, 195, 43, 98, 42, 54, 96, 254, 89, 134, 31, 103, 142, 16, 43, 92, 211, 145, 113, 217, 253, 161, 240, 121, 205, 146, 200, 168, 160, 221, 32, 229, 116, 26, 216, 86, 189, 78, 120, 10, 224, 85, 52, 40, 244 ]; for i in 0..expected_outgoing_body_ciphertext.len() { diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr index de297b664f4..9e9f629ad8f 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr @@ -6,7 +6,7 @@ use std::embedded_curve_ops::fixed_base_scalar_mul as derive_public_key; use std::field::bytes32_to_field; use crate::oracle::unsafe_rand::unsafe_rand; -use crate::utils::point::pub_key_to_bytes; +use crate::utils::point::point_to_bytes; use crate::event::event_interface::EventInterface; use crate::note::note_interface::NoteInterface; @@ -32,38 +32,38 @@ pub fn compute_encrypted_event_log( 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 outgoing_header_ciphertext: [u8; 48] = header.compute_ciphertext(eph_sk, ovpk); let incoming_body_ciphertext = EncryptedLogIncomingBody::from_event(event, randomness).compute_ciphertext(eph_sk, ivpk); - let outgoing_body_ciphertext: [u8; 176] = EncryptedLogOutgoingBody::new(eph_sk, recipient, ivpk).compute_ciphertext(fr_to_fq(ovsk_app), eph_pk); + let outgoing_body_ciphertext: [u8; 144] = EncryptedLogOutgoingBody::new(eph_sk, recipient, ivpk).compute_ciphertext(fr_to_fq(ovsk_app), eph_pk); let mut encrypted_bytes: [u8; OB] = [0; OB]; // @todo We ignore the tags for now - let eph_pk_bytes = pub_key_to_bytes(eph_pk); - for i in 0..64 { + let eph_pk_bytes = point_to_bytes(eph_pk); + for i in 0..32 { 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]; + encrypted_bytes[96 + i] = incoming_header_ciphertext[i]; + encrypted_bytes[144 + i] = outgoing_header_ciphertext[i]; } - for i in 0..176 { - encrypted_bytes[224 + i] = outgoing_body_ciphertext[i]; + for i in 0..144 { + encrypted_bytes[192 + i] = outgoing_body_ciphertext[i]; } // Then we fill in the rest as the incoming body ciphertext - let size = OB - 400; + let size = OB - 336; 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[336 + i] = incoming_body_ciphertext[i]; } // Current unoptimized size of the encrypted log // incoming_tag (32 bytes) // outgoing_tag (32 bytes) - // eph_pk (64 bytes) + // eph_pk (32 bytes) // incoming_header (48 bytes) // outgoing_header (48 bytes) - // outgoing_body (176 bytes) + // outgoing_body (144 bytes) // incoming_body_fixed (64 bytes) // incoming_body_variable (N * 32 bytes + 16 bytes padding) encrypted_bytes @@ -85,38 +85,38 @@ pub fn compute_encrypted_note_log( 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 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); - let outgoing_body_ciphertext: [u8; 176] = EncryptedLogOutgoingBody::new(eph_sk, recipient, ivpk).compute_ciphertext(fr_to_fq(ovsk_app), eph_pk); + let outgoing_body_ciphertext: [u8; 144] = EncryptedLogOutgoingBody::new(eph_sk, recipient, ivpk).compute_ciphertext(fr_to_fq(ovsk_app), eph_pk); let mut encrypted_bytes: [u8; M] = [0; M]; // @todo We ignore the tags for now - let eph_pk_bytes = pub_key_to_bytes(eph_pk); - for i in 0..64 { + let eph_pk_bytes = point_to_bytes(eph_pk); + for i in 0..32 { 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]; + encrypted_bytes[96 + i] = incoming_header_ciphertext[i]; + encrypted_bytes[144 + i] = outgoing_header_ciphertext[i]; } - for i in 0..176 { - encrypted_bytes[224 + i] = outgoing_body_ciphertext[i]; + for i in 0..144 { + encrypted_bytes[192 + i] = outgoing_body_ciphertext[i]; } // Then we fill in the rest as the incoming body ciphertext - let size = M - 400; + let size = M - 336; 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[336 + i] = incoming_body_ciphertext[i]; } // Current unoptimized size of the encrypted log // incoming_tag (32 bytes) // outgoing_tag (32 bytes) - // eph_pk (64 bytes) + // eph_pk (32 bytes) // incoming_header (48 bytes) // outgoing_header (48 bytes) - // outgoing_body (176 bytes) + // outgoing_body (144 bytes) // incoming_body_fixed (64 bytes) // incoming_body_variable (N * 32 bytes + 16 bytes padding) encrypted_bytes @@ -142,10 +142,7 @@ fn fr_to_fq(r: Field) -> Scalar { } mod test { - use crate::{ - encrypted_logs::payload::compute_encrypted_note_log, - test::mocks::mock_note::{MockNoteBuilder, MockNote} - }; + use crate::{encrypted_logs::payload::compute_encrypted_note_log, test::mocks::mock_note::MockNoteBuilder}; use dep::protocol_types::{address::AztecAddress, point::Point}; use std::test::OracleMock; @@ -173,7 +170,7 @@ mod test { let recipient = AztecAddress::from_field(0x10ee41ee4b62703b16f61e03cb0d88c4b306a9eb4a6ceeb2aff13428541689a2); - let log: [u8; 512] = compute_encrypted_note_log( + let log: [u8; 448] = compute_encrypted_note_log( contract_address, storage_slot, ovsk_app, @@ -184,7 +181,7 @@ mod test { ); let expected_encrypted_note_log = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 70, 12, 14, 67, 77, 132, 110, 193, 234, 40, 110, 64, 144, 235, 86, 55, 111, 242, 123, 221, 193, 170, 202, 225, 216, 86, 84, 159, 112, 31, 167, 17, 160, 52, 217, 22, 191, 84, 175, 25, 133, 53, 220, 2, 251, 32, 105, 198, 147, 24, 131, 202, 112, 149, 136, 66, 205, 254, 3, 134, 195, 101, 73, 212, 19, 232, 42, 39, 191, 165, 183, 8, 7, 18, 118, 74, 69, 91, 146, 69, 16, 184, 101, 144, 48, 25, 190, 254, 181, 223, 24, 183, 175, 118, 159, 176, 135, 62, 255, 169, 124, 170, 3, 92, 81, 122, 107, 65, 125, 95, 97, 110, 198, 200, 74, 147, 217, 93, 23, 227, 84, 59, 15, 75, 108, 122, 49, 230, 228, 246, 207, 173, 7, 60, 16, 74, 236, 201, 102, 237, 48, 179, 223, 191, 223, 248, 78, 167, 61, 203, 25, 114, 223, 58, 60, 180, 255, 116, 170, 136, 173, 178, 40, 2, 125, 229, 20, 220, 82, 28, 191, 147, 133, 137, 1, 45, 243, 229, 140, 115, 165, 150, 154, 96, 22, 120, 223, 237, 213, 182, 252, 192, 8, 132, 43, 21, 56, 243, 116, 144, 182, 75, 16, 30, 222, 222, 60, 205, 147, 214, 53, 41, 62, 53, 16, 147, 117, 72, 169, 220, 125, 208, 210, 45, 65, 233, 40, 87, 88, 140, 237, 200, 161, 9, 86, 82, 128, 191, 51, 4, 195, 243, 100, 102, 240, 54, 129, 176, 116, 139, 73, 27, 98, 222, 1, 117, 99, 178, 51, 207, 67, 29, 83, 104, 231, 49, 137, 217, 247, 111, 172, 245, 198, 171, 123, 57, 41, 203, 219, 177, 135, 227, 2, 189, 205, 150, 238, 81, 1, 202, 202, 244, 139, 194, 123, 195, 148, 255, 169, 226, 43, 234, 31, 252, 105, 35, 2, 91, 12, 19, 26, 134, 114, 184, 210, 92, 191, 188, 7, 172, 232, 163, 189, 38, 199, 56, 251, 225, 202, 249, 17, 117, 132, 162, 113, 61, 75, 246, 144, 94, 99, 132, 235, 149, 93, 3, 115, 131, 132, 250, 238, 138, 194, 233, 144, 156, 140, 1, 42, 44, 12, 214, 94, 137, 130, 56, 105, 149, 124, 81, 178, 1, 73, 79, 156, 26, 65, 163, 18, 152, 116, 138, 128, 158, 63, 151, 205, 151, 73, 68, 173, 220, 126, 213, 72, 112, 237, 15, 235, 178, 217, 122, 146, 224, 89, 213, 217, 34, 172, 4, 164, 40, 102, 220, 174, 222, 250, 221, 149, 238, 174, 97, 65, 183, 255, 168, 139, 67, 122, 124, 41, 89, 147, 255, 109, 57, 197, 150, 174, 186, 221, 50, 19, 216, 14, 100, 176 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 70, 12, 14, 67, 77, 132, 110, 193, 234, 40, 110, 64, 144, 235, 86, 55, 111, 242, 123, 221, 193, 170, 202, 225, 216, 86, 84, 159, 112, 31, 167, 159, 53, 114, 117, 237, 57, 131, 19, 111, 150, 50, 83, 173, 155, 234, 225, 71, 187, 141, 79, 245, 43, 111, 83, 219, 149, 124, 68, 12, 244, 253, 216, 0, 62, 108, 232, 118, 80, 87, 140, 215, 185, 111, 48, 128, 236, 110, 92, 46, 205, 7, 226, 131, 66, 205, 0, 103, 83, 217, 90, 60, 138, 6, 172, 246, 129, 92, 172, 69, 73, 77, 65, 147, 18, 231, 20, 35, 217, 180, 253, 72, 242, 32, 57, 45, 11, 2, 235, 24, 96, 244, 224, 33, 61, 151, 225, 136, 173, 178, 40, 2, 125, 229, 20, 220, 82, 28, 191, 147, 133, 137, 1, 45, 243, 229, 140, 115, 165, 150, 154, 96, 22, 120, 223, 237, 213, 182, 252, 192, 8, 132, 43, 21, 56, 243, 116, 144, 182, 75, 16, 30, 222, 222, 60, 205, 147, 214, 53, 41, 62, 53, 16, 147, 117, 72, 169, 220, 125, 208, 210, 45, 65, 233, 40, 87, 88, 140, 237, 200, 161, 9, 86, 82, 128, 191, 51, 4, 195, 243, 100, 102, 240, 54, 129, 176, 116, 139, 73, 27, 98, 222, 1, 243, 199, 72, 238, 213, 66, 91, 159, 183, 143, 36, 103, 94, 5, 62, 50, 13, 217, 161, 79, 30, 231, 41, 228, 109, 139, 243, 119, 166, 54, 37, 250, 193, 6, 67, 29, 148, 185, 153, 58, 64, 210, 164, 219, 165, 80, 35, 75, 109, 177, 14, 168, 136, 105, 21, 235, 62, 159, 71, 61, 245, 193, 234, 169, 100, 165, 8, 222, 157, 239, 41, 221, 223, 67, 80, 61, 252, 54, 27, 100, 1, 104, 2, 121, 62, 41, 23, 132, 15, 124, 120, 21, 198, 113, 151, 172, 42, 161, 64, 240, 166, 205, 80, 169, 58, 191, 111, 130, 55, 58, 141, 26, 97, 118, 114, 216, 69, 207, 212, 227, 250, 199, 21, 72, 144, 85, 43, 76, 213, 28, 132, 134, 16, 221, 105, 112, 82, 238, 114, 61, 36, 144, 179, 178, 68, 198, 162, 212, 85, 100, 116, 186, 131, 232, 33, 229, 101, 251, 5, 251 ]; for i in 0..expected_encrypted_note_log.len() { assert_eq(log[i], expected_encrypted_note_log[i]); diff --git a/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr b/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr index f6ac8cc5ca6..f84dd3fe861 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr @@ -1,5 +1,5 @@ use dep::protocol_types::{constants::GENERATOR_INDEX__SYMMETRIC_KEY, scalar::Scalar, point::Point, utils::arr_copy_slice}; -use crate::utils::point::pub_key_to_bytes; +use crate::utils::point::point_to_bytes; use std::{hash::sha256, embedded_curve_ops::multi_scalar_mul}; // TODO(#5726): This function is called deriveAESSecret in TS. I don't like point_to_symmetric_key name much since @@ -7,10 +7,10 @@ use std::{hash::sha256, embedded_curve_ops::multi_scalar_mul}; pub fn point_to_symmetric_key(secret: Scalar, point: Point) -> [u8; 32] { let shared_secret: Point = multi_scalar_mul([point], [secret]); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/6061): make the func return Point struct directly - let shared_secret = pub_key_to_bytes(shared_secret); - let mut shared_secret_bytes_with_separator = [0 as u8; 65]; + let shared_secret = point_to_bytes(shared_secret); + let mut shared_secret_bytes_with_separator = [0 as u8; 33]; shared_secret_bytes_with_separator = arr_copy_slice(shared_secret, shared_secret_bytes_with_separator, 0); - shared_secret_bytes_with_separator[64] = GENERATOR_INDEX__SYMMETRIC_KEY; + shared_secret_bytes_with_separator[32] = GENERATOR_INDEX__SYMMETRIC_KEY; sha256(shared_secret_bytes_with_separator) } @@ -30,7 +30,7 @@ fn check_point_to_symmetric_key() { let key = point_to_symmetric_key(secret, point); // The following value gets updated when running encrypt_buffer.test.ts with AZTEC_GENERATE_TEST_DATA=1 let expected_key = [ - 49, 167, 146, 222, 151, 129, 138, 184, 87, 210, 245, 249, 99, 100, 1, 59, 223, 180, 5, 99, 14, 7, 177, 236, 159, 203, 231, 72, 220, 180, 241, 23 + 217, 245, 196, 116, 55, 39, 202, 184, 117, 231, 19, 56, 102, 254, 94, 14, 172, 169, 123, 96, 61, 247, 209, 140, 4, 132, 119, 222, 79, 1, 154, 136 ]; assert_eq(key, expected_key); } diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr index b874d949afd..d1f916e329f 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr @@ -1,7 +1,7 @@ use dep::protocol_types::address::AztecAddress; -// TODO: this is awful but since we can't have a fn that maps [Field; N] -> [u8; 480 + N * 32] -// (where N is the note pre-image size and 480 + N * 32 is the encryption output size) +// TODO: this is awful but since we can't have a fn that maps [Field; N] -> [u8; 416 + N * 32] +// (where N is the note pre-image size and 416 + N * 32 is the encryption output size) // The fns for LensForEncryptedLog are never used, it's just to tell the compiler what the lens are // The to_bytes fn for ToBytesForUnencryptedLog is used to allow us to hash some generic T @@ -11,84 +11,84 @@ use dep::protocol_types::address::AztecAddress; // anything, so we can remove this trait trait LensForEncryptedLog { // N = note preimage input in fields - // M = encryption output len in bytes (= 480 + N * 32) + // M = encryption output len in bytes (= 416 + N * 32) fn output_fields(self: [Field; N]) -> [Field; N]; fn output_bytes(self: [Field; N]) -> [u8; M]; } -impl LensForEncryptedLog<1, 512> for [Field; 1] { +impl LensForEncryptedLog<1, 448> for [Field; 1] { fn output_fields(self) -> [Field; 1] {[self[0]; 1]} - fn output_bytes(self) -> [u8; 512] {[self[0] as u8; 512]} + fn output_bytes(self) -> [u8; 448] {[self[0] as u8; 448]} } -impl LensForEncryptedLog<2, 544> for [Field; 2] { +impl LensForEncryptedLog<2, 480> for [Field; 2] { fn output_fields(self) -> [Field; 2] {[self[0]; 2]} - fn output_bytes(self) -> [u8; 544] {[self[0] as u8; 544]} + fn output_bytes(self) -> [u8; 480] {[self[0] as u8; 480]} } -impl LensForEncryptedLog<3, 576> for [Field; 3] { +impl LensForEncryptedLog<3, 512> for [Field; 3] { fn output_fields(self) -> [Field; 3] {[self[0]; 3]} - fn output_bytes(self) -> [u8; 576] {[self[0] as u8; 576]} + fn output_bytes(self) -> [u8; 512] {[self[0] as u8; 512]} } -impl LensForEncryptedLog<4, 608> for [Field; 4] { +impl LensForEncryptedLog<4, 544> for [Field; 4] { fn output_fields(self) -> [Field; 4] {[self[0]; 4]} - fn output_bytes(self) -> [u8; 608] {[self[0] as u8; 608]} + fn output_bytes(self) -> [u8; 544] {[self[0] as u8; 544]} } -impl LensForEncryptedLog<5, 640> for [Field; 5] { +impl LensForEncryptedLog<5, 576> for [Field; 5] { fn output_fields(self) -> [Field; 5] {[self[0]; 5]} - fn output_bytes(self) -> [u8; 640] {[self[0] as u8; 640]} + fn output_bytes(self) -> [u8; 576] {[self[0] as u8; 576]} } -impl LensForEncryptedLog<6, 672> for [Field; 6] { +impl LensForEncryptedLog<6, 608> for [Field; 6] { fn output_fields(self) -> [Field; 6] {[self[0]; 6]} - fn output_bytes(self) -> [u8; 672] {[self[0] as u8; 672]} + fn output_bytes(self) -> [u8; 608] {[self[0] as u8; 608]} } -impl LensForEncryptedLog<7, 704> for [Field; 7] { +impl LensForEncryptedLog<7, 640> for [Field; 7] { fn output_fields(self) -> [Field; 7] {[self[0]; 7]} - fn output_bytes(self) -> [u8; 704] {[self[0] as u8; 704]} + fn output_bytes(self) -> [u8; 640] {[self[0] as u8; 640]} } -impl LensForEncryptedLog<8, 736> for [Field; 8] { +impl LensForEncryptedLog<8, 672> for [Field; 8] { fn output_fields(self) -> [Field; 8] {[self[0]; 8]} - fn output_bytes(self) -> [u8; 736] {[self[0] as u8; 736]} + fn output_bytes(self) -> [u8; 672] {[self[0] as u8; 672]} } -impl LensForEncryptedLog<9, 768> for [Field; 9] { +impl LensForEncryptedLog<9, 704> for [Field; 9] { fn output_fields(self) -> [Field; 9] {[self[0]; 9]} - fn output_bytes(self) -> [u8; 768] {[self[0] as u8; 768]} + fn output_bytes(self) -> [u8; 704] {[self[0] as u8; 704]} } -impl LensForEncryptedLog<10, 800> for [Field; 10] { +impl LensForEncryptedLog<10, 736> for [Field; 10] { fn output_fields(self) -> [Field; 10] {[self[0]; 10]} - fn output_bytes(self) -> [u8; 800] {[self[0] as u8; 800]} + fn output_bytes(self) -> [u8; 736] {[self[0] as u8; 736]} } -impl LensForEncryptedLog<11, 832> for [Field; 11] { +impl LensForEncryptedLog<11, 768> for [Field; 11] { fn output_fields(self) -> [Field; 11] {[self[0]; 11]} - fn output_bytes(self) -> [u8; 832] {[self[0] as u8; 832]} + fn output_bytes(self) -> [u8; 768] {[self[0] as u8; 768]} } -impl LensForEncryptedLog<12, 864> for [Field; 12] { +impl LensForEncryptedLog<12, 800> for [Field; 12] { fn output_fields(self) -> [Field; 12] {[self[0]; 12]} - fn output_bytes(self) -> [u8; 864] {[self[0] as u8; 864]} + fn output_bytes(self) -> [u8; 800] {[self[0] as u8; 800]} } trait LensForEncryptedEvent { // N = event preimage input in bytes - // M = encryption output len in bytes (= 480 + M) + // M = encryption output len in bytes (= 416 + M) fn output(self: [u8; N]) -> [u8; M]; } -impl LensForEncryptedEvent<96, 512> for [u8; 96] { +impl LensForEncryptedEvent<96, 448> for [u8; 96] { + fn output(self) -> [u8; 448] {[self[0] as u8; 448]} +} +impl LensForEncryptedEvent<128, 480> for [u8; 128] { + fn output(self) -> [u8; 480] {[self[0] as u8; 480]} +} +impl LensForEncryptedEvent<160, 512> for [u8; 160] { fn output(self) -> [u8; 512] {[self[0] as u8; 512]} } -impl LensForEncryptedEvent<128, 544> for [u8; 128] { +impl LensForEncryptedEvent<192, 544> for [u8; 192] { fn output(self) -> [u8; 544] {[self[0] as u8; 544]} } -impl LensForEncryptedEvent<160, 576> for [u8; 160] { +impl LensForEncryptedEvent<224, 576> for [u8; 224] { fn output(self) -> [u8; 576] {[self[0] as u8; 576]} } -impl LensForEncryptedEvent<192, 608> for [u8; 192] { +impl LensForEncryptedEvent<256, 608> for [u8; 256] { fn output(self) -> [u8; 608] {[self[0] as u8; 608]} } -impl LensForEncryptedEvent<224, 640> for [u8; 224] { - fn output(self) -> [u8; 640] {[self[0] as u8; 640]} -} -impl LensForEncryptedEvent<256, 672> for [u8; 256] { - fn output(self) -> [u8; 672] {[self[0] as u8; 672]} -} // This trait defines the length of the inputs in bytes to // the unencrypted log hash fn, where the log can be any type T diff --git a/noir-projects/aztec-nr/aztec/src/utils/point.nr b/noir-projects/aztec-nr/aztec/src/utils/point.nr index e490f685f6b..c2d0d2635d5 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/point.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/point.nr @@ -1,17 +1,72 @@ use dep::protocol_types::point::Point; +// I am storing the modulus divided by 2 plus 1 here because full modulus would throw "String literal too large" error +// Full modulus is 21888242871839275222246405745257275088548364400416034343698204186575808495617 +global BN254_FR_MODULUS_DIV_2: Field = 10944121435919637611123202872628637544274182200208017171849102093287904247808; + /// Converts a public key to a byte array. /// /// We don't serialize the point at infinity flag because this function is used in situations where we do not want /// to waste the extra byte (encrypted log). -pub fn pub_key_to_bytes(pk: Point) -> [u8; 64] { - assert(!pk.is_infinite, "Point at infinity is not a valid public key."); - let mut result = [0 as u8; 64]; - let x_bytes = pk.x.to_be_bytes(32); - let y_bytes = pk.y.to_be_bytes(32); - for i in 0..32 { - result[i] = x_bytes[i]; - result[i + 32] = y_bytes[i]; +pub fn point_to_bytes(pk: Point) -> [u8; 32] { + assert(!pk.is_infinite, "Cannot serialize point at infinity as bytes."); + + let mut result = pk.x.to_be_bytes(32); + + // We store only a "sign" of the y coordinate because the rest can be derived from the x coordinate. To get + // the sign we check if the y coordinate is greater than the curve's order minus 1 divided by 2. + if !BN254_FR_MODULUS_DIV_2.lt(pk.y) { + // y is <= (modulus - 1) / 2 so we set the sign bit to 1 + // Here we leverage that field fits into 254 bits (log2(Fr.MODULUS) < 254) and given that we serialize Fr to 32 + // bytes and we use big-endian the 2 most significant bits are never populated. Hence we can use one of + // the bits as a sign bit. + result[0] += 128; + } + + result.as_array() +} + +mod test { + use dep::protocol_types::point::Point; + use crate::utils::point::point_to_bytes; + + #[test] + fn test_point_to_bytes_positive_sign() { + let p = Point { + x: 0x1af41f5de96446dc3776a1eb2d98bb956b7acd9979a67854bec6fa7c2973bd73, + y: 0x07fc22c7f2c7057571f137fe46ea9c95114282bc95d37d71ec4bfb88de457d4a, + is_infinite: false + }; + + let compressed_point = point_to_bytes(p); + + let expected_compressed_point_positive_sign = [ + 154, 244, 31, 93, 233, 100, 70, 220, 55, 118, 161, 235, 45, 152, 187, 149, 107, 122, 205, 153, 121, 166, 120, 84, 190, 198, 250, 124, 41, 115, 189, 115 + ]; + + assert_eq(expected_compressed_point_positive_sign.len(), compressed_point.len()); + for i in 0..expected_compressed_point_positive_sign.len() { + assert_eq(compressed_point[i], expected_compressed_point_positive_sign[i]); + } + } + + #[test] + fn test_point_to_bytes_negative_sign() { + let p = Point { + x: 0x247371652e55dd74c9af8dbe9fb44931ba29a9229994384bd7077796c14ee2b5, + y: 0x26441aec112e1ae4cee374f42556932001507ad46e255ffb27369c7e3766e5c0, + is_infinite: false + }; + + let compressed_point = point_to_bytes(p); + + let expected_compressed_point_negative_sign = [ + 36, 115, 113, 101, 46, 85, 221, 116, 201, 175, 141, 190, 159, 180, 73, 49, 186, 41, 169, 34, 153, 148, 56, 75, 215, 7, 119, 150, 193, 78, 226, 181 + ]; + + assert_eq(expected_compressed_point_negative_sign.len(), compressed_point.len()); + for i in 0..expected_compressed_point_negative_sign.len() { + assert_eq(compressed_point[i], expected_compressed_point_negative_sign[i]); + } } - result } 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 948dbd27896..e58e9ca0ba4 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -429,7 +429,7 @@ contract Test { recipient: AztecAddress, recipient_ivpk: Point, ovsk_app: Scalar - ) -> [u8; 176] { + ) -> [u8; 144] { let eph_pk = derive_public_key(eph_sk); EncryptedLogOutgoingBody::new(eph_sk, recipient, recipient_ivpk).compute_ciphertext(ovsk_app, eph_pk) } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/point.nr b/noir-projects/noir-protocol-circuits/crates/types/src/point.nr index 766f8dccae2..5de5cc7ec03 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/point.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/point.nr @@ -26,4 +26,3 @@ impl Empty for Point { } } } - diff --git a/yarn-project/circuit-types/src/logs/encrypted_l2_log.ts b/yarn-project/circuit-types/src/logs/encrypted_l2_log.ts index e1ac39f000b..dd866306c99 100644 --- a/yarn-project/circuit-types/src/logs/encrypted_l2_log.ts +++ b/yarn-project/circuit-types/src/logs/encrypted_l2_log.ts @@ -65,8 +65,8 @@ export class EncryptedL2Log { */ public static random(): EncryptedL2Log { const randomEphPubKey = Point.random(); - const randomLogContent = randomBytes(144 - Point.SIZE_IN_BYTES); - const data = Buffer.concat([Fr.random().toBuffer(), randomLogContent, randomEphPubKey.toBuffer()]); + const randomLogContent = randomBytes(144 - Point.COMPRESSED_SIZE_IN_BYTES); + const data = Buffer.concat([Fr.random().toBuffer(), randomLogContent, randomEphPubKey.toCompressedBuffer()]); return new EncryptedL2Log(data, Fr.random()); } } diff --git a/yarn-project/circuit-types/src/logs/encrypted_l2_note_log.ts b/yarn-project/circuit-types/src/logs/encrypted_l2_note_log.ts index a84039e3829..3202155a858 100644 --- a/yarn-project/circuit-types/src/logs/encrypted_l2_note_log.ts +++ b/yarn-project/circuit-types/src/logs/encrypted_l2_note_log.ts @@ -62,8 +62,8 @@ export class EncryptedL2NoteLog { */ public static random(): EncryptedL2NoteLog { const randomEphPubKey = Point.random(); - const randomLogContent = randomBytes(144 - Point.SIZE_IN_BYTES); - const data = Buffer.concat([Fr.random().toBuffer(), randomLogContent, randomEphPubKey.toBuffer()]); + const randomLogContent = randomBytes(144 - Point.COMPRESSED_SIZE_IN_BYTES); + const data = Buffer.concat([Fr.random().toBuffer(), randomLogContent, randomEphPubKey.toCompressedBuffer()]); return new EncryptedL2NoteLog(data); } diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypt_buffer.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypt_buffer.ts index d19c2452390..10afdcf9deb 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypt_buffer.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypt_buffer.ts @@ -27,7 +27,7 @@ export function encryptBuffer(data: Buffer, ephSecretKey: GrumpkinScalar, incomi const ephPubKey = curve.mul(curve.generator(), ephSecretKey); // We encrypt eth pub key without the isInfinite flag because infinite point is not a valid pub key - return Buffer.concat([cipher.update(plaintext), cipher.final(), ephPubKey.toBuffer()]); + return Buffer.concat([cipher.update(plaintext), cipher.final(), ephPubKey.toCompressedBuffer()]); } /** @@ -38,14 +38,14 @@ export function encryptBuffer(data: Buffer, ephSecretKey: GrumpkinScalar, incomi */ export function decryptBuffer(data: Buffer, incomingViewingSecretKey: GrumpkinScalar): Buffer | undefined { // Extract the ephemeral public key from the end of the data - const ephPubKey = Point.fromBuffer(data.subarray(-Point.SIZE_IN_BYTES)); + const ephPubKey = Point.fromCompressedBuffer(data.subarray(-Point.COMPRESSED_SIZE_IN_BYTES)); // Derive the AES secret key using the secret key and the ephemeral public key const aesSecret = deriveAESSecret(incomingViewingSecretKey, ephPubKey); const aesKey = aesSecret.subarray(0, 16); const iv = aesSecret.subarray(16, 32); const cipher = createDecipheriv('aes-128-cbc', aesKey, iv); try { - const plaintext = Buffer.concat([cipher.update(data.subarray(0, -Point.SIZE_IN_BYTES)), cipher.final()]); + const plaintext = Buffer.concat([cipher.update(data.subarray(0, -Point.COMPRESSED_SIZE_IN_BYTES)), cipher.final()]); if (plaintext.subarray(0, 8).equals(iv.subarray(0, 8))) { return plaintext.subarray(8); } diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_outgoing_body.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_outgoing_body.ts index 4d0b48b8bb3..69f1593d23d 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_outgoing_body.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_outgoing_body.ts @@ -12,9 +12,9 @@ export class EncryptedLogOutgoingBody { * @returns The serialized log body */ public toBuffer(): Buffer { - // The serialization of Fq is [high, low] check `grumpkin_private_key.nr` + // The serialization of Fq is [high, low] check `outgoing_body.nr` const ephSkBytes = serializeToBuffer([this.ephSk.hi, this.ephSk.lo]); - return serializeToBuffer(ephSkBytes, this.recipient, this.recipientIvpk); + return serializeToBuffer(ephSkBytes, this.recipient, this.recipientIvpk.toCompressedBuffer()); } /** @@ -29,7 +29,7 @@ export class EncryptedLogOutgoingBody { const low = reader.readObject(Fr); const ephSk = GrumpkinScalar.fromHighLow(high, low); const recipient = reader.readObject(AztecAddress); - const recipientIvpk = reader.readObject(Point); // PublicKey = Point + const recipientIvpk = Point.fromCompressedBuffer(reader.readBytes(Point.COMPRESSED_SIZE_IN_BYTES)); // PublicKey = Point return new EncryptedLogOutgoingBody(ephSk, recipient, recipientIvpk); } diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encryption_utils.ts b/yarn-project/circuit-types/src/logs/l1_payload/encryption_utils.ts index 4feccb927b6..0367f87cb4c 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encryption_utils.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encryption_utils.ts @@ -24,7 +24,7 @@ export function deriveAESSecret(secretKey: GrumpkinScalar, publicKey: PublicKey) } const curve = new Grumpkin(); const sharedSecret = curve.mul(publicKey, secretKey); - const secretBuffer = Buffer.concat([sharedSecret.toBuffer(), numToUInt8(GeneratorIndex.SYMMETRIC_KEY)]); + const secretBuffer = Buffer.concat([sharedSecret.toCompressedBuffer(), numToUInt8(GeneratorIndex.SYMMETRIC_KEY)]); const hash = sha256(secretBuffer); return hash; } diff --git a/yarn-project/circuit-types/src/logs/l1_payload/l1_payload.ts b/yarn-project/circuit-types/src/logs/l1_payload/l1_payload.ts index 26def7fedd4..758b0379c9c 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/l1_payload.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/l1_payload.ts @@ -18,9 +18,9 @@ import { EncryptedLogOutgoingBody } from './encrypted_log_outgoing_body.js'; // 32 bytes for the address, and 16 bytes padding to follow PKCS#7 const HEADER_SIZE = 48; -// The outgoing body is constant size of 176 bytes. -// 160 bytes for the secret key, address, and public key, and 16 bytes padding to follow PKCS#7 -const OUTGOING_BODY_SIZE = 176; +// The outgoing body is constant size of 144 bytes. +// 128 bytes for the secret key, address and public key, and 16 bytes padding to follow PKCS#7 +const OUTGOING_BODY_SIZE = 144; /** * A class which wraps event data which is pushed on L1. */ @@ -70,7 +70,7 @@ export abstract class L1Payload { ); return Buffer.concat([ - ephPk.toBuffer(), + ephPk.toCompressedBuffer(), incomingHeaderCiphertext, outgoingHeaderCiphertext, outgoingBodyCiphertext, @@ -97,7 +97,7 @@ export abstract class L1Payload { ): [AztecAddress, T] { const reader = BufferReader.asReader(data); - const ephPk = reader.readObject(Point); + const ephPk = Point.fromCompressedBuffer(reader.readBytes(Point.COMPRESSED_SIZE_IN_BYTES)); const incomingHeader = EncryptedLogHeader.fromCiphertext(reader.readBytes(HEADER_SIZE), ivsk, ephPk); @@ -133,7 +133,7 @@ export abstract class L1Payload { ): [AztecAddress, T] { const reader = BufferReader.asReader(data); - const ephPk = reader.readObject(Point); + const ephPk = Point.fromCompressedBuffer(reader.readBytes(Point.COMPRESSED_SIZE_IN_BYTES)); reader.readBytes(HEADER_SIZE); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts index d2dc9f3f57a..af8397ff49d 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts @@ -82,7 +82,7 @@ describe('L1 Note Payload', () => { const encrypted = taggedLog.encrypt(ephSk, recipientAddress, ivpk, ovKeys).toString('hex'); expect(encrypted).toMatchInlineSnapshot( - `"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d460c0e434d846ec1ea286e4090eb56376ff27bddc1aacae1d856549f701fa711a034d916bf54af198535dc02fb2069c6931883ca70958842cdfe0386c36549d413e82a27bfa5b7080712764a455b924510b865903019befeb5df18b7af769fb0873effa97caa035c517a6b417d5f616ec6c84a93d95d17e3543b0f4b6c7a31e6e4f6cfad073c104aecc966ed30b3dfbfdff84ea73dcb1972df3a3cb4ff74aa88adb228027de514dc521cbf938589012df3e58c73a5969a601678dfedd5b6fcc008842b1538f37490b64b101edede3ccd93d635293e3510937548a9dc7dd0d22d41e92857588cedc8a109565280bf3304c3f36466f03681b0748b491b62de017563b233cf431d5368e73189d9f76facf5c6ab7b3929cbdbb187e302bdcd96ee5101cacaf48bc27bc394ffa9e22bea1ffc6923025b0c131a8672b8d25cbfbc07ace8a3bd26c738fbe1caf9117584a2713d4bf6905e6384eb955d03738384faee8ac2e9909c8c012a2c0cd65e89823869957c51b201494f9c1a41a31298748a809e3f97cd974944addc7ed54870ed0febb2d97a92e059d5d922ac04a42866dcaedefadd95eeae6141b7ffa88b437a7c295993ff6d39c596aebadd3213d80e64b0"`, + `"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d460c0e434d846ec1ea286e4090eb56376ff27bddc1aacae1d856549f701fa79f357275ed3983136f963253ad9beae147bb8d4ff52b6f53db957c440cf4fdd8003e6ce87650578cd7b96f3080ec6e5c2ecd07e28342cd006753d95a3c8a06acf6815cac45494d419312e71423d9b4fd48f220392d0b02eb1860f4e0213d97e188adb228027de514dc521cbf938589012df3e58c73a5969a601678dfedd5b6fcc008842b1538f37490b64b101edede3ccd93d635293e3510937548a9dc7dd0d22d41e92857588cedc8a109565280bf3304c3f36466f03681b0748b491b62de01f3c748eed5425b9fb78f24675e053e320dd9a14f1ee729e46d8bf377a63625fac106431d94b9993a40d2a4dba550234b6db10ea8886915eb3e9f473df5c1eaa964a508de9def29dddf43503dfc361b64016802793e2917840f7c7815c67197ac2aa140f0a6cd50a93abf6f82373a8d1a617672d845cfd4e3fac7154890552b4cd51c848610dd697052ee723d2490b3b244c6a2d4556474ba83e821e565fb05fb"`, ); const byteArrayString = `[${encrypted.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16))}]`; diff --git a/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.ts b/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.ts index 82cd805e9d3..70568c08ad6 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.ts @@ -1,5 +1,5 @@ import { AztecAddress, type GrumpkinScalar, type KeyValidationRequest, type PublicKey } from '@aztec/circuits.js'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, NotOnCurveError } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { type EncryptedL2Log } from '../encrypted_l2_log.js'; @@ -97,6 +97,7 @@ export class TaggedLog { } catch (e: any) { // Following error messages are expected to occur when decryption fails if ( + !(e instanceof NotOnCurveError) && !e.message.endsWith('is greater or equal to field modulus.') && !e.message.startsWith('Invalid AztecAddress length') && !e.message.startsWith('Selector must fit in') && @@ -142,6 +143,7 @@ export class TaggedLog { } catch (e: any) { // Following error messages are expected to occur when decryption fails if ( + !(e instanceof NotOnCurveError) && !e.message.endsWith('is greater or equal to field modulus.') && !e.message.startsWith('Invalid AztecAddress length') && !e.message.startsWith('Selector must fit in') && diff --git a/yarn-project/foundation/src/fields/point.test.ts b/yarn-project/foundation/src/fields/point.test.ts index 6fa64160b41..c95107f4dc1 100644 --- a/yarn-project/foundation/src/fields/point.test.ts +++ b/yarn-project/foundation/src/fields/point.test.ts @@ -1,3 +1,4 @@ +import { updateInlineTestData } from '../testing/test_data.js'; import { Fr } from './fields.js'; import { Point } from './point.js'; @@ -32,4 +33,46 @@ describe('Point', () => { expect(p.equals(p2)).toBeTruthy(); }); + + it('compressed point with + sign matches Noir', () => { + const p = new Point( + new Fr(0x1af41f5de96446dc3776a1eb2d98bb956b7acd9979a67854bec6fa7c2973bd73n), + new Fr(0x07fc22c7f2c7057571f137fe46ea9c95114282bc95d37d71ec4bfb88de457d4an), + false, + ); + expect(p.toXAndSign()[1]).toBe(true); + + const compressed = p.toCompressedBuffer().toString('hex'); + expect(compressed).toMatchInlineSnapshot(`"9af41f5de96446dc3776a1eb2d98bb956b7acd9979a67854bec6fa7c2973bd73"`); + + const byteArrayString = `[${compressed.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16))}]`; + + // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data + updateInlineTestData( + 'noir-projects/aztec-nr/aztec/src/utils/point.nr', + 'expected_compressed_point_positive_sign', + byteArrayString, + ); + }); + + it('compressed point with - sign matches Noir', () => { + const p = new Point( + new Fr(0x247371652e55dd74c9af8dbe9fb44931ba29a9229994384bd7077796c14ee2b5n), + new Fr(0x26441aec112e1ae4cee374f42556932001507ad46e255ffb27369c7e3766e5c0n), + false, + ); + expect(p.toXAndSign()[1]).toBe(false); + + const compressed = p.toCompressedBuffer().toString('hex'); + expect(compressed).toMatchInlineSnapshot(`"247371652e55dd74c9af8dbe9fb44931ba29a9229994384bd7077796c14ee2b5"`); + + const byteArrayString = `[${compressed.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16))}]`; + + // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data + updateInlineTestData( + 'noir-projects/aztec-nr/aztec/src/utils/point.nr', + 'expected_compressed_point_negative_sign', + byteArrayString, + ); + }); }); diff --git a/yarn-project/foundation/src/fields/point.ts b/yarn-project/foundation/src/fields/point.ts index 8961a3b90f9..eb443cbf6c0 100644 --- a/yarn-project/foundation/src/fields/point.ts +++ b/yarn-project/foundation/src/fields/point.ts @@ -124,10 +124,10 @@ export class Point { // If y is null, the x-coordinate is not on the curve if (y === null) { - throw new NotOnCurveError(); + throw new NotOnCurveError(x); } - const yPositiveBigInt = y.toBigInt() > (Fr.MODULUS - 1n) / 2n ? Fr.MODULUS - y.toBigInt() : y.toBigInt(); + const yPositiveBigInt = y.toBigInt() <= (Fr.MODULUS - 1n) / 2n ? y.toBigInt() : Fr.MODULUS - y.toBigInt(); const yNegativeBigInt = Fr.MODULUS - yPositiveBigInt; // Choose the positive or negative root based on isPositive @@ -280,9 +280,9 @@ export function isPoint(obj: object): obj is Point { return point.kind === 'point' && point.x !== undefined && point.y !== undefined; } -class NotOnCurveError extends Error { - constructor() { - super('The given x-coordinate is not on the Grumpkin curve'); +export class NotOnCurveError extends Error { + constructor(x: Fr) { + super('The given x-coordinate is not on the Grumpkin curve: ' + x.toString()); this.name = 'NotOnCurveError'; } }