-
Notifications
You must be signed in to change notification settings - Fork 236
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add ciphertext computation for log header (#6175)
Fixes #5867 with the AES oracle on the noir side and a encrypt/decrypt tool on the typescript side as well. Changes the symmetric key derivation to use `GrumpkinPrivateKey` instead of the `GrumpkinScalar`, this changes the order of low/high.
- Loading branch information
Showing
13 changed files
with
229 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
mod header; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
use dep::protocol_types::{address::AztecAddress, grumpkin_private_key::GrumpkinPrivateKey, grumpkin_point::GrumpkinPoint}; | ||
|
||
use crate::oracle::encryption::aes128_encrypt; | ||
use crate::keys::point_to_symmetric_key::point_to_symmetric_key; | ||
|
||
struct EncryptedLogHeader { | ||
address: AztecAddress, | ||
} | ||
|
||
impl EncryptedLogHeader { | ||
fn new(address: AztecAddress) -> Self { | ||
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; 32] { | ||
let full_key = point_to_symmetric_key(secret, point); | ||
let mut sym_key = [0; 16]; | ||
let mut iv = [0; 16]; | ||
let mut input = [0; 32]; | ||
let input_slice = self.address.to_field().to_be_bytes(32); | ||
|
||
for i in 0..16 { | ||
sym_key[i] = full_key[i]; | ||
iv[i] = full_key[i + 16]; | ||
|
||
// We copy address on the following 2 lines in order to avoid having 2 loops | ||
input[i] = input_slice[i]; | ||
input[i + 16] = input_slice[i + 16]; | ||
} | ||
|
||
// @todo Issue(#6172) This encryption is currently using an oracle. It is not actually constrained atm. | ||
aes128_encrypt(input, iv, sym_key) | ||
} | ||
} | ||
|
||
// @todo Issue(#6172) This is to be run as a test. But it is currently using the AES oracle so will fail there. | ||
fn test_encrypted_log_header() { | ||
let address = AztecAddress::from_field(0xdeadbeef); | ||
let header = EncryptedLogHeader::new(address); | ||
let secret = GrumpkinPrivateKey::new( | ||
0x0000000000000000000000000000000023b3127c127b1f29a7adff5cccf8fb06, | ||
0x00000000000000000000000000000000649e7ca01d9de27b21624098b897babd | ||
); | ||
let point = GrumpkinPoint::new( | ||
0x2688431c705a5ff3e6c6f2573c9e3ba1c1026d2251d0dbbf2d810aa53fd1d186, | ||
0x1e96887b117afca01c00468264f4f80b5bb16d94c1808a448595f115556e5c8e | ||
); | ||
|
||
let ciphertext = header.compute_ciphertext(secret, point); | ||
|
||
let expected_header_ciphertext = [ | ||
131, 119, 105, 129, 244, 32, 151, 205, 12, 99, 93, 62, 10, 180, 72, 21, 179, 36, 250, 95, 56, 167, 171, 16, 195, 164, 223, 57, 75, 5, 24, 119 | ||
]; | ||
|
||
assert_eq(ciphertext, expected_header_ciphertext); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,4 +10,5 @@ mod oracle; | |
mod state_vars; | ||
mod prelude; | ||
mod public_storage; | ||
mod encrypted_logs; | ||
use dep::protocol_types; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
yarn-project/circuit-types/src/logs/encrypted_log_header.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { AztecAddress, GrumpkinScalar } from '@aztec/circuits.js'; | ||
import { Grumpkin } from '@aztec/circuits.js/barretenberg'; | ||
import { updateInlineTestData } from '@aztec/foundation/testing'; | ||
|
||
import { EncryptedLogHeader } from './encrypted_log_header.js'; | ||
|
||
describe('encrypt log header', () => { | ||
let grumpkin: Grumpkin; | ||
|
||
beforeAll(() => { | ||
grumpkin = new Grumpkin(); | ||
}); | ||
|
||
it('encrypt and decrypt a log header', () => { | ||
const ephSecretKey = GrumpkinScalar.random(); | ||
const viewingSecretKey = GrumpkinScalar.random(); | ||
|
||
const ephPubKey = grumpkin.mul(Grumpkin.generator, ephSecretKey); | ||
const viewingPubKey = grumpkin.mul(Grumpkin.generator, viewingSecretKey); | ||
|
||
const addr = AztecAddress.random(); | ||
const header = new EncryptedLogHeader(addr); | ||
|
||
const encrypted = header.computeCiphertext(ephSecretKey, viewingPubKey); | ||
|
||
const recreated = EncryptedLogHeader.fromCiphertext(encrypted, viewingSecretKey, ephPubKey); | ||
|
||
expect(recreated.toBuffer()).toEqual(addr.toBuffer()); | ||
}); | ||
|
||
it('encrypt a log header, generate input for noir test', () => { | ||
// The following 2 are arbitrary fixed values - fixed in order to test a match with Noir | ||
const viewingSecretKey: GrumpkinScalar = new GrumpkinScalar( | ||
0x23b3127c127b1f29a7adff5cccf8fb06649e7ca01d9de27b21624098b897babdn, | ||
); | ||
const ephSecretKey: GrumpkinScalar = new GrumpkinScalar( | ||
0x1fdd0dd8c99b21af8e00d2d130bdc263b36dadcbea84ac5ec9293a0660deca01n, | ||
); | ||
|
||
const viewingPubKey = grumpkin.mul(Grumpkin.generator, viewingSecretKey); | ||
|
||
const addr = AztecAddress.fromBigInt(BigInt('0xdeadbeef')); | ||
const header = new EncryptedLogHeader(addr); | ||
|
||
const encrypted = header.computeCiphertext(ephSecretKey, viewingPubKey); | ||
|
||
const byteArrayString = `[${encrypted | ||
.toString('hex') | ||
.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/encrypted_logs/header.nr', | ||
'expected_header_ciphertext', | ||
byteArrayString, | ||
); | ||
}); | ||
}); |
72 changes: 72 additions & 0 deletions
72
yarn-project/circuit-types/src/logs/encrypted_log_header.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { AztecAddress, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; | ||
import { Aes128 } from '@aztec/circuits.js/barretenberg'; | ||
|
||
import { deriveAESSecret } from './l1_note_payload/encrypt_buffer.js'; | ||
|
||
/** | ||
* An encrypted log header, containing the address of the log along with utility | ||
* functions to compute and decrypt its ciphertext. | ||
* | ||
* Using AES-128-CBC for encryption. | ||
* Can be used for both incoming and outgoing logs. | ||
* | ||
*/ | ||
export class EncryptedLogHeader { | ||
constructor(public readonly address: AztecAddress) {} | ||
|
||
/** | ||
* Serializes the log header to a buffer | ||
* | ||
* @returns The serialized log header | ||
*/ | ||
public toBuffer(): Buffer { | ||
return this.address.toBuffer(); | ||
} | ||
|
||
public static fromBuffer(buf: Buffer): EncryptedLogHeader { | ||
return new EncryptedLogHeader(AztecAddress.fromBuffer(buf)); | ||
} | ||
|
||
/** | ||
* Computes the ciphertext of the encrypted log header | ||
* | ||
* @param secret - An ephemeral secret key | ||
* @param publicKey - The incoming or outgoing viewing key of the "recipient" of this log | ||
* @returns The ciphertext of the encrypted log header | ||
*/ | ||
public computeCiphertext(secret: GrumpkinPrivateKey, publicKey: PublicKey) { | ||
const aesSecret = deriveAESSecret(secret, publicKey); | ||
const key = aesSecret.subarray(0, 16); | ||
const iv = aesSecret.subarray(16, 32); | ||
|
||
const aes128 = new Aes128(); | ||
const buffer = this.address.toBuffer(); | ||
|
||
return aes128.encryptBufferCBC(buffer, iv, key); | ||
} | ||
|
||
/** | ||
* | ||
* @param ciphertext - The ciphertext buffer | ||
* @param secret - The private key matching the public key used in encryption | ||
* @param publicKey - The public key generated with the ephemeral secret key used in encryption | ||
* e.g., eph_sk * G | ||
* @returns | ||
*/ | ||
public static fromCiphertext( | ||
ciphertext: Buffer | bigint[], | ||
secret: GrumpkinPrivateKey, | ||
publicKey: PublicKey, | ||
): EncryptedLogHeader { | ||
const input = Buffer.isBuffer(ciphertext) ? ciphertext : Buffer.from(ciphertext.map((x: bigint) => Number(x))); | ||
|
||
const aesSecret = deriveAESSecret(secret, publicKey); | ||
const key = aesSecret.subarray(0, 16); | ||
const iv = aesSecret.subarray(16, 32); | ||
|
||
const aes128 = new Aes128(); | ||
const buffer = aes128.decryptBufferCBC(input, iv, key); | ||
const address = AztecAddress.fromBuffer(buffer); | ||
return new EncryptedLogHeader(address); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters