Skip to content

Commit

Permalink
fixing key naming + arg order
Browse files Browse the repository at this point in the history
  • Loading branch information
benesjan committed Apr 30, 2024
1 parent d22136a commit aa6a24e
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ describe('encrypt buffer', () => {

it('derive shared secret', () => {
// The following 2 are arbitrary fixed values - fixed in order to test a match with Noir
const ownerPrivKey: GrumpkinScalar = new Fq(0x23b3127c127b1f29a7adff5cccf8fb06649e7ca01d9de27b21624098b897babdn);
const ephPrivKey: GrumpkinScalar = new Fq(0x1fdd0dd8c99b21af8e00d2d130bdc263b36dadcbea84ac5ec9293a0660deca01n);
const ownerSecretKey: GrumpkinScalar = new Fq(0x23b3127c127b1f29a7adff5cccf8fb06649e7ca01d9de27b21624098b897babdn);
const ephSecretKey: GrumpkinScalar = new Fq(0x1fdd0dd8c99b21af8e00d2d130bdc263b36dadcbea84ac5ec9293a0660deca01n);

const ownerPubKey = grumpkin.mul(Grumpkin.generator, ownerPrivKey);
const ephPubKey = grumpkin.mul(Grumpkin.generator, ephPrivKey);
const ownerPubKey = grumpkin.mul(Grumpkin.generator, ownerSecretKey);
const ephPubKey = grumpkin.mul(Grumpkin.generator, ephSecretKey);

const secretBySender = deriveAESSecret(ownerPubKey, ephPrivKey);
const secretByReceiver = deriveAESSecret(ephPubKey, ownerPrivKey);
const secretBySender = deriveAESSecret(ephSecretKey, ownerPubKey);
const secretByReceiver = deriveAESSecret(ownerSecretKey, ephPubKey);
expect(secretBySender.toString('hex')).toEqual(secretByReceiver.toString('hex'));

const byteArrayString = `[${secretBySender
Expand All @@ -38,26 +38,26 @@ describe('encrypt buffer', () => {

it('convert to and from encrypted buffer', () => {
const data = randomBytes(253);
const ownerPrivKey = GrumpkinScalar.random();
const ownerPubKey = grumpkin.mul(Grumpkin.generator, ownerPrivKey);
const ephPrivKey = GrumpkinScalar.random();
const encrypted = encryptBuffer(data, ownerPubKey, ephPrivKey);
const decrypted = decryptBuffer(encrypted, ownerPrivKey);
const ownerSecretKey = GrumpkinScalar.random();
const ownerPubKey = grumpkin.mul(Grumpkin.generator, ownerSecretKey);
const ephSecretKey = GrumpkinScalar.random();
const encrypted = encryptBuffer(data, ephSecretKey, ownerPubKey);
const decrypted = decryptBuffer(encrypted, ownerSecretKey);
expect(decrypted).not.toBeUndefined();
expect(decrypted).toEqual(data);
});

it('decrypting gibberish returns undefined', () => {
const data = randomBytes(253);
const ownerPrivKey = GrumpkinScalar.random();
const ephPrivKey = GrumpkinScalar.random();
const ownerPubKey = grumpkin.mul(Grumpkin.generator, ownerPrivKey);
const encrypted = encryptBuffer(data, ownerPubKey, ephPrivKey);
const ownerSecretKey = GrumpkinScalar.random();
const ephSecretKey = GrumpkinScalar.random();
const ownerPubKey = grumpkin.mul(Grumpkin.generator, ownerSecretKey);
const encrypted = encryptBuffer(data, ephSecretKey, ownerPubKey);

// Introduce gibberish.
const gibberish = Buffer.concat([randomBytes(8), encrypted.subarray(8)]);

const decrypted = decryptBuffer(gibberish, ownerPrivKey);
const decrypted = decryptBuffer(gibberish, ownerSecretKey);
expect(decrypted).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import { createCipheriv, createDecipheriv } from 'browserify-cipher';
* the shared secret. The shared secret is then hashed using SHA-256 to produce the final
* AES secret key.
*
* @param ecdhPubKey - The ECDH public key represented as a PublicKey object.
* @param ecdhPrivKey - The ECDH private key represented as a Buffer object.
* @returns A Buffer containing the derived AES secret key.
* @param secretKey - The secret key used to derive shared secret.
* @param publicKey - The public key used to derive shared secret.
* @returns A derived AES secret key.
* TODO(#5726): This function is called point_to_symmetric_key in Noir. I don't like that name much since point is not
* the only input of the function. Unify naming once we have a better name.
*/
export function deriveAESSecret(ecdhPubKey: PublicKey, ecdhPrivKey: GrumpkinPrivateKey): Buffer {
export function deriveAESSecret(secretKey: GrumpkinPrivateKey, publicKey: PublicKey): Buffer {
const curve = new Grumpkin();
const sharedSecret = curve.mul(ecdhPubKey, ecdhPrivKey);
const sharedSecret = curve.mul(publicKey, secretKey);
const secretBuffer = Buffer.concat([sharedSecret.toBuffer(), numToUInt8(GeneratorIndex.SYMMETRIC_KEY)]);
const hash = sha256(secretBuffer);
return hash;
Expand All @@ -33,35 +33,37 @@ export function deriveAESSecret(ecdhPubKey: PublicKey, ecdhPrivKey: GrumpkinPriv
* with the provided curve instance for elliptic curve operations.
*
* @param data - The data buffer to be encrypted.
* @param ownerPubKey - The owner's public key as a PublicKey instance.
* @param ephPrivKey - The ephemeral private key as a Buffer instance.
* @param ephSecretKey - The ephemeral secret key..
* @param incomingViewingPublicKey - The note owner's incoming viewing public key.
* @returns A Buffer containing the encrypted data and the ephemeral public key.
*/
export function encryptBuffer(data: Buffer, ownerPubKey: PublicKey, ephPrivKey: GrumpkinPrivateKey): Buffer {
const aesSecret = deriveAESSecret(ownerPubKey, ephPrivKey);
export function encryptBuffer(
data: Buffer,
ephSecretKey: GrumpkinPrivateKey,
incomingViewingPublicKey: PublicKey,
): Buffer {
const aesSecret = deriveAESSecret(ephSecretKey, incomingViewingPublicKey);
const aesKey = aesSecret.subarray(0, 16);
const iv = aesSecret.subarray(16, 32);
const cipher = createCipheriv('aes-128-cbc', aesKey, iv);
const plaintext = Buffer.concat([iv.subarray(0, 8), data]);
const curve = new Grumpkin();
const ephPubKey = curve.mul(curve.generator(), ephPrivKey);
const ephPubKey = curve.mul(curve.generator(), ephSecretKey);

return Buffer.concat([cipher.update(plaintext), cipher.final(), ephPubKey.toBuffer()]);
}

/**
* Decrypts the given encrypted data buffer using the owner's private key and a Grumpkin curve.
* Extracts the ephemeral public key from the input data, derives the AES secret using
* the owner's private key, and decrypts the plaintext.
* If the decryption is successful, returns the decrypted plaintext, otherwise returns undefined.
*
* Decrypts the given encrypted data buffer using the provided secret key.
* @param data - The encrypted data buffer to be decrypted.
* @param ownerPrivKey - The private key of the owner used for decryption.
* @param incomingViewingSecretKey - The secret key used for decryption.
* @returns The decrypted plaintext as a Buffer or undefined if decryption fails.
*/
export function decryptBuffer(data: Buffer, ownerPrivKey: GrumpkinPrivateKey): Buffer | undefined {
export function decryptBuffer(data: Buffer, incomingViewingSecretKey: GrumpkinPrivateKey): Buffer | undefined {
// Extract the ephemeral public key from the end of the data
const ephPubKey = Point.fromBuffer(data.subarray(-64));
const aesSecret = deriveAESSecret(ephPubKey, ownerPrivKey);
// Derive the AES secret key using the secret key and the ephemeral public key
const aesSecret = deriveAESSecret(incomingViewingSecretKey, ephPubKey);
const aesKey = aesSecret.subarray(0, 16);
const iv = aesSecret.subarray(16, 32);
const cipher = createDecipheriv('aes-128-cbc', aesKey, iv);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,22 @@ export class L1NotePayload {

/**
* Encrypt the L1NotePayload object using the owner's public key and the ephemeral private key.
* @param ownerPubKey - Public key of the owner of the L1NotePayload object.
* @param incomingViewingPubKey - Public key of the owner of the L1NotePayload object.
* @returns The encrypted L1NotePayload object.
*/
public toEncryptedBuffer(ownerPubKey: PublicKey): Buffer {
const ephPrivKey: GrumpkinPrivateKey = GrumpkinScalar.random();
return encryptBuffer(this.toBuffer(), ownerPubKey, ephPrivKey);
public toEncryptedBuffer(incomingViewingPubKey: PublicKey): Buffer {
const ephSecretKey: GrumpkinPrivateKey = GrumpkinScalar.random();
return encryptBuffer(this.toBuffer(), ephSecretKey, incomingViewingPubKey);
}

/**
* Decrypts the L1NotePayload object using the owner's private key.
* Decrypts the L1NotePayload object using the owner's incoming viewing secret key.
* @param data - Encrypted L1NotePayload object.
* @param ownerPrivKey - Private key of the owner of the L1NotePayload object.
* @param incomingViewingSecretKey - Incoming viewing secret key of the owner of the L1NotePayload object.
* @returns Instance of L1NotePayload if the decryption was successful, undefined otherwise.
*/
static fromEncryptedBuffer(data: Buffer, ownerPrivKey: GrumpkinPrivateKey): L1NotePayload | undefined {
const buf = decryptBuffer(data, ownerPrivKey);
static fromEncryptedBuffer(data: Buffer, incomingViewingSecretKey: GrumpkinPrivateKey): L1NotePayload | undefined {
const buf = decryptBuffer(data, incomingViewingSecretKey);
if (!buf) {
return;
}
Expand Down

0 comments on commit aa6a24e

Please sign in to comment.