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 23, 2024
1 parent c7a2b84 commit 45efc88
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 3 deletions.
12 changes: 10 additions & 2 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// 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,
Expand All @@ -7,7 +12,7 @@ use crate::{
oracle::{
key_validation_request::get_key_validation_request, arguments, returns,
call_private_function::call_private_function_internal, header::get_header_at,
logs::{emit_encrypted_log, emit_encrypted_note_log, compute_encrypted_log},
logs::{emit_encrypted_log, emit_encrypted_note_log},
logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog},
enqueue_public_function_call::{
enqueue_public_function_call_internal, set_public_teardown_function_call_internal,
Expand Down Expand Up @@ -267,7 +272,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 counter = self.next_counter();
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
96 changes: 96 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,96 @@
use dep::protocol_types::{
address::AztecAddress, grumpkin_private_key::GrumpkinPrivateKey, grumpkin_point::GrumpkinPoint,
constants::{GENERATOR_INDEX__IVSK_M, GENERATOR_INDEX__OVSK_M}, hash::poseidon2_hash
};

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

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

use crate::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]))
}

0 comments on commit 45efc88

Please sign in to comment.