Skip to content

Commit

Permalink
feat: constrain encryption
Browse files Browse the repository at this point in the history
  • Loading branch information
LHerskind committed May 15, 2024
1 parent d631fd2 commit 008a88a
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 25 deletions.
13 changes: 10 additions & 3 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
// By updating the import here, you can "switch" between using an oracle for computing the encrypted
// logs, or constraining it in the circuit. The interfaces are the exact same, so just flip this.
// use crate::oracle::logs::compute_encrypted_log;
use crate::encrypted_logs::payload::compute_encrypted_log;

use crate::{
context::{inputs::PrivateContextInputs, interface::ContextInterface},
messaging::process_l1_to_l2_message,
hash::{hash_args_array, ArgsHasher, compute_unencrypted_log_hash},
oracle::{
arguments, returns, call_private_function::call_private_function_internal, header::get_header_at,
logs::{emit_encrypted_log, compute_encrypted_log},
logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog},
logs::emit_encrypted_log, logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog},
nullifier_key::{get_nullifier_keys, get_nullifier_keys_with_npk_m_hash, NullifierKeys},
enqueue_public_function_call::{
enqueue_public_function_call_internal, set_public_teardown_function_call_internal,
Expand Down Expand Up @@ -278,7 +282,10 @@ impl PrivateContext {
// --> might be a better approach to force devs to make a public function call that emits the log if needed then
// it would be less easy to accidentally leak information.
// If we decide to keep this function around would make sense to wait for traits and then merge it with emit_unencrypted_log.
pub fn emit_unencrypted_log<T, N, M>(&mut self, log: T) where T: ToBytesForUnencryptedLog<N, M> {
pub fn emit_unencrypted_log<T, N, M>(
&mut self,
log: T
) where T: ToBytesForUnencryptedLog<N, M> {
let event_selector = 5; // TODO: compute actual event selector.
let contract_address = self.this_address();
let log_slice = log.to_be_bytes_arr();
Expand Down
1 change: 1 addition & 0 deletions noir-projects/aztec-nr/aztec/src/encrypted_logs.nr
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod header;
mod incoming_body;
mod outgoing_body;
mod payload;
1 change: 0 additions & 1 deletion noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ impl EncryptedLogHeader {
EncryptedLogHeader { address }
}

// @todo Issue(#5901) Figure out if we return the bytes or fields for the log
fn compute_ciphertext(self, secret: GrumpkinPrivateKey, point: GrumpkinPoint) -> [u8; 48] {
let full_key = point_to_symmetric_key(secret, point);
let mut sym_key = [0; 16];
Expand Down
29 changes: 10 additions & 19 deletions noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,18 @@ use dep::protocol_types::{grumpkin_private_key::GrumpkinPrivateKey, grumpkin_poi
use dep::std::aes128::aes128_encrypt_slice;
use crate::keys::point_to_symmetric_key::point_to_symmetric_key;

struct EncryptedLogIncomingBody<Note> {
struct EncryptedLogIncomingBody<N> {
storage_slot: Field,
note_type_id: Field,
note: Note,
note_preimage: [Field; N],
}

impl<Note> EncryptedLogIncomingBody<Note> {
pub fn new<N>(
storage_slot: Field,
note_type_id: Field,
note: Note
) -> Self where Note: NoteInterface<N> {
Self { storage_slot, note_type_id, note }
impl<N> EncryptedLogIncomingBody<N> {
pub fn new(storage_slot: Field, note_type_id: Field, note_preimage: [Field; N]) -> Self {
Self { storage_slot, note_type_id, note_preimage }
}

pub fn compute_ciphertext<N>(
self,
eph_sk: GrumpkinPrivateKey,
ivpk_app: GrumpkinPoint
) -> [u8] where Note: NoteInterface<N> {
let serialized_note: [Field; N] = self.note.serialize_content();

pub fn compute_ciphertext(self, eph_sk: GrumpkinPrivateKey, ivpk_app: GrumpkinPoint) -> [u8] {
let mut buffer_slice: [u8] = &[];

let storage_slot_bytes = self.storage_slot.to_be_bytes(32);
Expand All @@ -39,8 +29,8 @@ impl<Note> EncryptedLogIncomingBody<Note> {
buffer_slice = buffer_slice.push_back(note_type_id_bytes[i]);
}

for i in 0..serialized_note.len() {
let bytes = serialized_note[i].to_be_bytes(32);
for i in 0..self.note_preimage.len() {
let bytes = self.note_preimage[i].to_be_bytes(32);
for j in 0..32 {
buffer_slice = buffer_slice.push_back(bytes[j]);
}
Expand Down Expand Up @@ -118,7 +108,8 @@ mod test {

let note_type_id = 1;
let storage_slot = 2;
let body = EncryptedLogIncomingBody::new(storage_slot, note_type_id, note);
let note_preimage = note.serialize_content();
let body = EncryptedLogIncomingBody::new(storage_slot, note_type_id, note_preimage);

let eph_sk = GrumpkinPrivateKey::new(
0x0000000000000000000000000000000023b3127c127b1f29a7adff5cccf8fb06,
Expand Down
98 changes: 98 additions & 0 deletions noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use dep::protocol_types::{
address::AztecAddress, grumpkin_private_key::GrumpkinPrivateKey, grumpkin_point::GrumpkinPoint,
constants::{GENERATOR_INDEX__IVSK_M, GENERATOR_INDEX__OVSK_M}
};

use dep::std::{embedded_curve_ops::{embedded_curve_add, EmbeddedCurvePoint}, field::bytes32_to_field};

use crate::oracle::unsafe_rand::unsafe_rand;

use crate::hash::poseidon2_hash;

use crate::encrypted_logs::header::EncryptedLogHeader;
use crate::encrypted_logs::incoming_body::EncryptedLogIncomingBody;
use crate::encrypted_logs::outgoing_body::EncryptedLogOutgoingBody;

pub fn compute_encrypted_log<N, M>(
contract_address: AztecAddress,
storage_slot: Field,
note_type_id: Field,
ivpk: GrumpkinPoint,
preimage: [Field; N]
) -> [u8; M] {
// @todo Need to draw randomness from the full domain of Fq not only Fr
let eph_sk: GrumpkinPrivateKey = fr_to_private_key(unsafe_rand());
let eph_pk = eph_sk.derive_public_key();

// @todo Issue(#6410) need to provide ovsk as input, and constrain the deriviation
// The current outgoing is meaningless so we just fill it with garbage.
let ovsk: GrumpkinPrivateKey = fr_to_private_key(unsafe_rand());
let ovpk = ovsk.derive_public_key();
let recipient = AztecAddress::from_field(0);

let ivpk_app = compute_ivpk_app(ivpk, contract_address);
let ovsk_app = compute_ovsk_app(ovsk, contract_address);

let header = EncryptedLogHeader::new(contract_address);

let incoming_header_ciphertext: [u8; 48] = header.compute_ciphertext(eph_sk, ivpk);
let outgoing_Header_ciphertext: [u8; 48] = header.compute_ciphertext(eph_sk, ovpk);
let incoming_body_ciphertext = EncryptedLogIncomingBody::new(storage_slot, note_type_id, preimage).compute_ciphertext(eph_sk, ivpk_app);
let outgoing_body_ciphertext: [u8; 176] = EncryptedLogOutgoingBody::new(eph_sk, recipient, ivpk_app).compute_ciphertext(ovsk_app, eph_pk);

let mut encrypted_bytes: [u8; M] = [0; M];
// @todo We ignore the tags for now

let eph_pk_bytes = eph_pk.to_be_bytes();
for i in 0..64 {
encrypted_bytes[64 + i] = eph_pk_bytes[i];
}
for i in 0..48 {
encrypted_bytes[128 + i] = incoming_header_ciphertext[i];
encrypted_bytes[176 + i] = outgoing_Header_ciphertext[i];
}
for i in 0..176 {
encrypted_bytes[224 + i] = outgoing_body_ciphertext[i];
}
// Then we fill in the rest as the incoming body ciphertext
let size = M - 400;
assert_eq(size, incoming_body_ciphertext.len(), "ciphertext length mismatch");
for i in 0..size {
encrypted_bytes[400 + i] = incoming_body_ciphertext[i];
}

encrypted_bytes
}

fn fr_to_private_key(r: Field) -> GrumpkinPrivateKey {
let r_bytes = r.to_be_bytes(32);

let mut high_bytes = [0; 32];
let mut low_bytes = [0; 32];

for i in 0..16 {
high_bytes[16 + i] = r_bytes[i];
low_bytes[16 + i] = r_bytes[i + 16];
}

let low = bytes32_to_field(low_bytes);
let high = bytes32_to_field(high_bytes);

GrumpkinPrivateKey::new(high, low)
}

fn compute_ivpk_app(ivpk: GrumpkinPoint, contract_address: AztecAddress) -> GrumpkinPoint {
let i = fr_to_private_key(poseidon2_hash([contract_address.to_field(), ivpk.x, ivpk.y, GENERATOR_INDEX__IVSK_M]));
let I = i.derive_public_key();

let embed_I = EmbeddedCurvePoint { x: I.x, y: I.y };
let embed_ivpk = EmbeddedCurvePoint { x: ivpk.x, y: ivpk.y };

let embed_result = embedded_curve_add(embed_I, embed_ivpk);

GrumpkinPoint::new(embed_result.x, embed_result.y)
}

fn compute_ovsk_app(ovsk: GrumpkinPrivateKey, contract_address: AztecAddress) -> GrumpkinPrivateKey {
fr_to_private_key(poseidon2_hash([contract_address.to_field(), ovsk.high, ovsk.low, GENERATOR_INDEX__OVSK_M]))
}
Original file line number Diff line number Diff line change
Expand Up @@ -367,8 +367,7 @@ contract Test {
storage_slot: Field,
value: Field
) -> [u8; 112] {
let note = TestNote::new(value);
EncryptedLogIncomingBody::new(storage_slot, TestNote::get_note_type_id(), note).compute_ciphertext(secret, point).as_array()
EncryptedLogIncomingBody::new(storage_slot, TestNote::get_note_type_id(), [value]).compute_ciphertext(secret, point).as_array()
}

#[aztec(private)]
Expand Down

0 comments on commit 008a88a

Please sign in to comment.