diff --git a/noir-projects/aztec-nr/aztec/src/oracle.nr b/noir-projects/aztec-nr/aztec/src/oracle.nr index 57415dc9575..753ef6e930a 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle.nr @@ -4,6 +4,7 @@ mod arguments; mod call_private_function; +mod encryption; mod get_contract_instance; mod get_l1_to_l2_membership_witness; mod get_nullifier_membership_witness; diff --git a/noir-projects/aztec-nr/aztec/src/oracle/encryption.nr b/noir-projects/aztec-nr/aztec/src/oracle/encryption.nr new file mode 100644 index 00000000000..cb655c756ce --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/oracle/encryption.nr @@ -0,0 +1,7 @@ + +#[oracle(aes128Encrypt)] +pub fn aes128_encrypt_oracle(input: [u8; N], iv: [u8; 16], key: [u8; 16]) -> [u8; N] {} + +unconstrained pub fn aes128_encrypt(input: [u8; N], iv: [u8; 16], key: [u8; 16]) -> [u8; N] { + aes128_encrypt_oracle(input, iv, key) +} 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 f7a40cf41a8..107df25dc72 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -25,7 +25,7 @@ contract Test { note_getter_options::NoteStatus }, deploy::deploy_contract as aztec_deploy_contract, - oracle::{get_public_key::get_public_key as get_public_key_oracle, unsafe_rand::unsafe_rand} + oracle::{encryption::aes128_encrypt, get_public_key::get_public_key as get_public_key_oracle, unsafe_rand::unsafe_rand} }; use dep::token_portal_content_hash_lib::{get_mint_private_content_hash, get_mint_public_content_hash}; use dep::value_note::value_note::ValueNote; @@ -309,6 +309,12 @@ contract Test { assert(context.version() == version, "Invalid version"); } + #[aztec(private)] + fn encrypt(input: [u8; 64], iv: [u8; 16], key: [u8; 16]) { + let result = aes128_encrypt(input, iv, key); + context.emit_unencrypted_log(result); + } + #[aztec(public)] fn assert_public_global_vars( chain_id: Field, diff --git a/yarn-project/end-to-end/src/e2e_encryption.test.ts b/yarn-project/end-to-end/src/e2e_encryption.test.ts new file mode 100644 index 00000000000..861a7c573c3 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_encryption.test.ts @@ -0,0 +1,41 @@ +import { type Wallet } from '@aztec/aztec.js'; +import { Aes128 } from '@aztec/circuits.js/barretenberg'; +import { TestContract } from '@aztec/noir-contracts.js'; + +import { randomBytes } from 'crypto'; + +import { setup } from './fixtures/utils.js'; + +describe('e2e_encryption', () => { + const aes128 = new Aes128(); + + let wallet: Wallet; + let teardown: () => Promise; + + let contract: TestContract; + + beforeAll(async () => { + ({ teardown, wallet } = await setup()); + contract = await TestContract.deploy(wallet).send().deployed(); + }, 25_000); + + afterAll(() => teardown()); + + it('encrypts', async () => { + const input = randomBytes(64); + const iv = randomBytes(16); + const key = randomBytes(16); + + const expectedCiphertext = aes128.encryptBufferCBC(input, iv, key); + + const logs = await contract.methods + .encrypt(Array.from(input), Array.from(iv), Array.from(key)) + .send() + .getUnencryptedLogs(); + // Each byte of encrypted data is in its own field and it's all serialized into a long buffer so we simply extract + // each 32nd byte from the buffer to get the encrypted data + const recoveredCiphertext = logs.logs[0].log.data.filter((_, i) => (i + 1) % 32 === 0); + + expect(recoveredCiphertext).toEqual(expectedCiphertext); + }); +}); diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index ac88228ef08..f112db7d65a 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -378,4 +378,17 @@ export class Oracle { ); return toAcvmEnqueuePublicFunctionResult(enqueuedRequest); } + + aes128Encrypt(input: ACVMField[], initializationVector: ACVMField[], key: ACVMField[]): ACVMField[] { + // Convert each field to a number and then to a buffer (1 byte is stored in 1 field) + const processedInput = Buffer.from(input.map(fromACVMField).map(f => f.toNumber())); + const processedIV = Buffer.from(initializationVector.map(fromACVMField).map(f => f.toNumber())); + const processedKey = Buffer.from(key.map(fromACVMField).map(f => f.toNumber())); + + // Encrypt the input + const ciphertext = this.typedOracle.aes128Encrypt(processedInput, processedIV, processedKey); + + // Convert each byte of ciphertext to a field and return it + return Array.from(ciphertext).map(byte => toACVMField(byte)); + } } diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 70f57233af1..0771458a972 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -233,4 +233,8 @@ export abstract class TypedOracle { ): Promise { throw new OracleMethodNotAvailableError('enqueuePublicFunctionCall'); } + + aes128Encrypt(_input: Buffer, _initializationVector: Buffer, _key: Buffer): Buffer { + throw new OracleMethodNotAvailableError('encrypt'); + } } diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 0ec755281cc..0bcb1f07dbb 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -21,7 +21,7 @@ import { type SideEffect, type TxContext, } from '@aztec/circuits.js'; -import { type Grumpkin } from '@aztec/circuits.js/barretenberg'; +import { Aes128, type Grumpkin } from '@aztec/circuits.js/barretenberg'; import { computePublicDataTreeLeafSlot, computeUniqueNoteHash, siloNoteHash } from '@aztec/circuits.js/hash'; import { type FunctionAbi, type FunctionArtifact, countArgumentsSize } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; @@ -522,4 +522,9 @@ export class ClientExecutionContext extends ViewDataOracle { } return values; } + + public override aes128Encrypt(input: Buffer, initializationVector: Buffer, key: Buffer): Buffer { + const aes128 = new Aes128(); + return aes128.encryptBufferCBC(input, initializationVector, key); + } }