Skip to content

Commit

Permalink
chore: address comments
Browse files Browse the repository at this point in the history
  • Loading branch information
LHerskind committed May 17, 2024
1 parent 995d2e1 commit 699e357
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 34 deletions.
94 changes: 62 additions & 32 deletions yarn-project/circuit-types/src/logs/encrypted_log_payload.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { AztecAddress, Fq, Fr, GeneratorIndex, GrumpkinPrivateKey, Point, type PublicKey } from '@aztec/circuits.js';
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
import { poseidon2Hash } from '@aztec/foundation/crypto';
import {
AztecAddress,
Fr,
type GrumpkinPrivateKey,
Point,
type PublicKey,
computeIvpkApp,
computeIvskApp,
computeOvskApp,
derivePublicKeyFromSecretKey,
} from '@aztec/circuits.js';
import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';

import { EncryptedLogHeader } from './encrypted_log_header.js';
Expand All @@ -9,12 +17,16 @@ import { EncryptedLogOutgoingBody } from './encrypted_log_outgoing_body.js';
import { type L1NotePayload } from './l1_note_payload/l1_note_payload.js';
import { Note } from './l1_note_payload/note.js';

// A placeholder tag until we have a proper tag system in place.
const PLACEHOLDER_TAG = new Fr(33);

const grumpkin = new Grumpkin();
// Both the incoming and the outgoing header are 48 bytes.
// 32 bytes for the address, and 16 bytes padding to follow PKCS#7
const HEADER_SIZE = 48;

const HEADER_SIZE = 48; // 32 bytes + 16 bytes padding. (address)
const OUTGOING_BODY_SIZE = 176; // 160 bytes + 16 bytes padding. (secret key | address | public key)
// 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;

export class EncryptedLogPayload {
constructor(
Expand All @@ -27,7 +39,7 @@ export class EncryptedLogPayload {
*/
public contractAddress: AztecAddress,
/**
* Storage slot of the contract this tx is interacting with.
* Storage slot of the underlying note.
*/
public storageSlot: Fr,
/**
Expand Down Expand Up @@ -59,24 +71,35 @@ export class EncryptedLogPayload {
);
}

/**
* Encrypts a note payload for a given recipient and sender.
* Creates an incoming log the the recipient using the recipient's ivsk, and
* an outgoing log for the sender using the sender's ovsk.
*
* @param ephSk - An ephemeral secret key used for the encryption
* @param recipient - The recipient address, retrievable by the sender for his logs
* @param ivpk - The incoming viewing public key of the recipient
* @param ovsk - The outgoing viewing secret key of the sender
* @returns A buffer containing the encrypted log payload
*/
public encrypt(ephSk: GrumpkinPrivateKey, recipient: AztecAddress, ivpk: PublicKey, ovsk: GrumpkinPrivateKey) {
const ephPk = grumpkin.mul(Grumpkin.generator, ephSk);
const ovpk = grumpkin.mul(Grumpkin.generator, ovsk);
const ephPk = derivePublicKeyFromSecretKey(ephSk);
const ovpk = derivePublicKeyFromSecretKey(ovsk);

const header = new EncryptedLogHeader(this.contractAddress);

const incomingHeaderCiphertext = header.computeCiphertext(ephSk, ivpk);
const outgoingHeaderCiphertext = header.computeCiphertext(ephSk, ovpk);

const ivpkApp = EncryptedLogPayload.computeIvpkApp(ivpk, this.contractAddress);
const ivpkApp = computeIvpkApp(ivpk, this.contractAddress);

const incomingBodyCiphertext = new EncryptedLogIncomingBody(
this.storageSlot,
this.noteTypeId,
this.note,
).computeCiphertext(ephSk, ivpkApp);

const ovskApp = EncryptedLogPayload.computeOvskApp(ovsk, this.contractAddress);
const ovskApp = computeOvskApp(ovsk, this.contractAddress);

const outgoingBodyCiphertext = new EncryptedLogOutgoingBody(ephSk, recipient, ivpkApp).computeCiphertext(
ovskApp,
Expand All @@ -94,6 +117,18 @@ export class EncryptedLogPayload {
]);
}

/**
* Decrypts a ciphertext as an incoming log.
*
* This is executable by the recipient of the note, and uses the ivsk to decrypt the payload.
* The outgoing parts of the log are ignored entirely.
*
* Produces the same output as `decryptAsOutgoing`.
*
* @param ciphertext - The ciphertext for the log
* @param ivsk - The incoming viewing secret key, used to decrypt the logs
* @returns The decrypted log payload
*/
public static decryptAsIncoming(ciphertext: Buffer | bigint[], ivsk: GrumpkinPrivateKey) {
const input = Buffer.isBuffer(ciphertext) ? ciphertext : Buffer.from(ciphertext.map((x: bigint) => Number(x)));
const reader = BufferReader.asReader(input);
Expand All @@ -106,14 +141,14 @@ export class EncryptedLogPayload {

const incomingHeader = EncryptedLogHeader.fromCiphertext(reader.readBytes(HEADER_SIZE), ivsk, ephPk);

// Skipping outgoing
// Skipping the outgoing header and body
reader.readBytes(HEADER_SIZE);
reader.readBytes(OUTGOING_BODY_SIZE);

// The incoming can be of variable size, so we read until the end
const incomingBodySlice = reader.readToEnd();

const ivskApp = EncryptedLogPayload.computeIvskApp(ivsk, incomingHeader.address);
const ivskApp = computeIvskApp(ivsk, incomingHeader.address);
const incomingBody = EncryptedLogIncomingBody.fromCiphertext(incomingBodySlice, ivskApp, ephPk);

return new EncryptedLogPayload(
Expand All @@ -124,6 +159,19 @@ export class EncryptedLogPayload {
);
}

/**
* Decrypts a ciphertext as an outgoing log.
*
* This is executable by the sender of the note, and uses the ovsk to decrypt the payload.
* The outgoing parts are decrypted to retrieve information that allows the sender to
* decrypt the incoming log, and learn about the note contents.
*
* Produces the same output as `decryptAsIncoming`.
*
* @param ciphertext - The ciphertext for the log
* @param ovsk - The outgoing viewing secret key, used to decrypt the logs
* @returns The decrypted log payload
*/
public static decryptAsOutgoing(ciphertext: Buffer | bigint[], ovsk: GrumpkinPrivateKey) {
const input = Buffer.isBuffer(ciphertext) ? ciphertext : Buffer.from(ciphertext.map((x: bigint) => Number(x)));
const reader = BufferReader.asReader(input);
Expand All @@ -137,10 +185,9 @@ export class EncryptedLogPayload {
// Skip the incoming header
reader.readBytes(HEADER_SIZE);

// Skipping outgoing
const outgoingHeader = EncryptedLogHeader.fromCiphertext(reader.readBytes(HEADER_SIZE), ovsk, ephPk);

const ovskApp = EncryptedLogPayload.computeOvskApp(ovsk, outgoingHeader.address);
const ovskApp = computeOvskApp(ovsk, outgoingHeader.address);
const outgoingBody = EncryptedLogOutgoingBody.fromCiphertext(reader.readBytes(OUTGOING_BODY_SIZE), ovskApp, ephPk);

// The incoming can be of variable size, so we read until the end
Expand All @@ -159,21 +206,4 @@ export class EncryptedLogPayload {
incomingBody.noteTypeId,
);
}

static computeIvpkApp(ivpk: PublicKey, address: AztecAddress) {
const I = Fq.fromBuffer(poseidon2Hash([address.toField(), ivpk.x, ivpk.y, GeneratorIndex.IVSK_M]).toBuffer());
return grumpkin.add(grumpkin.mul(Grumpkin.generator, I), ivpk);
}

static computeIvskApp(ivsk: GrumpkinPrivateKey, address: AztecAddress) {
const ivpk = grumpkin.mul(Grumpkin.generator, ivsk);
const I = Fq.fromBuffer(poseidon2Hash([address.toField(), ivpk.x, ivpk.y, GeneratorIndex.IVSK_M]).toBuffer());
return new Fq((I.toBigInt() + ivsk.toBigInt()) % Fq.MODULUS);
}

static computeOvskApp(ovsk: GrumpkinPrivateKey, address: AztecAddress) {
return GrumpkinPrivateKey.fromBuffer(
poseidon2Hash([address.toField(), ovsk.high, ovsk.low, GeneratorIndex.OVSK_M]).toBuffer(),
);
}
}
24 changes: 22 additions & 2 deletions yarn-project/circuits.js/src/keys/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,36 @@
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { poseidon2Hash, sha512ToGrumpkinScalar } from '@aztec/foundation/crypto';
import { type Fq, type Fr, type GrumpkinScalar } from '@aztec/foundation/fields';
import { Fq, type Fr, type GrumpkinScalar } from '@aztec/foundation/fields';

import { Grumpkin } from '../barretenberg/crypto/grumpkin/index.js';
import { GeneratorIndex } from '../constants.gen.js';
import { type GrumpkinPrivateKey } from '../types/grumpkin_private_key.js';
import { GrumpkinPrivateKey } from '../types/grumpkin_private_key.js';
import { type PublicKey } from '../types/public_key.js';
import { PublicKeys } from '../types/public_keys.js';

const curve = new Grumpkin();

export function computeAppNullifierSecretKey(masterNullifierSecretKey: GrumpkinPrivateKey, app: AztecAddress): Fr {
return poseidon2Hash([masterNullifierSecretKey.high, masterNullifierSecretKey.low, app, GeneratorIndex.NSK_M]);
}

export function computeIvpkApp(ivpk: PublicKey, address: AztecAddress) {
const I = Fq.fromBuffer(poseidon2Hash([address.toField(), ivpk.x, ivpk.y, GeneratorIndex.IVSK_M]).toBuffer());
return curve.add(curve.mul(Grumpkin.generator, I), ivpk);
}

export function computeIvskApp(ivsk: GrumpkinPrivateKey, address: AztecAddress) {
const ivpk = curve.mul(Grumpkin.generator, ivsk);
const I = Fq.fromBuffer(poseidon2Hash([address.toField(), ivpk.x, ivpk.y, GeneratorIndex.IVSK_M]).toBuffer());
return new Fq((I.toBigInt() + ivsk.toBigInt()) % Fq.MODULUS);
}

export function computeOvskApp(ovsk: GrumpkinPrivateKey, address: AztecAddress) {
return GrumpkinPrivateKey.fromBuffer(
poseidon2Hash([address.toField(), ovsk.high, ovsk.low, GeneratorIndex.OVSK_M]).toBuffer(),
);
}

export function deriveMasterNullifierSecretKey(secretKey: Fr): GrumpkinScalar {
return sha512ToGrumpkinScalar([secretKey, GeneratorIndex.NSK_M]);
}
Expand Down

0 comments on commit 699e357

Please sign in to comment.