From 72b3d2949b552bf49a347b287ac8d5505de0f26e Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 11 Apr 2024 13:55:55 +0000 Subject: [PATCH 1/7] WIP --- .../crates/types/src/constants.nr | 7 +++- yarn-project/circuit-types/src/keys/index.ts | 1 + .../circuit-types/src/keys/key_store.ts | 1 + .../circuit-types/src/keys/new_key_store.ts | 36 +++++++++++++++++ yarn-project/circuits.js/src/constants.gen.ts | 4 ++ .../key-store/src/new_test_key_store.ts | 40 +++++++++++++++++++ yarn-project/key-store/src/test_key_store.ts | 1 + 7 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 yarn-project/circuit-types/src/keys/new_key_store.ts create mode 100644 yarn-project/key-store/src/new_test_key_store.ts diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index a25b45b2226..31f453742a4 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -193,8 +193,6 @@ global NUM_BASE_PARITY_PER_ROOT_PARITY: u64 = 4; * | MID | 8 < n ≤ 16 | 32 < hash_index ≤ 40 | * | HIGH | 16 < n ≤ 48 | 40 < hash_index ≤ 48 | * +-----------+-------------------------------+----------------------+ - * - * Note: When modifying, modify `GeneratorIndexPacker` in packer.hpp accordingly. */ // Indices with size ≤ 8 global GENERATOR_INDEX__NOTE_HASH = 1; @@ -238,3 +236,8 @@ global GENERATOR_INDEX__PUBLIC_CIRCUIT_PUBLIC_INPUTS = 43; global GENERATOR_INDEX__FUNCTION_ARGS = 44; global GENERATOR_INDEX__AUTHWIT_INNER = 45; global GENERATOR_INDEX__AUTHWIT_OUTER = 46; +// Key related generators follow +global GENERATOR_INDEX__NSK_M = 47; +global GENERATOR_INDEX__IVSK_M = 48; +global GENERATOR_INDEX__OVSK_M = 49; +global GENERATOR_INDEX__TSK_M = 50; diff --git a/yarn-project/circuit-types/src/keys/index.ts b/yarn-project/circuit-types/src/keys/index.ts index f137b0d567a..c770f679814 100644 --- a/yarn-project/circuit-types/src/keys/index.ts +++ b/yarn-project/circuit-types/src/keys/index.ts @@ -1,2 +1,3 @@ export * from './key_pair.js'; export * from './key_store.js'; +export * from './new_key_store.js'; \ No newline at end of file diff --git a/yarn-project/circuit-types/src/keys/key_store.ts b/yarn-project/circuit-types/src/keys/key_store.ts index 4869503def4..3cf2c960d59 100644 --- a/yarn-project/circuit-types/src/keys/key_store.ts +++ b/yarn-project/circuit-types/src/keys/key_store.ts @@ -3,6 +3,7 @@ import { type AztecAddress, type GrumpkinPrivateKey, type PublicKey } from '@azt /** * Represents a secure storage for managing keys. * Provides functionality to create and retrieve accounts, private and public keys, + * TODO(#5627): 💣💣💣 */ export interface KeyStore { /** diff --git a/yarn-project/circuit-types/src/keys/new_key_store.ts b/yarn-project/circuit-types/src/keys/new_key_store.ts new file mode 100644 index 00000000000..861072b8147 --- /dev/null +++ b/yarn-project/circuit-types/src/keys/new_key_store.ts @@ -0,0 +1,36 @@ +import { AztecAddress, Fr, PartialAddress, type PublicKey } from '@aztec/circuits.js'; + +/** + * Represents a secure storage for managing keys. + */ +export interface NewKeyStore { + /** + * Retrieves the master nullifier public key. + * @returns A Promise that resolves to the master nullifier public key. + */ + getMasterNullifierPublicKey(): Promise; + + /** + * Retrieves the master incoming viewing key. + * @returns A Promise that resolves to the master incoming viewing key. + */ + getMasterIncomingViewingPublicKey(): Promise; + + /** + * Retrieves the master outgoing viewing key. + * @returns A Promise that resolves to the master outgoing viewing key. + */ + getMasterOutgoingViewingPublicKey(): Promise; + + /** + * Retrieves the master tagging key. + * @returns A Promise that resolves to the master tagging key. + */ + getMasterTaggingPublicKey(): Promise; + + /** + * Retrieves the hash of the public keys. + * @returns A Promise that resolves to the hash of the public keys. + */ + getPublicKeysHash(): Promise; +} diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 5a8cb29928c..c5608cb77a2 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -152,4 +152,8 @@ export enum GeneratorIndex { FUNCTION_ARGS = 44, AUTHWIT_INNER = 45, AUTHWIT_OUTER = 46, + NSK_M = 47, + IVSK_M = 48, + OVSK_M = 49, + TSK_M = 50, } diff --git a/yarn-project/key-store/src/new_test_key_store.ts b/yarn-project/key-store/src/new_test_key_store.ts new file mode 100644 index 00000000000..14476d6084b --- /dev/null +++ b/yarn-project/key-store/src/new_test_key_store.ts @@ -0,0 +1,40 @@ +import { type NewKeyStore, type KeyPair, type KeyStore, type PublicKey } from '@aztec/circuit-types'; +import { + type AztecAddress, + type GrumpkinPrivateKey, + GrumpkinScalar, + Point, + computeNullifierSecretKey, + computeSiloedNullifierSecretKey, + derivePublicKey, + type Fr, + GeneratorIndex, + Fq, +} from '@aztec/circuits.js'; +import { type Grumpkin } from '@aztec/circuits.js/barretenberg'; +import { type AztecKVStore, type AztecMap } from '@aztec/kv-store'; + +import { ConstantKeyPair } from './key_pair.js'; +import { poseidonHash } from '@aztec/foundation/crypto'; +/** + * TestKeyStore is an implementation of the KeyStore interface, used for managing key pairs in a testing environment. + * It should be utilized in testing scenarios where secure key management is not required, and ease-of-use is prioritized. + * TODO: Potentially rename to not include 'Test' in the name. + */ +export class NewTestKeyStore implements NewKeyStore { + #keys: AztecMap; + + constructor(private curve: Grumpkin, database: AztecKVStore) { + this.#keys = database.openMap('key_store'); + } + + public async addAccount(sk: Fr): Promise { + const masterNullifierSecretKey = poseidonHash([sk], GeneratorIndex.NSK_M); + // TODO: Is converting from Fr to Fq an issue? Fr.MODULUS is < Fq.MODULUS so it wont' throw but should we refactor this? + const masterNullifierPublicKey = this.curve.mul(this.curve.generator(), Fq.fromBuffer(masterNullifierSecretKey.toBuffer())) + } + + public async getMasterNullifierPublicKey(): Promise { + return poseidonHash() + } +} diff --git a/yarn-project/key-store/src/test_key_store.ts b/yarn-project/key-store/src/test_key_store.ts index a5bfe3144b3..4af7e2c2dfa 100644 --- a/yarn-project/key-store/src/test_key_store.ts +++ b/yarn-project/key-store/src/test_key_store.ts @@ -16,6 +16,7 @@ import { ConstantKeyPair } from './key_pair.js'; /** * TestKeyStore is an implementation of the KeyStore interface, used for managing key pairs in a testing environment. * It should be utilized in testing scenarios where secure key management is not required, and ease-of-use is prioritized. + * TODO(#5627): 💣💣💣 */ export class TestKeyStore implements KeyStore { #keys: AztecMap; From 347a00934ea22d39b6148c7a945840c89b1b0767 Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 11 Apr 2024 14:57:54 +0000 Subject: [PATCH 2/7] WIP --- .../crates/types/src/constants.nr | 2 + .../circuit-types/src/keys/new_key_store.ts | 32 ++--- yarn-project/circuits.js/src/constants.gen.ts | 2 + .../key-store/src/new_test_key_store.ts | 127 +++++++++++++++--- 4 files changed, 128 insertions(+), 35 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 31f453742a4..63b35b38f27 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -241,3 +241,5 @@ global GENERATOR_INDEX__NSK_M = 47; global GENERATOR_INDEX__IVSK_M = 48; global GENERATOR_INDEX__OVSK_M = 49; global GENERATOR_INDEX__TSK_M = 50; +global GENERATOR_INDEX__PUBLIC_KEYS_HASH = 51; +global GENERATOR_INDEX__CONTRACT_ADDRESS_V1 = 52; diff --git a/yarn-project/circuit-types/src/keys/new_key_store.ts b/yarn-project/circuit-types/src/keys/new_key_store.ts index 861072b8147..faaf018b833 100644 --- a/yarn-project/circuit-types/src/keys/new_key_store.ts +++ b/yarn-project/circuit-types/src/keys/new_key_store.ts @@ -1,36 +1,38 @@ -import { AztecAddress, Fr, PartialAddress, type PublicKey } from '@aztec/circuits.js'; +import { type AztecAddress, Fr, PartialAddress, type PublicKey } from '@aztec/circuits.js'; /** * Represents a secure storage for managing keys. */ export interface NewKeyStore { /** - * Retrieves the master nullifier public key. - * @returns A Promise that resolves to the master nullifier public key. + * Gets the master nullifier public key for a given account. + * @throws If the account does not exist in the key store. + * @param account - The account address for which to retrieve the master nullifier public key. + * @returns The master nullifier public key for the account. */ - getMasterNullifierPublicKey(): Promise; + getMasterNullifierPublicKey(account: AztecAddress): Promise; /** - * Retrieves the master incoming viewing key. - * @returns A Promise that resolves to the master incoming viewing key. + * Gets the master incoming viewing public key for a given account. + * @throws If the account does not exist in the key store. + * @param account - The account address for which to retrieve the master incoming viewing public key. + * @returns The master incoming viewing public key for the account. */ - getMasterIncomingViewingPublicKey(): Promise; + getMasterIncomingViewingPublicKey(account: AztecAddress): Promise; /** * Retrieves the master outgoing viewing key. + * @throws If the account does not exist in the key store. + * @param account - The account to retrieve the master outgoing viewing key for. * @returns A Promise that resolves to the master outgoing viewing key. */ - getMasterOutgoingViewingPublicKey(): Promise; + getMasterOutgoingViewingPublicKey(account: AztecAddress): Promise; /** * Retrieves the master tagging key. + * @throws If the account does not exist in the key store. + * @param account - The account to retrieve the master tagging key for. * @returns A Promise that resolves to the master tagging key. */ - getMasterTaggingPublicKey(): Promise; - - /** - * Retrieves the hash of the public keys. - * @returns A Promise that resolves to the hash of the public keys. - */ - getPublicKeysHash(): Promise; + getMasterTaggingPublicKey(account: AztecAddress): Promise; } diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index c5608cb77a2..253c792aab9 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -156,4 +156,6 @@ export enum GeneratorIndex { IVSK_M = 48, OVSK_M = 49, TSK_M = 50, + PUBLIC_KEYS_HASH = 51, + CONTRACT_ADDRESS_V1 = 52, } diff --git a/yarn-project/key-store/src/new_test_key_store.ts b/yarn-project/key-store/src/new_test_key_store.ts index 14476d6084b..784410316f9 100644 --- a/yarn-project/key-store/src/new_test_key_store.ts +++ b/yarn-project/key-store/src/new_test_key_store.ts @@ -1,21 +1,9 @@ -import { type NewKeyStore, type KeyPair, type KeyStore, type PublicKey } from '@aztec/circuit-types'; -import { - type AztecAddress, - type GrumpkinPrivateKey, - GrumpkinScalar, - Point, - computeNullifierSecretKey, - computeSiloedNullifierSecretKey, - derivePublicKey, - type Fr, - GeneratorIndex, - Fq, -} from '@aztec/circuits.js'; +import { type NewKeyStore, type PublicKey } from '@aztec/circuit-types'; +import { AztecAddress, Fq, type Fr, GeneratorIndex, type PartialAddress, Point } from '@aztec/circuits.js'; import { type Grumpkin } from '@aztec/circuits.js/barretenberg'; +import { poseidonHash } from '@aztec/foundation/crypto'; import { type AztecKVStore, type AztecMap } from '@aztec/kv-store'; -import { ConstantKeyPair } from './key_pair.js'; -import { poseidonHash } from '@aztec/foundation/crypto'; /** * TestKeyStore is an implementation of the KeyStore interface, used for managing key pairs in a testing environment. * It should be utilized in testing scenarios where secure key management is not required, and ease-of-use is prioritized. @@ -28,13 +16,112 @@ export class NewTestKeyStore implements NewKeyStore { this.#keys = database.openMap('key_store'); } - public async addAccount(sk: Fr): Promise { + public async addAccount(sk: Fr, partialAddress: PartialAddress): Promise { + // First we derive the master secret keys const masterNullifierSecretKey = poseidonHash([sk], GeneratorIndex.NSK_M); - // TODO: Is converting from Fr to Fq an issue? Fr.MODULUS is < Fq.MODULUS so it wont' throw but should we refactor this? - const masterNullifierPublicKey = this.curve.mul(this.curve.generator(), Fq.fromBuffer(masterNullifierSecretKey.toBuffer())) + const masterIncomingViewingSecretKey = poseidonHash([sk], GeneratorIndex.IVSK_M); + const masterOutgoingViewingSecretKey = poseidonHash([sk], GeneratorIndex.OVSK_M); + const masterTaggingSecretKey = poseidonHash([sk], GeneratorIndex.TSK_M); + + // Then we derive the master public keys + // TODO: Is converting from Fr to Fq bellow an issue? Fr.MODULUS is < Fq.MODULUS so it shouldn't but should we refactor this anyway? + const masterNullifierPublicKey = this.curve.mul( + this.curve.generator(), + Fq.fromBuffer(masterNullifierSecretKey.toBuffer()), + ); + const masterIncomingViewingPublicKey = this.curve.mul( + this.curve.generator(), + Fq.fromBuffer(masterIncomingViewingSecretKey.toBuffer()), + ); + const masterOutgoingViewingPublicKey = this.curve.mul( + this.curve.generator(), + Fq.fromBuffer(masterOutgoingViewingSecretKey.toBuffer()), + ); + const masterTaggingPublicKey = this.curve.mul( + this.curve.generator(), + Fq.fromBuffer(masterTaggingSecretKey.toBuffer()), + ); + + // We hash the public keys to get the public keys hash + const publicKeysHash = poseidonHash( + [ + masterNullifierPublicKey, + masterIncomingViewingPublicKey, + masterOutgoingViewingPublicKey, + masterTaggingPublicKey, + ], + GeneratorIndex.PUBLIC_KEYS_HASH, + ); + + // We hash the partial address and the public keys hash to get the account address + // TODO: Should GeneratorIndex.CONTRACT_ADDRESS be removed given that we introduced CONTRACT_ADDRESS_V1? + // TODO: Move the following line to AztecAddress class? + const accountAddressFr = poseidonHash([partialAddress, publicKeysHash], GeneratorIndex.CONTRACT_ADDRESS_V1); + const accountAddress = AztecAddress.fromField(accountAddressFr); + + // We store the keys in the database + await this.#keys.set(`${accountAddress.toString()}-npk_m`, masterNullifierPublicKey.toBuffer()); + await this.#keys.set(`${accountAddress.toString()}-ivpk_m`, masterIncomingViewingPublicKey.toBuffer()); + await this.#keys.set(`${accountAddress.toString()}-ovpk_m`, masterOutgoingViewingPublicKey.toBuffer()); + await this.#keys.set(`${accountAddress.toString()}-tpk_m`, masterTaggingPublicKey.toBuffer()); + + // At last, we return the newly derived account address + return Promise.resolve(accountAddress); + } + + /** + * Gets the master nullifier public key for a given account. + * @throws If the account does not exist in the key store. + * @param account - The account address for which to retrieve the master nullifier public key. + * @returns The master nullifier public key for the account. + */ + public getMasterNullifierPublicKey(account: AztecAddress): Promise { + const masterNullifierPublicKeyBuffer = this.#keys.get(`${account.toString()}-npk_m`); + if (!masterNullifierPublicKeyBuffer) { + throw new Error(`Account ${account.toString()} does not exist.`); + } + return Promise.resolve(Point.fromBuffer(masterNullifierPublicKeyBuffer)); + } + + /** + * Gets the master incoming viewing public key for a given account. + * @throws If the account does not exist in the key store. + * @param account - The account address for which to retrieve the master incoming viewing public key. + * @returns The master incoming viewing public key for the account. + */ + public getMasterIncomingViewingPublicKey(account: AztecAddress): Promise { + const masterIncomingViewingPublicKeyBuffer = this.#keys.get(`${account.toString()}-ivpk_m`); + if (!masterIncomingViewingPublicKeyBuffer) { + throw new Error(`Account ${account.toString()} does not exist.`); + } + return Promise.resolve(Point.fromBuffer(masterIncomingViewingPublicKeyBuffer)); + } + + /** + * Retrieves the master outgoing viewing public key. + * @throws If the account does not exist in the key store. + * @param account - The account to retrieve the master outgoing viewing key for. + * @returns A Promise that resolves to the master outgoing viewing key. + */ + public getMasterOutgoingViewingPublicKey(account: AztecAddress): Promise { + const masterOutgoingViewingPublicKeyBuffer = this.#keys.get(`${account.toString()}-ovpk_m`); + if (!masterOutgoingViewingPublicKeyBuffer) { + throw new Error(`Account ${account.toString()} does not exist.`); + } + return Promise.resolve(Point.fromBuffer(masterOutgoingViewingPublicKeyBuffer)); } - public async getMasterNullifierPublicKey(): Promise { - return poseidonHash() + /** + * Retrieves the master tagging public key. + * @throws If the account does not exist in the key store. + * @param account - The account to retrieve the master tagging key for. + * @returns A Promise that resolves to the master tagging key. + */ + public getMasterTaggingPublicKey(account: AztecAddress): Promise { + const masterTaggingPublicKeyBuffer = this.#keys.get(`${account.toString()}-tpk_m`); + if (!masterTaggingPublicKeyBuffer) { + throw new Error(`Account ${account.toString()} does not exist.`); + } + return Promise.resolve(Point.fromBuffer(masterTaggingPublicKeyBuffer)); } } From 98a763b24e3fee64faa939553322d28bfd40df03 Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 11 Apr 2024 15:14:33 +0000 Subject: [PATCH 3/7] WIP --- yarn-project/circuit-types/src/keys/index.ts | 2 +- .../circuit-types/src/keys/new_key_store.ts | 16 +++++++++++++- .../key-store/src/new_test_key_store.test.ts | 18 +++++++++++++++ .../key-store/src/new_test_key_store.ts | 22 ++++++++++++++++--- 4 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 yarn-project/key-store/src/new_test_key_store.test.ts diff --git a/yarn-project/circuit-types/src/keys/index.ts b/yarn-project/circuit-types/src/keys/index.ts index c770f679814..be16aca18e4 100644 --- a/yarn-project/circuit-types/src/keys/index.ts +++ b/yarn-project/circuit-types/src/keys/index.ts @@ -1,3 +1,3 @@ export * from './key_pair.js'; export * from './key_store.js'; -export * from './new_key_store.js'; \ No newline at end of file +export * from './new_key_store.js'; diff --git a/yarn-project/circuit-types/src/keys/new_key_store.ts b/yarn-project/circuit-types/src/keys/new_key_store.ts index faaf018b833..6ae9337cc18 100644 --- a/yarn-project/circuit-types/src/keys/new_key_store.ts +++ b/yarn-project/circuit-types/src/keys/new_key_store.ts @@ -1,9 +1,23 @@ -import { type AztecAddress, Fr, PartialAddress, type PublicKey } from '@aztec/circuits.js'; +import { type AztecAddress, type Fr, type PartialAddress, type PublicKey } from '@aztec/circuits.js'; /** * Represents a secure storage for managing keys. */ export interface NewKeyStore { + /** + * Creates a new account from a randomly generated secret key. + * @returns A promise that resolves to the newly created account's AztecAddress. + */ + createAccount(): Promise; + + /** + * Adds an account to the key store from the provided secret key. + * @param sk - The secret key of the account. + * @param partialAddress - The partial address of the account. + * @returns The account's address. + */ + addAccount(sk: Fr, partialAddress: PartialAddress): Promise; + /** * Gets the master nullifier public key for a given account. * @throws If the account does not exist in the key store. diff --git a/yarn-project/key-store/src/new_test_key_store.test.ts b/yarn-project/key-store/src/new_test_key_store.test.ts new file mode 100644 index 00000000000..6bc488e2764 --- /dev/null +++ b/yarn-project/key-store/src/new_test_key_store.test.ts @@ -0,0 +1,18 @@ +import { Fr } from "@aztec/circuits.js"; +import { NewTestKeyStore } from "./new_test_key_store.js"; +import { openTmpStore } from "@aztec/kv-store/utils"; +import { Grumpkin } from "@aztec/circuits.js/barretenberg"; + +describe('NewTestKeyStore', () => { + it('Adds account and returns keys', () => { + const db = openTmpStore(); + const keyStore = new NewTestKeyStore(new Grumpkin(), db); + + // Arbitrary fixed values + const sk = new Fr(8923n); + const partialAddress = new Fr(243523n); + + const accountAddress = keyStore.addAccount(sk, partialAddress); + expect(accountAddress).toMatchInlineSnapshot(`"0071f7630d28ce02cc1ca8b15c44953f84a39e1478445395247ae04dfa213c0e"`); + }); +}); diff --git a/yarn-project/key-store/src/new_test_key_store.ts b/yarn-project/key-store/src/new_test_key_store.ts index 784410316f9..63a87773541 100644 --- a/yarn-project/key-store/src/new_test_key_store.ts +++ b/yarn-project/key-store/src/new_test_key_store.ts @@ -1,5 +1,5 @@ import { type NewKeyStore, type PublicKey } from '@aztec/circuit-types'; -import { AztecAddress, Fq, type Fr, GeneratorIndex, type PartialAddress, Point } from '@aztec/circuits.js'; +import { AztecAddress, Fq, Fr, GeneratorIndex, type PartialAddress, Point } from '@aztec/circuits.js'; import { type Grumpkin } from '@aztec/circuits.js/barretenberg'; import { poseidonHash } from '@aztec/foundation/crypto'; import { type AztecKVStore, type AztecMap } from '@aztec/kv-store'; @@ -16,14 +16,30 @@ export class NewTestKeyStore implements NewKeyStore { this.#keys = database.openMap('key_store'); } + /** + * Creates a new account from a randomly generated secret key. + * @returns A promise that resolves to the newly created account's AztecAddress. + */ + public createAccount(): Promise { + const sk = Fr.random(); + const partialAddress = Fr.random(); + return this.addAccount(sk, partialAddress); + } + + /** + * Adds an account to the key store from the provided secret key. + * @param sk - The secret key of the account. + * @param partialAddress - The partial address of the account. + * @returns The account's address. + */ public async addAccount(sk: Fr, partialAddress: PartialAddress): Promise { - // First we derive the master secret keys + // First we derive master secret keys const masterNullifierSecretKey = poseidonHash([sk], GeneratorIndex.NSK_M); const masterIncomingViewingSecretKey = poseidonHash([sk], GeneratorIndex.IVSK_M); const masterOutgoingViewingSecretKey = poseidonHash([sk], GeneratorIndex.OVSK_M); const masterTaggingSecretKey = poseidonHash([sk], GeneratorIndex.TSK_M); - // Then we derive the master public keys + // Then we derive master public keys // TODO: Is converting from Fr to Fq bellow an issue? Fr.MODULUS is < Fq.MODULUS so it shouldn't but should we refactor this anyway? const masterNullifierPublicKey = this.curve.mul( this.curve.generator(), From 99443e52c3e281b8fcf6bab7b6df1887317bce5a Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 12 Apr 2024 09:07:18 +0000 Subject: [PATCH 4/7] new keystore test --- .../key-store/src/new_test_key_store.test.ts | 40 +++++++++++++++---- .../key-store/src/new_test_key_store.ts | 1 - 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/yarn-project/key-store/src/new_test_key_store.test.ts b/yarn-project/key-store/src/new_test_key_store.test.ts index 6bc488e2764..2f60f964045 100644 --- a/yarn-project/key-store/src/new_test_key_store.test.ts +++ b/yarn-project/key-store/src/new_test_key_store.test.ts @@ -1,18 +1,42 @@ -import { Fr } from "@aztec/circuits.js"; -import { NewTestKeyStore } from "./new_test_key_store.js"; -import { openTmpStore } from "@aztec/kv-store/utils"; -import { Grumpkin } from "@aztec/circuits.js/barretenberg"; +import { Fr } from '@aztec/circuits.js'; +import { Grumpkin } from '@aztec/circuits.js/barretenberg'; +import { openTmpStore } from '@aztec/kv-store/utils'; + +import { NewTestKeyStore } from './new_test_key_store.js'; describe('NewTestKeyStore', () => { - it('Adds account and returns keys', () => { + it('Adds account and returns keys', async () => { const db = openTmpStore(); const keyStore = new NewTestKeyStore(new Grumpkin(), db); // Arbitrary fixed values const sk = new Fr(8923n); const partialAddress = new Fr(243523n); - - const accountAddress = keyStore.addAccount(sk, partialAddress); - expect(accountAddress).toMatchInlineSnapshot(`"0071f7630d28ce02cc1ca8b15c44953f84a39e1478445395247ae04dfa213c0e"`); + + const accountAddress = await keyStore.addAccount(sk, partialAddress); + expect(accountAddress.toString()).toMatchInlineSnapshot( + `"0x2ae5eeea29e4059842653d97864456c28fa53e4a823a8df65802090de1e85baa"`, + ); + + // TODO(#5714): The keys are currently the same here because separator is currently ignored in poseidon + const masterNullifierPublicKey = await keyStore.getMasterNullifierPublicKey(accountAddress); + expect(masterNullifierPublicKey.toString()).toMatchInlineSnapshot( + `"0x1b0b998b70b295ed14912584c64abfd402ee13511d0dcf05badef38e8c10acd00fce0a5909d612c9a2d2c9172ff3cf5ba6be3e314d66b05edd74f3d5d259110f"`, + ); + + const masterIncomingViewingPublicKey = await keyStore.getMasterIncomingViewingPublicKey(accountAddress); + expect(masterIncomingViewingPublicKey.toString()).toMatchInlineSnapshot( + `"0x1b0b998b70b295ed14912584c64abfd402ee13511d0dcf05badef38e8c10acd00fce0a5909d612c9a2d2c9172ff3cf5ba6be3e314d66b05edd74f3d5d259110f"`, + ); + + const masterOutgoingViewingPublicKey = await keyStore.getMasterOutgoingViewingPublicKey(accountAddress); + expect(masterOutgoingViewingPublicKey.toString()).toMatchInlineSnapshot( + `"0x1b0b998b70b295ed14912584c64abfd402ee13511d0dcf05badef38e8c10acd00fce0a5909d612c9a2d2c9172ff3cf5ba6be3e314d66b05edd74f3d5d259110f"`, + ); + + const masterTaggingPublicKey = await keyStore.getMasterTaggingPublicKey(accountAddress); + expect(masterTaggingPublicKey.toString()).toMatchInlineSnapshot( + `"0x1b0b998b70b295ed14912584c64abfd402ee13511d0dcf05badef38e8c10acd00fce0a5909d612c9a2d2c9172ff3cf5ba6be3e314d66b05edd74f3d5d259110f"`, + ); }); }); diff --git a/yarn-project/key-store/src/new_test_key_store.ts b/yarn-project/key-store/src/new_test_key_store.ts index 63a87773541..d6737153dfd 100644 --- a/yarn-project/key-store/src/new_test_key_store.ts +++ b/yarn-project/key-store/src/new_test_key_store.ts @@ -7,7 +7,6 @@ import { type AztecKVStore, type AztecMap } from '@aztec/kv-store'; /** * TestKeyStore is an implementation of the KeyStore interface, used for managing key pairs in a testing environment. * It should be utilized in testing scenarios where secure key management is not required, and ease-of-use is prioritized. - * TODO: Potentially rename to not include 'Test' in the name. */ export class NewTestKeyStore implements NewKeyStore { #keys: AztecMap; From ed08aceea599a680a9e57e14948086e425fc755c Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 12 Apr 2024 11:19:01 +0000 Subject: [PATCH 5/7] using sha512 after feedback from crypto team --- yarn-project/foundation/src/crypto/sha256/index.ts | 12 ++++++++++++ .../key-store/src/new_test_key_store.test.ts | 10 +++++----- yarn-project/key-store/src/new_test_key_store.ts | 13 +++++++------ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/yarn-project/foundation/src/crypto/sha256/index.ts b/yarn-project/foundation/src/crypto/sha256/index.ts index 1ff89efda47..0cbea7f96d5 100644 --- a/yarn-project/foundation/src/crypto/sha256/index.ts +++ b/yarn-project/foundation/src/crypto/sha256/index.ts @@ -12,3 +12,15 @@ export const sha256ToField = (data: Bufferable[]) => { const buffer = serializeToBuffer(data); return Fr.fromBuffer(sha256Trunc(buffer)); }; + +export const sha512 = (data: Buffer) => Buffer.from(hash.sha512().update(data).digest()); + +/** + * @dev We don't truncate in this function (unlike in sha256ToField) because this function is used in situations where + * we don't care only about collision resistance but we need the output to be uniformly distributed as well. This is + * because we use it as a pseudo-random function. + */ +export const sha512ToField = (data: Bufferable[]) => { + const buffer = serializeToBuffer(data); + return Fr.fromBufferReduce(sha512(buffer)); +}; diff --git a/yarn-project/key-store/src/new_test_key_store.test.ts b/yarn-project/key-store/src/new_test_key_store.test.ts index 2f60f964045..e9fe13923d0 100644 --- a/yarn-project/key-store/src/new_test_key_store.test.ts +++ b/yarn-project/key-store/src/new_test_key_store.test.ts @@ -15,28 +15,28 @@ describe('NewTestKeyStore', () => { const accountAddress = await keyStore.addAccount(sk, partialAddress); expect(accountAddress.toString()).toMatchInlineSnapshot( - `"0x2ae5eeea29e4059842653d97864456c28fa53e4a823a8df65802090de1e85baa"`, + `"0x2be4a07a3c5b5d746cb3c312bce46ee844748039b5e772ddb8d0e961f1512bb9"`, ); // TODO(#5714): The keys are currently the same here because separator is currently ignored in poseidon const masterNullifierPublicKey = await keyStore.getMasterNullifierPublicKey(accountAddress); expect(masterNullifierPublicKey.toString()).toMatchInlineSnapshot( - `"0x1b0b998b70b295ed14912584c64abfd402ee13511d0dcf05badef38e8c10acd00fce0a5909d612c9a2d2c9172ff3cf5ba6be3e314d66b05edd74f3d5d259110f"`, + `"0x03e81abc4e901640f7e3a2ad2058c94f17985bbb482774e9ec2c047c21ff25f30b7997c999ace8289fe5595cf0df6a038b73e3955241d6240263f73b51401911"`, ); const masterIncomingViewingPublicKey = await keyStore.getMasterIncomingViewingPublicKey(accountAddress); expect(masterIncomingViewingPublicKey.toString()).toMatchInlineSnapshot( - `"0x1b0b998b70b295ed14912584c64abfd402ee13511d0dcf05badef38e8c10acd00fce0a5909d612c9a2d2c9172ff3cf5ba6be3e314d66b05edd74f3d5d259110f"`, + `"0x09c762a9e8da1471ca67eb9e150398cc8406aee86f397f842f6ef10a7a0fda32239588ed8e880e81000efd81c7a856ba063cdfaa6212e3a512ad59bff163c619"`, ); const masterOutgoingViewingPublicKey = await keyStore.getMasterOutgoingViewingPublicKey(accountAddress); expect(masterOutgoingViewingPublicKey.toString()).toMatchInlineSnapshot( - `"0x1b0b998b70b295ed14912584c64abfd402ee13511d0dcf05badef38e8c10acd00fce0a5909d612c9a2d2c9172ff3cf5ba6be3e314d66b05edd74f3d5d259110f"`, + `"0x282f7a4242121b26a16a72228593db50de79bb312ce0825657a175ca8e7802100b161ab6f43d98fe6ecafaeeeeef8d1c0d77b220d5eb92d2bb2aee50b7558940"`, ); const masterTaggingPublicKey = await keyStore.getMasterTaggingPublicKey(accountAddress); expect(masterTaggingPublicKey.toString()).toMatchInlineSnapshot( - `"0x1b0b998b70b295ed14912584c64abfd402ee13511d0dcf05badef38e8c10acd00fce0a5909d612c9a2d2c9172ff3cf5ba6be3e314d66b05edd74f3d5d259110f"`, + `"0x14348f7ca16a769fe76dfbeef2812a6b788b94952a39fcf10f78114a2a85c3e517387463d0b7fad7aac6f9f970a5533d919fbda66b537b7ce4544f13d497ffad"`, ); }); }); diff --git a/yarn-project/key-store/src/new_test_key_store.ts b/yarn-project/key-store/src/new_test_key_store.ts index d6737153dfd..41c30eccf84 100644 --- a/yarn-project/key-store/src/new_test_key_store.ts +++ b/yarn-project/key-store/src/new_test_key_store.ts @@ -1,7 +1,7 @@ import { type NewKeyStore, type PublicKey } from '@aztec/circuit-types'; import { AztecAddress, Fq, Fr, GeneratorIndex, type PartialAddress, Point } from '@aztec/circuits.js'; import { type Grumpkin } from '@aztec/circuits.js/barretenberg'; -import { poseidonHash } from '@aztec/foundation/crypto'; +import { poseidonHash, sha512ToField } from '@aztec/foundation/crypto'; import { type AztecKVStore, type AztecMap } from '@aztec/kv-store'; /** @@ -32,11 +32,12 @@ export class NewTestKeyStore implements NewKeyStore { * @returns The account's address. */ public async addAccount(sk: Fr, partialAddress: PartialAddress): Promise { - // First we derive master secret keys - const masterNullifierSecretKey = poseidonHash([sk], GeneratorIndex.NSK_M); - const masterIncomingViewingSecretKey = poseidonHash([sk], GeneratorIndex.IVSK_M); - const masterOutgoingViewingSecretKey = poseidonHash([sk], GeneratorIndex.OVSK_M); - const masterTaggingSecretKey = poseidonHash([sk], GeneratorIndex.TSK_M); + // First we derive master secret keys - we use sha512 here because this derivation will never take place + // in a circuit + const masterNullifierSecretKey = sha512ToField([sk, GeneratorIndex.NSK_M]); + const masterIncomingViewingSecretKey = sha512ToField([sk, GeneratorIndex.IVSK_M]); + const masterOutgoingViewingSecretKey = sha512ToField([sk, GeneratorIndex.OVSK_M]); + const masterTaggingSecretKey = sha512ToField([sk, GeneratorIndex.TSK_M]); // Then we derive master public keys // TODO: Is converting from Fr to Fq bellow an issue? Fr.MODULUS is < Fq.MODULUS so it shouldn't but should we refactor this anyway? From 5f40a71317ebbc572c39dd076854b91635df4f59 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 12 Apr 2024 12:33:45 +0000 Subject: [PATCH 6/7] reducing sha512 to correct field --- yarn-project/foundation/src/crypto/index.ts | 1 + .../foundation/src/crypto/sha256/index.ts | 12 ------- .../foundation/src/crypto/sha512/index.ts | 16 +++++++++ .../key-store/src/new_test_key_store.test.ts | 10 +++--- .../key-store/src/new_test_key_store.ts | 33 ++++++------------- 5 files changed, 32 insertions(+), 40 deletions(-) create mode 100644 yarn-project/foundation/src/crypto/sha512/index.ts diff --git a/yarn-project/foundation/src/crypto/index.ts b/yarn-project/foundation/src/crypto/index.ts index 96f4df584ef..96365958e92 100644 --- a/yarn-project/foundation/src/crypto/index.ts +++ b/yarn-project/foundation/src/crypto/index.ts @@ -3,6 +3,7 @@ import { BarretenbergSync } from '@aztec/bb.js'; export * from './keccak/index.js'; export * from './random/index.js'; export * from './sha256/index.js'; +export * from './sha512/index.js'; export * from './pedersen/index.js'; export * from './poseidon/index.js'; diff --git a/yarn-project/foundation/src/crypto/sha256/index.ts b/yarn-project/foundation/src/crypto/sha256/index.ts index 0cbea7f96d5..1ff89efda47 100644 --- a/yarn-project/foundation/src/crypto/sha256/index.ts +++ b/yarn-project/foundation/src/crypto/sha256/index.ts @@ -12,15 +12,3 @@ export const sha256ToField = (data: Bufferable[]) => { const buffer = serializeToBuffer(data); return Fr.fromBuffer(sha256Trunc(buffer)); }; - -export const sha512 = (data: Buffer) => Buffer.from(hash.sha512().update(data).digest()); - -/** - * @dev We don't truncate in this function (unlike in sha256ToField) because this function is used in situations where - * we don't care only about collision resistance but we need the output to be uniformly distributed as well. This is - * because we use it as a pseudo-random function. - */ -export const sha512ToField = (data: Bufferable[]) => { - const buffer = serializeToBuffer(data); - return Fr.fromBufferReduce(sha512(buffer)); -}; diff --git a/yarn-project/foundation/src/crypto/sha512/index.ts b/yarn-project/foundation/src/crypto/sha512/index.ts new file mode 100644 index 00000000000..d672c0be919 --- /dev/null +++ b/yarn-project/foundation/src/crypto/sha512/index.ts @@ -0,0 +1,16 @@ +import { default as hash } from 'hash.js'; + +import { GrumpkinScalar } from '../../fields/fields.js'; +import { type Bufferable, serializeToBuffer } from '../../serialize/serialize.js'; + +export const sha512 = (data: Buffer) => Buffer.from(hash.sha512().update(data).digest()); + +/** + * @dev We don't truncate in this function (unlike in sha256ToField) because this function is used in situations where + * we don't care only about collision resistance but we need the output to be uniformly distributed as well. This is + * because we use it as a pseudo-random function. + */ +export const sha512ToGrumpkinScalar = (data: Bufferable[]) => { + const buffer = serializeToBuffer(data); + return GrumpkinScalar.fromBufferReduce(sha512(buffer)); +}; diff --git a/yarn-project/key-store/src/new_test_key_store.test.ts b/yarn-project/key-store/src/new_test_key_store.test.ts index e9fe13923d0..4732e0c6ca0 100644 --- a/yarn-project/key-store/src/new_test_key_store.test.ts +++ b/yarn-project/key-store/src/new_test_key_store.test.ts @@ -15,28 +15,28 @@ describe('NewTestKeyStore', () => { const accountAddress = await keyStore.addAccount(sk, partialAddress); expect(accountAddress.toString()).toMatchInlineSnapshot( - `"0x2be4a07a3c5b5d746cb3c312bce46ee844748039b5e772ddb8d0e961f1512bb9"`, + `"0x2e34847ad9019320ac89a6ec9b42fec90f94ef4162fdfdd7f5b7668e32d82655"`, ); // TODO(#5714): The keys are currently the same here because separator is currently ignored in poseidon const masterNullifierPublicKey = await keyStore.getMasterNullifierPublicKey(accountAddress); expect(masterNullifierPublicKey.toString()).toMatchInlineSnapshot( - `"0x03e81abc4e901640f7e3a2ad2058c94f17985bbb482774e9ec2c047c21ff25f30b7997c999ace8289fe5595cf0df6a038b73e3955241d6240263f73b51401911"`, + `"0x2ef5d15dd65d29546680ab72846fb071f41cb9f2a0212215e6c560e29df4ff650ce764818364b376be92dc2f49577fe440e64a16012584f7c4ee94f7edbc323a"`, ); const masterIncomingViewingPublicKey = await keyStore.getMasterIncomingViewingPublicKey(accountAddress); expect(masterIncomingViewingPublicKey.toString()).toMatchInlineSnapshot( - `"0x09c762a9e8da1471ca67eb9e150398cc8406aee86f397f842f6ef10a7a0fda32239588ed8e880e81000efd81c7a856ba063cdfaa6212e3a512ad59bff163c619"`, + `"0x1c088f4e4a711f236a88b55da9ddf388de0bc00d56a5ceca96cea3a5cbe75bf32db0a333ba30c36b844d9fc6d2fb0de8d10e4371f0c5baebae452d90ff366798"`, ); const masterOutgoingViewingPublicKey = await keyStore.getMasterOutgoingViewingPublicKey(accountAddress); expect(masterOutgoingViewingPublicKey.toString()).toMatchInlineSnapshot( - `"0x282f7a4242121b26a16a72228593db50de79bb312ce0825657a175ca8e7802100b161ab6f43d98fe6ecafaeeeeef8d1c0d77b220d5eb92d2bb2aee50b7558940"`, + `"0x232d0b445d097fbc2046012c3fc474f6a9beef97eda1d8d1f2487dbe501ee1e70e8db9a824531a14e8717dee54cbb7abfec29a88c550a49617258bd6fd858242"`, ); const masterTaggingPublicKey = await keyStore.getMasterTaggingPublicKey(accountAddress); expect(masterTaggingPublicKey.toString()).toMatchInlineSnapshot( - `"0x14348f7ca16a769fe76dfbeef2812a6b788b94952a39fcf10f78114a2a85c3e517387463d0b7fad7aac6f9f970a5533d919fbda66b537b7ce4544f13d497ffad"`, + `"0x076429010fdebfa522b053267f654a4c5daf18589915d96f7e5001d63ea2033f27f915f254560c84450aa38e93c3162be52492d05b316e75f542e3b302117360"`, ); }); }); diff --git a/yarn-project/key-store/src/new_test_key_store.ts b/yarn-project/key-store/src/new_test_key_store.ts index 41c30eccf84..2a14a99d5b4 100644 --- a/yarn-project/key-store/src/new_test_key_store.ts +++ b/yarn-project/key-store/src/new_test_key_store.ts @@ -1,7 +1,7 @@ import { type NewKeyStore, type PublicKey } from '@aztec/circuit-types'; -import { AztecAddress, Fq, Fr, GeneratorIndex, type PartialAddress, Point } from '@aztec/circuits.js'; +import { AztecAddress, Fr, GeneratorIndex, type PartialAddress, Point } from '@aztec/circuits.js'; import { type Grumpkin } from '@aztec/circuits.js/barretenberg'; -import { poseidonHash, sha512ToField } from '@aztec/foundation/crypto'; +import { poseidonHash, sha512ToGrumpkinScalar } from '@aztec/foundation/crypto'; import { type AztecKVStore, type AztecMap } from '@aztec/kv-store'; /** @@ -34,29 +34,16 @@ export class NewTestKeyStore implements NewKeyStore { public async addAccount(sk: Fr, partialAddress: PartialAddress): Promise { // First we derive master secret keys - we use sha512 here because this derivation will never take place // in a circuit - const masterNullifierSecretKey = sha512ToField([sk, GeneratorIndex.NSK_M]); - const masterIncomingViewingSecretKey = sha512ToField([sk, GeneratorIndex.IVSK_M]); - const masterOutgoingViewingSecretKey = sha512ToField([sk, GeneratorIndex.OVSK_M]); - const masterTaggingSecretKey = sha512ToField([sk, GeneratorIndex.TSK_M]); + const masterNullifierSecretKey = sha512ToGrumpkinScalar([sk, GeneratorIndex.NSK_M]); + const masterIncomingViewingSecretKey = sha512ToGrumpkinScalar([sk, GeneratorIndex.IVSK_M]); + const masterOutgoingViewingSecretKey = sha512ToGrumpkinScalar([sk, GeneratorIndex.OVSK_M]); + const masterTaggingSecretKey = sha512ToGrumpkinScalar([sk, GeneratorIndex.TSK_M]); // Then we derive master public keys - // TODO: Is converting from Fr to Fq bellow an issue? Fr.MODULUS is < Fq.MODULUS so it shouldn't but should we refactor this anyway? - const masterNullifierPublicKey = this.curve.mul( - this.curve.generator(), - Fq.fromBuffer(masterNullifierSecretKey.toBuffer()), - ); - const masterIncomingViewingPublicKey = this.curve.mul( - this.curve.generator(), - Fq.fromBuffer(masterIncomingViewingSecretKey.toBuffer()), - ); - const masterOutgoingViewingPublicKey = this.curve.mul( - this.curve.generator(), - Fq.fromBuffer(masterOutgoingViewingSecretKey.toBuffer()), - ); - const masterTaggingPublicKey = this.curve.mul( - this.curve.generator(), - Fq.fromBuffer(masterTaggingSecretKey.toBuffer()), - ); + const masterNullifierPublicKey = this.curve.mul(this.curve.generator(), masterNullifierSecretKey); + const masterIncomingViewingPublicKey = this.curve.mul(this.curve.generator(), masterIncomingViewingSecretKey); + const masterOutgoingViewingPublicKey = this.curve.mul(this.curve.generator(), masterOutgoingViewingSecretKey); + const masterTaggingPublicKey = this.curve.mul(this.curve.generator(), masterTaggingSecretKey); // We hash the public keys to get the public keys hash const publicKeysHash = poseidonHash( From 62a6b963f86247cd50acc6791c1621481ebc4ca2 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 12 Apr 2024 13:12:02 +0000 Subject: [PATCH 7/7] fixes --- yarn-project/key-store/src/new_test_key_store.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn-project/key-store/src/new_test_key_store.ts b/yarn-project/key-store/src/new_test_key_store.ts index 2a14a99d5b4..5a48036d760 100644 --- a/yarn-project/key-store/src/new_test_key_store.ts +++ b/yarn-project/key-store/src/new_test_key_store.ts @@ -1,7 +1,7 @@ import { type NewKeyStore, type PublicKey } from '@aztec/circuit-types'; import { AztecAddress, Fr, GeneratorIndex, type PartialAddress, Point } from '@aztec/circuits.js'; import { type Grumpkin } from '@aztec/circuits.js/barretenberg'; -import { poseidonHash, sha512ToGrumpkinScalar } from '@aztec/foundation/crypto'; +import { poseidon2Hash, sha512ToGrumpkinScalar } from '@aztec/foundation/crypto'; import { type AztecKVStore, type AztecMap } from '@aztec/kv-store'; /** @@ -46,7 +46,7 @@ export class NewTestKeyStore implements NewKeyStore { const masterTaggingPublicKey = this.curve.mul(this.curve.generator(), masterTaggingSecretKey); // We hash the public keys to get the public keys hash - const publicKeysHash = poseidonHash( + const publicKeysHash = poseidon2Hash( [ masterNullifierPublicKey, masterIncomingViewingPublicKey, @@ -57,9 +57,9 @@ export class NewTestKeyStore implements NewKeyStore { ); // We hash the partial address and the public keys hash to get the account address - // TODO: Should GeneratorIndex.CONTRACT_ADDRESS be removed given that we introduced CONTRACT_ADDRESS_V1? - // TODO: Move the following line to AztecAddress class? - const accountAddressFr = poseidonHash([partialAddress, publicKeysHash], GeneratorIndex.CONTRACT_ADDRESS_V1); + // TODO(#5726): Should GeneratorIndex.CONTRACT_ADDRESS be removed given that we introduced CONTRACT_ADDRESS_V1? + // TODO(#5726): Move the following line to AztecAddress class? + const accountAddressFr = poseidon2Hash([partialAddress, publicKeysHash], GeneratorIndex.CONTRACT_ADDRESS_V1); const accountAddress = AztecAddress.fromField(accountAddressFr); // We store the keys in the database