@aztec/key-store 0.0.1-commit.f504929 → 0.0.1-commit.f5a9928

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ import type { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
2
+ import type { PublicKey } from '@aztec/stdlib/keys';
3
+ /**
4
+ * The four master privacy secret keys the key store holds for an account: the nullifier-hiding, incoming-viewing,
5
+ * outgoing-viewing, and tagging keys.
6
+ */
7
+ export type AccountPrivacySecretKeys = {
8
+ masterNullifierHidingSecretKey: GrumpkinScalar;
9
+ masterIncomingViewingSecretKey: GrumpkinScalar;
10
+ masterOutgoingViewingSecretKey: GrumpkinScalar;
11
+ masterTaggingSecretKey: GrumpkinScalar;
12
+ };
13
+ /**
14
+ * The keys needed to register an account: the four privacy secret keys the key store holds, plus the *public*
15
+ * message-signing and fallback keys. The message-signing and fallback secret keys are withheld from the key store (and
16
+ * hence from PXE, which embeds it), since it is not trusted to hold them: only their public keys are needed (to
17
+ * reconstruct the account's address).
18
+ */
19
+ export type AccountPrivacyKeys = AccountPrivacySecretKeys & {
20
+ masterMessageSigningPublicKey: PublicKey;
21
+ masterFallbackPublicKey: PublicKey;
22
+ };
23
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNjb3VudF9wcml2YWN5X2tleXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9hY2NvdW50X3ByaXZhY3lfa2V5cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxjQUFjLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUN4RSxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUVwRDs7O0dBR0c7QUFDSCxNQUFNLE1BQU0sd0JBQXdCLEdBQUc7SUFDckMsOEJBQThCLEVBQUUsY0FBYyxDQUFDO0lBQy9DLDhCQUE4QixFQUFFLGNBQWMsQ0FBQztJQUMvQyw4QkFBOEIsRUFBRSxjQUFjLENBQUM7SUFDL0Msc0JBQXNCLEVBQUUsY0FBYyxDQUFDO0NBQ3hDLENBQUM7QUFFRjs7Ozs7R0FLRztBQUNILE1BQU0sTUFBTSxrQkFBa0IsR0FBRyx3QkFBd0IsR0FBRztJQUMxRCw2QkFBNkIsRUFBRSxTQUFTLENBQUM7SUFDekMsdUJBQXVCLEVBQUUsU0FBUyxDQUFDO0NBQ3BDLENBQUMifQ==
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account_privacy_keys.d.ts","sourceRoot":"","sources":["../src/account_privacy_keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,8BAA8B,EAAE,cAAc,CAAC;IAC/C,8BAA8B,EAAE,cAAc,CAAC;IAC/C,8BAA8B,EAAE,cAAc,CAAC;IAC/C,sBAAsB,EAAE,cAAc,CAAC;CACxC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,wBAAwB,GAAG;IAC1D,6BAA6B,EAAE,SAAS,CAAC;IACzC,uBAAuB,EAAE,SAAS,CAAC;CACpC,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The keys needed to register an account: the four privacy secret keys the key store holds, plus the *public*
3
+ * message-signing and fallback keys. The message-signing and fallback secret keys are withheld from the key store (and
4
+ * hence from PXE, which embeds it), since it is not trusted to hold them: only their public keys are needed (to
5
+ * reconstruct the account's address).
6
+ */ export { };
package/dest/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
+ export * from './account_privacy_keys.js';
1
2
  export * from './key_store.js';
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLGdCQUFnQixDQUFDIn0=
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDJCQUEyQixDQUFDO0FBQzFDLGNBQWMsZ0JBQWdCLENBQUMifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,gBAAgB,CAAC"}
package/dest/index.js CHANGED
@@ -1 +1,2 @@
1
+ export * from './account_privacy_keys.js';
1
2
  export * from './key_store.js';
@@ -6,6 +6,7 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address';
6
6
  import { CompleteAddress, type PartialAddress } from '@aztec/stdlib/contract';
7
7
  import { KeyValidationRequest } from '@aztec/stdlib/kernel';
8
8
  import { type KeyPrefix, type PublicKey } from '@aztec/stdlib/keys';
9
+ import type { AccountPrivacyKeys, AccountPrivacySecretKeys } from './account_privacy_keys.js';
9
10
  /**
10
11
  * Used for managing keys. Can hold keys of multiple accounts.
11
12
  */
@@ -14,17 +15,19 @@ export declare class KeyStore {
14
15
  static readonly SCHEMA_VERSION = 1;
15
16
  constructor(database: AztecAsyncKVStore);
16
17
  /**
17
- * Creates a new account from a randomly generated secret key.
18
- * @returns A promise that resolves to the newly created account's CompleteAddress.
19
- */
20
- createAccount(): Promise<CompleteAddress>;
21
- /**
22
- * Adds an account to the key store from the provided secret key.
23
- * @param sk - The secret key of the account.
18
+ * Adds an account to the key store.
19
+ *
20
+ * The key store holds the four privacy secret keys (nullifier-hiding, incoming-viewing, outgoing-viewing, tagging),
21
+ * but only the *public* message-signing and fallback keys: their secret keys are withheld, since the key store (and
22
+ * PXE, which embeds it) is not trusted to hold them. The public keys are still needed to reconstruct the account's
23
+ * address, which commits to all six master public keys.
24
+ *
25
+ * @param keys - The account's privacy keys: four secret keys plus the message-signing and fallback public keys.
24
26
  * @param partialAddress - The partial address of the account.
25
27
  * @returns The account's complete address.
28
+ * @throws If any of the account's six master public keys would be the point at infinity.
26
29
  */
27
- addAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress>;
30
+ addAccount(keys: AccountPrivacyKeys, partialAddress: PartialAddress): Promise<CompleteAddress>;
28
31
  /**
29
32
  * Retrieves addresses of accounts stored in the key store.
30
33
  * @returns A Promise that resolves to an array of account addresses.
@@ -43,38 +46,36 @@ export declare class KeyStore {
43
46
  /**
44
47
  * Gets the master nullifier public key for a given account.
45
48
  * @throws If the account does not exist in the key store.
46
- * @param account - The account address for which to retrieve the master nullifier public key.
47
- * @returns The master nullifier public key for the account.
48
49
  */
49
- getMasterNullifierPublicKey(account: AztecAddress): Promise<PublicKey>;
50
+ getMasterNullifierHidingPublicKey(account: AztecAddress): Promise<PublicKey>;
50
51
  /**
51
52
  * Gets the master incoming viewing public key for a given account.
52
53
  * @throws If the account does not exist in the key store.
53
- * @param account - The account address for which to retrieve the master incoming viewing public key.
54
- * @returns The master incoming viewing public key for the account.
55
54
  */
56
55
  getMasterIncomingViewingPublicKey(account: AztecAddress): Promise<PublicKey>;
57
56
  /**
58
57
  * Retrieves the master outgoing viewing public key.
59
58
  * @throws If the account does not exist in the key store.
60
- * @param account - The account to retrieve the master outgoing viewing key for.
61
- * @returns A Promise that resolves to the master outgoing viewing key.
62
59
  */
63
60
  getMasterOutgoingViewingPublicKey(account: AztecAddress): Promise<PublicKey>;
64
61
  /**
65
62
  * Retrieves the master tagging public key.
66
63
  * @throws If the account does not exist in the key store.
67
- * @param account - The account to retrieve the master tagging key for.
68
- * @returns A Promise that resolves to the master tagging key.
69
64
  */
70
65
  getMasterTaggingPublicKey(account: AztecAddress): Promise<PublicKey>;
71
66
  /**
72
67
  * Retrieves master incoming viewing secret key.
73
68
  * @throws If the account does not exist in the key store.
74
- * @param account - The account to retrieve the master incoming viewing secret key for.
75
- * @returns A Promise that resolves to the master incoming viewing secret key.
76
69
  */
77
70
  getMasterIncomingViewingSecretKey(account: AztecAddress): Promise<GrumpkinScalar>;
71
+ /**
72
+ * Retrieves the four privacy secret keys the key store holds for an account. Paired with {@link addAccount}, this
73
+ * allows exporting an account's privacy secret keys, e.g. to re-register it on another PXE. The message-signing and
74
+ * fallback secret keys are not held by the key store and so are not returned.
75
+ *
76
+ * @throws If the account does not exist in the key store.
77
+ */
78
+ getAccountSecretKeys(account: AztecAddress): Promise<AccountPrivacySecretKeys>;
78
79
  /**
79
80
  * Retrieves application outgoing viewing secret key.
80
81
  * @throws If the account does not exist in the key store.
@@ -84,13 +85,13 @@ export declare class KeyStore {
84
85
  */
85
86
  getAppOutgoingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise<Fr>;
86
87
  /**
87
- * Retrieves the sk_m corresponding to the pk_m.
88
- * @throws If the provided public key is not associated with any of the registered accounts.
89
- * @param pkM - The master public key to get secret key for.
88
+ * Retrieves the sk_m corresponding to the given pk_m hash.
89
+ * @throws If the provided hash is not associated with any of the registered accounts.
90
+ * @param pkMHash - The master public key hash to get secret key for.
90
91
  * @returns A Promise that resolves to sk_m.
91
92
  * @dev Used when feeding the sk_m to the kernel circuit for keys verification.
92
93
  */
93
- getMasterSecretKey(pkM: PublicKey): Promise<GrumpkinScalar>;
94
+ getMasterSecretKey(pkMHash: Fr): Promise<GrumpkinScalar>;
94
95
  /**
95
96
  * Checks whether a given account has a key matching the provided master public key hash.
96
97
  * @param account - The account address to check.
@@ -106,4 +107,4 @@ export declare class KeyStore {
106
107
  */
107
108
  getKeyPrefixAndAccount(value: Bufferable): Promise<[KeyPrefix, AztecAddress]>;
108
109
  }
109
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5X3N0b3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMva2V5X3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFTLE1BQU0sbUNBQW1DLENBQUM7QUFFMUUsT0FBTyxFQUFFLEtBQUssVUFBVSxFQUFxQixNQUFNLDZCQUE2QixDQUFDO0FBQ2pGLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFpQixNQUFNLGlCQUFpQixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDNUQsT0FBTyxFQUVMLEtBQUssU0FBUyxFQUNkLEtBQUssU0FBUyxFQUlmLE1BQU0sb0JBQW9CLENBQUM7QUFPNUI7O0dBRUc7QUFDSCxxQkFBYSxRQUFROztJQUNuQixnQkFBdUIsY0FBYyxLQUFLO0lBSTFDLFlBQVksUUFBUSxFQUFFLGlCQUFpQixFQUd0QztJQUVEOzs7T0FHRztJQUNJLGFBQWEsSUFBSSxPQUFPLENBQUMsZUFBZSxDQUFDLENBSS9DO0lBRUQ7Ozs7O09BS0c7SUFDVSxVQUFVLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxjQUFjLEVBQUUsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0F3Q3hGO0lBRUQ7OztPQUdHO0lBQ1UsV0FBVyxJQUFJLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUtsRDtJQUVELGdFQUFnRTtJQUNuRCxVQUFVLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRS9EO0lBRUQ7Ozs7OztPQU1HO0lBQ1UsdUJBQXVCLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxlQUFlLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQXNDOUc7SUFFRDs7Ozs7T0FLRztJQUNVLDJCQUEyQixDQUFDLE9BQU8sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQVFsRjtJQUVEOzs7OztPQUtHO0lBQ1UsaUNBQWlDLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBUXhGO0lBRUQ7Ozs7O09BS0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FReEY7SUFFRDs7Ozs7T0FLRztJQUNVLHlCQUF5QixDQUFDLE9BQU8sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQVFoRjtJQUVEOzs7OztPQUtHO0lBQ1UsaUNBQWlDLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDLENBUTdGO0lBRUQ7Ozs7OztPQU1HO0lBQ1UsOEJBQThCLENBQUMsT0FBTyxFQUFFLFlBQVksRUFBRSxHQUFHLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0Fhakc7SUFFRDs7Ozs7O09BTUc7SUFDVSxrQkFBa0IsQ0FBQyxHQUFHLEVBQUUsU0FBUyxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FrQnZFO0lBRUQ7Ozs7O09BS0c7SUFDVSxhQUFhLENBQUMsT0FBTyxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FTL0U7SUFFRDs7Ozs7T0FLRztJQUNVLHNCQUFzQixDQUFDLEtBQUssRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLENBQUMsU0FBUyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBY3pGO0NBQ0YifQ==
110
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5X3N0b3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMva2V5X3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFTLE1BQU0sbUNBQW1DLENBQUM7QUFFMUUsT0FBTyxFQUFFLEtBQUssVUFBVSxFQUFxQixNQUFNLDZCQUE2QixDQUFDO0FBQ2pGLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFpQixNQUFNLGlCQUFpQixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDNUQsT0FBTyxFQUVMLEtBQUssU0FBUyxFQUNkLEtBQUssU0FBUyxFQUtmLE1BQU0sb0JBQW9CLENBQUM7QUFFNUIsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQStEOUY7O0dBRUc7QUFDSCxxQkFBYSxRQUFROztJQUNuQixnQkFBdUIsY0FBYyxLQUFLO0lBSTFDLFlBQVksUUFBUSxFQUFFLGlCQUFpQixFQUd0QztJQUVEOzs7Ozs7Ozs7Ozs7T0FZRztJQUNVLFVBQVUsQ0FBQyxJQUFJLEVBQUUsa0JBQWtCLEVBQUUsY0FBYyxFQUFFLGNBQWMsR0FBRyxPQUFPLENBQUMsZUFBZSxDQUFDLENBRzFHO0lBRUQ7OztPQUdHO0lBQ1UsV0FBVyxJQUFJLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUtsRDtJQUVELGdFQUFnRTtJQUNuRCxVQUFVLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRS9EO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksdUJBQXVCLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxlQUFlLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQTBDeEc7SUFFRDs7O09BR0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFeEY7SUFFRDs7O09BR0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFeEY7SUFFRDs7O09BR0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFeEY7SUFFRDs7O09BR0c7SUFDVSx5QkFBeUIsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFaEY7SUFFRDs7O09BR0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FFN0Y7SUFFRDs7Ozs7O09BTUc7SUFDVSxvQkFBb0IsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxDQVExRjtJQUVEOzs7Ozs7T0FNRztJQUNVLDhCQUE4QixDQUFDLE9BQU8sRUFBRSxZQUFZLEVBQUUsR0FBRyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDLENBT2pHO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksa0JBQWtCLENBQUMsT0FBTyxFQUFFLEVBQUUsR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDLENBNkI5RDtJQUVEOzs7OztPQUtHO0lBQ0ksYUFBYSxDQUFDLE9BQU8sRUFBRSxZQUFZLEVBQUUsT0FBTyxFQUFFLEVBQUUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBV3pFO0lBRUQ7Ozs7O09BS0c7SUFDVSxzQkFBc0IsQ0FBQyxLQUFLLEVBQUUsVUFBVSxHQUFHLE9BQU8sQ0FBQyxDQUFDLFNBQVMsRUFBRSxZQUFZLENBQUMsQ0FBQyxDQWN6RjtDQWdGRiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"key_store.d.ts","sourceRoot":"","sources":["../src/key_store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACpD,OAAO,EAAE,cAAc,EAAS,MAAM,mCAAmC,CAAC;AAE1E,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,6BAA6B,CAAC;AACjF,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,SAAS,EAIf,MAAM,oBAAoB,CAAC;AAO5B;;GAEG;AACH,qBAAa,QAAQ;;IACnB,gBAAuB,cAAc,KAAK;IAI1C,YAAY,QAAQ,EAAE,iBAAiB,EAGtC;IAED;;;OAGG;IACI,aAAa,IAAI,OAAO,CAAC,eAAe,CAAC,CAI/C;IAED;;;;;OAKG;IACU,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAwCxF;IAED;;;OAGG;IACU,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAKlD;IAED,gEAAgE;IACnD,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAE/D;IAED;;;;;;OAMG;IACU,uBAAuB,CAAC,OAAO,EAAE,EAAE,EAAE,eAAe,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAsC9G;IAED;;;;;OAKG;IACU,2BAA2B,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQlF;IAED;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQxF;IAED;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQxF;IAED;;;;;OAKG;IACU,yBAAyB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQhF;IAED;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAQ7F;IAED;;;;;;OAMG;IACU,8BAA8B,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC,CAajG;IAED;;;;;;OAMG;IACU,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC,CAkBvE;IAED;;;;;OAKG;IACU,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAS/E;IAED;;;;;OAKG;IACU,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAczF;CACF"}
1
+ {"version":3,"file":"key_store.d.ts","sourceRoot":"","sources":["../src/key_store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACpD,OAAO,EAAE,cAAc,EAAS,MAAM,mCAAmC,CAAC;AAE1E,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,6BAA6B,CAAC;AACjF,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,SAAS,EAKf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AA+D9F;;GAEG;AACH,qBAAa,QAAQ;;IACnB,gBAAuB,cAAc,KAAK;IAI1C,YAAY,QAAQ,EAAE,iBAAiB,EAGtC;IAED;;;;;;;;;;;;OAYG;IACU,UAAU,CAAC,IAAI,EAAE,kBAAkB,EAAE,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAG1G;IAED;;;OAGG;IACU,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAKlD;IAED,gEAAgE;IACnD,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAE/D;IAED;;;;;;OAMG;IACI,uBAAuB,CAAC,OAAO,EAAE,EAAE,EAAE,eAAe,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA0CxG;IAED;;;OAGG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAExF;IAED;;;OAGG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAExF;IAED;;;OAGG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAExF;IAED;;;OAGG;IACU,yBAAyB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAEhF;IAED;;;OAGG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAE7F;IAED;;;;;;OAMG;IACU,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAQ1F;IAED;;;;;;OAMG;IACU,8BAA8B,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC,CAOjG;IAED;;;;;;OAMG;IACI,kBAAkB,CAAC,OAAO,EAAE,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CA6B9D;IAED;;;;;OAKG;IACI,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAWzE;IAED;;;;;OAKG;IACU,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAczF;CAgFF"}
package/dest/key_store.js CHANGED
@@ -1,16 +1,51 @@
1
1
  import { DomainSeparator } from '@aztec/constants';
2
2
  import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon';
3
- import { Fr } from '@aztec/foundation/curves/bn254';
4
3
  import { GrumpkinScalar, Point } from '@aztec/foundation/curves/grumpkin';
5
4
  import { toArray } from '@aztec/foundation/iterable';
6
5
  import { serializeToBuffer } from '@aztec/foundation/serialize';
7
6
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
8
7
  import { CompleteAddress } from '@aztec/stdlib/contract';
9
8
  import { KeyValidationRequest } from '@aztec/stdlib/kernel';
10
- import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecretKey } from '@aztec/stdlib/keys';
9
+ import { KEY_PREFIXES, PublicKeys, computeAppSecretKey, derivePublicKeyFromSecretKey, hashPublicKey } from '@aztec/stdlib/keys';
11
10
  /** Maps a key prefix to the storage suffix for the corresponding master secret key. */ function secretKeyStorageSuffix(prefix) {
12
11
  return prefix === 'n' ? 'nhk_m' : `${prefix}sk_m`;
13
12
  }
13
+ /**
14
+ * Computes the public counterparts of an account's four privacy secret keys and assembles its {@link PublicKeys} struct
15
+ * (used to derive the address), from the {@link AccountPrivacyKeys} passed to {@link KeyStore.addAccount}.
16
+ *
17
+ * The message-signing and fallback keys are already supplied as public keys, since the key store never holds their
18
+ * secrets.
19
+ */ async function completeAccountKeys(keys) {
20
+ const { masterNullifierHidingSecretKey, masterIncomingViewingSecretKey, masterOutgoingViewingSecretKey, masterTaggingSecretKey, masterMessageSigningPublicKey, masterFallbackPublicKey } = keys;
21
+ const masterNullifierHidingPublicKey = await derivePublicKeyFromSecretKey(masterNullifierHidingSecretKey);
22
+ const masterIncomingViewingPublicKey = await derivePublicKeyFromSecretKey(masterIncomingViewingSecretKey);
23
+ const masterOutgoingViewingPublicKey = await derivePublicKeyFromSecretKey(masterOutgoingViewingSecretKey);
24
+ const masterTaggingPublicKey = await derivePublicKeyFromSecretKey(masterTaggingSecretKey);
25
+ for (const [name, publicKey] of Object.entries({
26
+ masterNullifierHidingPublicKey,
27
+ masterIncomingViewingPublicKey,
28
+ masterOutgoingViewingPublicKey,
29
+ masterTaggingPublicKey,
30
+ masterMessageSigningPublicKey,
31
+ masterFallbackPublicKey
32
+ })){
33
+ if (publicKey.isInfinite) {
34
+ throw new Error(`Cannot register an account with an infinity ${name}.`);
35
+ }
36
+ }
37
+ const publicKeys = new PublicKeys(await hashPublicKey(masterNullifierHidingPublicKey), masterIncomingViewingPublicKey, await hashPublicKey(masterOutgoingViewingPublicKey), await hashPublicKey(masterTaggingPublicKey), await hashPublicKey(masterMessageSigningPublicKey), await hashPublicKey(masterFallbackPublicKey));
38
+ return {
39
+ masterNullifierHidingSecretKey,
40
+ masterIncomingViewingSecretKey,
41
+ masterOutgoingViewingSecretKey,
42
+ masterTaggingSecretKey,
43
+ masterNullifierHidingPublicKey,
44
+ masterOutgoingViewingPublicKey,
45
+ masterTaggingPublicKey,
46
+ publicKeys
47
+ };
48
+ }
14
49
  /**
15
50
  * Used for managing keys. Can hold keys of multiple accounts.
16
51
  */ export class KeyStore {
@@ -22,58 +57,32 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
22
57
  this.#keys = database.openMap('key_store');
23
58
  }
24
59
  /**
25
- * Creates a new account from a randomly generated secret key.
26
- * @returns A promise that resolves to the newly created account's CompleteAddress.
27
- */ createAccount() {
28
- const sk = Fr.random();
29
- const partialAddress = Fr.random();
30
- return this.addAccount(sk, partialAddress);
31
- }
32
- /**
33
- * Adds an account to the key store from the provided secret key.
34
- * @param sk - The secret key of the account.
60
+ * Adds an account to the key store.
61
+ *
62
+ * The key store holds the four privacy secret keys (nullifier-hiding, incoming-viewing, outgoing-viewing, tagging),
63
+ * but only the *public* message-signing and fallback keys: their secret keys are withheld, since the key store (and
64
+ * PXE, which embeds it) is not trusted to hold them. The public keys are still needed to reconstruct the account's
65
+ * address, which commits to all six master public keys.
66
+ *
67
+ * @param keys - The account's privacy keys: four secret keys plus the message-signing and fallback public keys.
35
68
  * @param partialAddress - The partial address of the account.
36
69
  * @returns The account's complete address.
37
- */ async addAccount(sk, partialAddress) {
38
- const { masterNullifierHidingKey, masterIncomingViewingSecretKey, masterOutgoingViewingSecretKey, masterTaggingSecretKey, publicKeys } = await deriveKeys(sk);
39
- const completeAddress = await CompleteAddress.fromSecretKeyAndPartialAddress(sk, partialAddress);
40
- const { address: account } = completeAddress;
41
- // Compute hashes before transaction
42
- const masterNullifierPublicKeyHash = await publicKeys.masterNullifierPublicKey.hash();
43
- const masterIncomingViewingPublicKeyHash = await publicKeys.masterIncomingViewingPublicKey.hash();
44
- const masterOutgoingViewingPublicKeyHash = await publicKeys.masterOutgoingViewingPublicKey.hash();
45
- const masterTaggingPublicKeyHash = await publicKeys.masterTaggingPublicKey.hash();
46
- await this.#db.transactionAsync(async ()=>{
47
- // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
48
- await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
49
- await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
50
- await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
51
- await this.#keys.set(`${account.toString()}-nhk_m`, masterNullifierHidingKey.toBuffer());
52
- await this.#keys.set(`${account.toString()}-npk_m`, publicKeys.masterNullifierPublicKey.toBuffer());
53
- await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.masterIncomingViewingPublicKey.toBuffer());
54
- await this.#keys.set(`${account.toString()}-ovpk_m`, publicKeys.masterOutgoingViewingPublicKey.toBuffer());
55
- await this.#keys.set(`${account.toString()}-tpk_m`, publicKeys.masterTaggingPublicKey.toBuffer());
56
- // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
57
- // using the #getKeyPrefixAndAccount function later on
58
- await this.#keys.set(`${account.toString()}-npk_m_hash`, masterNullifierPublicKeyHash.toBuffer());
59
- await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
60
- await this.#keys.set(`${account.toString()}-ovpk_m_hash`, masterOutgoingViewingPublicKeyHash.toBuffer());
61
- await this.#keys.set(`${account.toString()}-tpk_m_hash`, masterTaggingPublicKeyHash.toBuffer());
62
- });
63
- // At last, we return the newly derived account address
64
- return completeAddress;
70
+ * @throws If any of the account's six master public keys would be the point at infinity.
71
+ */ async addAccount(keys, partialAddress) {
72
+ const accountKeys = await completeAccountKeys(keys);
73
+ return this.#storeAccountKeys(accountKeys, partialAddress);
65
74
  }
66
75
  /**
67
76
  * Retrieves addresses of accounts stored in the key store.
68
77
  * @returns A Promise that resolves to an array of account addresses.
69
78
  */ async getAccounts() {
70
- const allMapKeys = await toArray(this.#keys.keysAsync());
79
+ const allMapKeys = await this.#db.transactionAsync(()=>toArray(this.#keys.keysAsync()));
71
80
  // We return account addresses based on the map keys that end with '-ivsk_m'
72
81
  const accounts = allMapKeys.filter((key)=>key.endsWith('-ivsk_m')).map((key)=>key.split('-')[0]);
73
- return accounts.map((account)=>AztecAddress.fromString(account));
82
+ return accounts.map((account)=>AztecAddress.fromStringUnsafe(account));
74
83
  }
75
84
  /** Checks whether an account is registered in the key store. */ async hasAccount(account) {
76
- return !!await this.#keys.getAsync(`${account.toString()}-ivsk_m`);
85
+ return !!await this.#db.transactionAsync(()=>this.#keys.getAsync(`${account.toString()}-ivsk_m`));
77
86
  }
78
87
  /**
79
88
  * Gets the key validation request for a given master public key hash and contract address.
@@ -81,93 +90,86 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
81
90
  * @param pkMHash - The master public key hash.
82
91
  * @param contractAddress - The contract address to silo the secret key in the key validation request with.
83
92
  * @returns The key validation request.
84
- */ async getKeyValidationRequest(pkMHash, contractAddress) {
85
- const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
86
- // Now we find the master public key for the account
87
- const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
88
- if (!pkMBuffer) {
89
- throw new Error(`Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
90
- }
91
- const pkM = Point.fromBuffer(pkMBuffer);
92
- const computedPkMHash = await pkM.hash();
93
- if (!computedPkMHash.equals(pkMHash)) {
94
- throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
95
- }
96
- // Now we find the secret key for the public key
97
- const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
98
- const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
99
- if (!skMBuffer) {
100
- throw new Error(`Could not find ${skStorageSuffix} for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
101
- }
102
- const skM = GrumpkinScalar.fromBuffer(skMBuffer);
103
- // We sanity check that it's possible to derive the public key from the secret key
104
- const derivedPkM = await derivePublicKeyFromSecretKey(skM);
105
- if (!derivedPkM.equals(pkM)) {
106
- throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
107
- }
108
- // At last we silo the secret key and return the key validation request
109
- const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix);
110
- return new KeyValidationRequest(pkM, skApp);
93
+ */ getKeyValidationRequest(pkMHash, contractAddress) {
94
+ return this.#db.transactionAsync(async ()=>{
95
+ const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
96
+ // Load the stored master public key point. The returned KVR carries only the hash, but we
97
+ // use the point here as a witness for two integrity checks below: (1) it matches the supplied
98
+ // hash, and (2) it matches the value derived from the stored secret key.
99
+ const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
100
+ if (!pkMBuffer) {
101
+ throw new Error(`Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
102
+ }
103
+ const pkM = Point.fromBuffer(pkMBuffer);
104
+ // Now we find the secret key for the public key
105
+ const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
106
+ const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
107
+ if (!skMBuffer) {
108
+ throw new Error(`Could not find ${skStorageSuffix} for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
109
+ }
110
+ const skM = GrumpkinScalar.fromBuffer(skMBuffer);
111
+ // The remaining awaits are non-DB computations. They are safe because no further IDB operations follow them.
112
+ const computedPkMHash = await hashPublicKey(pkM);
113
+ if (!computedPkMHash.equals(pkMHash)) {
114
+ throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
115
+ }
116
+ const derivedPkM = await derivePublicKeyFromSecretKey(skM);
117
+ if (!derivedPkM.equals(pkM)) {
118
+ throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
119
+ }
120
+ const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix);
121
+ return new KeyValidationRequest(pkMHash, skApp);
122
+ });
111
123
  }
112
124
  /**
113
125
  * Gets the master nullifier public key for a given account.
114
126
  * @throws If the account does not exist in the key store.
115
- * @param account - The account address for which to retrieve the master nullifier public key.
116
- * @returns The master nullifier public key for the account.
117
- */ async getMasterNullifierPublicKey(account) {
118
- const masterNullifierPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-npk_m`);
119
- if (!masterNullifierPublicKeyBuffer) {
120
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
121
- }
122
- return Point.fromBuffer(masterNullifierPublicKeyBuffer);
127
+ */ async getMasterNullifierHidingPublicKey(account) {
128
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'npk_m'));
123
129
  }
124
130
  /**
125
131
  * Gets the master incoming viewing public key for a given account.
126
132
  * @throws If the account does not exist in the key store.
127
- * @param account - The account address for which to retrieve the master incoming viewing public key.
128
- * @returns The master incoming viewing public key for the account.
129
133
  */ async getMasterIncomingViewingPublicKey(account) {
130
- const masterIncomingViewingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ivpk_m`);
131
- if (!masterIncomingViewingPublicKeyBuffer) {
132
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
133
- }
134
- return Point.fromBuffer(masterIncomingViewingPublicKeyBuffer);
134
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'ivpk_m'));
135
135
  }
136
136
  /**
137
137
  * Retrieves the master outgoing viewing public key.
138
138
  * @throws If the account does not exist in the key store.
139
- * @param account - The account to retrieve the master outgoing viewing key for.
140
- * @returns A Promise that resolves to the master outgoing viewing key.
141
139
  */ async getMasterOutgoingViewingPublicKey(account) {
142
- const masterOutgoingViewingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ovpk_m`);
143
- if (!masterOutgoingViewingPublicKeyBuffer) {
144
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
145
- }
146
- return Point.fromBuffer(masterOutgoingViewingPublicKeyBuffer);
140
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'ovpk_m'));
147
141
  }
148
142
  /**
149
143
  * Retrieves the master tagging public key.
150
144
  * @throws If the account does not exist in the key store.
151
- * @param account - The account to retrieve the master tagging key for.
152
- * @returns A Promise that resolves to the master tagging key.
153
145
  */ async getMasterTaggingPublicKey(account) {
154
- const masterTaggingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-tpk_m`);
155
- if (!masterTaggingPublicKeyBuffer) {
156
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
157
- }
158
- return Point.fromBuffer(masterTaggingPublicKeyBuffer);
146
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'tpk_m'));
159
147
  }
160
148
  /**
161
149
  * Retrieves master incoming viewing secret key.
162
150
  * @throws If the account does not exist in the key store.
163
- * @param account - The account to retrieve the master incoming viewing secret key for.
164
- * @returns A Promise that resolves to the master incoming viewing secret key.
165
151
  */ async getMasterIncomingViewingSecretKey(account) {
166
- const masterIncomingViewingSecretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ivsk_m`);
167
- if (!masterIncomingViewingSecretKeyBuffer) {
168
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
169
- }
170
- return GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer);
152
+ return GrumpkinScalar.fromBuffer(await this.#getMasterKeyBuffer(account, 'ivsk_m'));
153
+ }
154
+ /**
155
+ * Retrieves the four privacy secret keys the key store holds for an account. Paired with {@link addAccount}, this
156
+ * allows exporting an account's privacy secret keys, e.g. to re-register it on another PXE. The message-signing and
157
+ * fallback secret keys are not held by the key store and so are not returned.
158
+ *
159
+ * @throws If the account does not exist in the key store.
160
+ */ async getAccountSecretKeys(account) {
161
+ const [nhkM, ivskM, ovskM, tskM] = await this.#getMasterKeyBuffers(account, [
162
+ 'nhk_m',
163
+ 'ivsk_m',
164
+ 'ovsk_m',
165
+ 'tsk_m'
166
+ ]);
167
+ return {
168
+ masterNullifierHidingSecretKey: GrumpkinScalar.fromBuffer(nhkM),
169
+ masterIncomingViewingSecretKey: GrumpkinScalar.fromBuffer(ivskM),
170
+ masterOutgoingViewingSecretKey: GrumpkinScalar.fromBuffer(ovskM),
171
+ masterTaggingSecretKey: GrumpkinScalar.fromBuffer(tskM)
172
+ };
171
173
  }
172
174
  /**
173
175
  * Retrieves application outgoing viewing secret key.
@@ -176,11 +178,7 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
176
178
  * @param app - The application address to retrieve the outgoing viewing secret key for.
177
179
  * @returns A Promise that resolves to the application outgoing viewing secret key.
178
180
  */ async getAppOutgoingViewingSecretKey(account, app) {
179
- const masterOutgoingViewingSecretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ovsk_m`);
180
- if (!masterOutgoingViewingSecretKeyBuffer) {
181
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
182
- }
183
- const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(masterOutgoingViewingSecretKeyBuffer);
181
+ const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(await this.#getMasterKeyBuffer(account, 'ovsk_m'));
184
182
  return poseidon2HashWithSeparator([
185
183
  masterOutgoingViewingSecretKey.hi,
186
184
  masterOutgoingViewingSecretKey.lo,
@@ -188,39 +186,49 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
188
186
  ], DomainSeparator.OVSK_M);
189
187
  }
190
188
  /**
191
- * Retrieves the sk_m corresponding to the pk_m.
192
- * @throws If the provided public key is not associated with any of the registered accounts.
193
- * @param pkM - The master public key to get secret key for.
189
+ * Retrieves the sk_m corresponding to the given pk_m hash.
190
+ * @throws If the provided hash is not associated with any of the registered accounts.
191
+ * @param pkMHash - The master public key hash to get secret key for.
194
192
  * @returns A Promise that resolves to sk_m.
195
193
  * @dev Used when feeding the sk_m to the kernel circuit for keys verification.
196
- */ async getMasterSecretKey(pkM) {
197
- const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkM);
198
- const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
199
- const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
200
- if (!secretKeyBuffer) {
201
- throw new Error(`Could not find ${skStorageSuffix} for ${keyPrefix}pk_m ${pkM.toString()}. This should not happen.`);
202
- }
203
- const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
204
- const derivedpkM = await derivePublicKeyFromSecretKey(skM);
205
- if (!derivedpkM.equals(pkM)) {
206
- throw new Error(`Could not find ${skStorageSuffix} for ${keyPrefix}pkM ${pkM.toString()} in secret keys buffer.`);
207
- }
208
- return Promise.resolve(skM);
194
+ */ getMasterSecretKey(pkMHash) {
195
+ return this.#db.transactionAsync(async ()=>{
196
+ const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
197
+ const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
198
+ const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
199
+ if (!secretKeyBuffer) {
200
+ throw new Error(`Could not find ${skStorageSuffix} for ${keyPrefix}pk_m_hash ${pkMHash.toString()}. This should not happen.`);
201
+ }
202
+ const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
203
+ // Non-DB computation — safe because no further IDB operations follow.
204
+ // Integrity check: confirm the stored secret key still derives the requested hash. The check
205
+ // is hash-based rather than point-equal because the on-disk identifier is `pk_m_hash`;
206
+ // cryptographic collision resistance of `hashPublicKey` makes this equivalent to a
207
+ // direct point comparison in practice.
208
+ const derivedPkM = await derivePublicKeyFromSecretKey(skM);
209
+ const derivedPkMHash = await hashPublicKey(derivedPkM);
210
+ if (!derivedPkMHash.equals(pkMHash)) {
211
+ throw new Error(`Could not find ${skStorageSuffix} for ${keyPrefix}pk_m_hash ${pkMHash.toString()} in secret keys buffer.`);
212
+ }
213
+ return skM;
214
+ });
209
215
  }
210
216
  /**
211
217
  * Checks whether a given account has a key matching the provided master public key hash.
212
218
  * @param account - The account address to check.
213
219
  * @param pkMHash - The master public key hash to look for.
214
220
  * @returns True if the account has a key with the given hash.
215
- */ async accountHasKey(account, pkMHash) {
216
- const pkMHashBuffer = serializeToBuffer(pkMHash);
217
- for (const prefix of KEY_PREFIXES){
218
- const stored = await this.#keys.getAsync(`${account.toString()}-${prefix}pk_m_hash`);
219
- if (stored && Buffer.from(stored).equals(pkMHashBuffer)) {
220
- return true;
221
+ */ accountHasKey(account, pkMHash) {
222
+ return this.#db.transactionAsync(async ()=>{
223
+ const pkMHashBuffer = serializeToBuffer(pkMHash);
224
+ for (const prefix of KEY_PREFIXES){
225
+ const stored = await this.#keys.getAsync(`${account.toString()}-${prefix}pk_m_hash`);
226
+ if (stored && Buffer.from(stored).equals(pkMHashBuffer)) {
227
+ return true;
228
+ }
221
229
  }
222
- }
223
- return false;
230
+ return false;
231
+ });
224
232
  }
225
233
  /**
226
234
  * Gets the key prefix and account address for a given value.
@@ -234,7 +242,7 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
234
242
  if (Buffer.from(val).equals(valueBuffer)) {
235
243
  for (const prefix of KEY_PREFIXES){
236
244
  if (key.includes(`-${prefix}`)) {
237
- const account = AztecAddress.fromString(key.split('-')[0]);
245
+ const account = AztecAddress.fromStringUnsafe(key.split('-')[0]);
238
246
  return [
239
247
  prefix,
240
248
  account
@@ -245,4 +253,56 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
245
253
  }
246
254
  throw new Error(`Could not find key prefix.`);
247
255
  }
256
+ /**
257
+ * Persists a completed set of account keys and returns the resulting complete address.
258
+ */ async #storeAccountKeys(accountKeys, partialAddress) {
259
+ const { masterNullifierHidingSecretKey, masterIncomingViewingSecretKey, masterOutgoingViewingSecretKey, masterTaggingSecretKey, masterNullifierHidingPublicKey, masterOutgoingViewingPublicKey, masterTaggingPublicKey, publicKeys } = accountKeys;
260
+ const completeAddress = await CompleteAddress.fromPublicKeysAndPartialAddress(publicKeys, partialAddress);
261
+ const { address: account } = completeAddress;
262
+ // completeAccountKeys has already guaranteed these master public keys are non-infinity, which the kernel cannot
263
+ // check but the address relies on.
264
+ // The npk/ovpk/tpk hashes are already in publicKeys; ivpk_m_hash is computed for indexing.
265
+ const masterIncomingViewingPublicKeyHash = await hashPublicKey(publicKeys.ivpkM);
266
+ await this.#db.transactionAsync(async ()=>{
267
+ // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m.
268
+ //
269
+ // The message-signing and fallback keys are not stored: their secret keys are withheld from the key store, and
270
+ // their public keys are only needed transiently to compute the address (they live in the AddressStore).
271
+ await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
272
+ await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
273
+ await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
274
+ await this.#keys.set(`${account.toString()}-nhk_m`, masterNullifierHidingSecretKey.toBuffer());
275
+ await this.#keys.set(`${account.toString()}-npk_m`, masterNullifierHidingPublicKey.toBuffer());
276
+ await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.ivpkM.toBuffer());
277
+ await this.#keys.set(`${account.toString()}-ovpk_m`, masterOutgoingViewingPublicKey.toBuffer());
278
+ await this.#keys.set(`${account.toString()}-tpk_m`, masterTaggingPublicKey.toBuffer());
279
+ // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
280
+ // using the #getKeyPrefixAndAccount function later on
281
+ await this.#keys.set(`${account.toString()}-npk_m_hash`, publicKeys.npkMHash.toBuffer());
282
+ await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
283
+ await this.#keys.set(`${account.toString()}-ovpk_m_hash`, publicKeys.ovpkMHash.toBuffer());
284
+ await this.#keys.set(`${account.toString()}-tpk_m_hash`, publicKeys.tpkMHash.toBuffer());
285
+ });
286
+ return completeAddress;
287
+ }
288
+ /**
289
+ * Fetches a stored master key buffer for an account by its storage suffix (e.g. `npk_m`, `ivsk_m`).
290
+ * @throws If the account does not exist in the key store.
291
+ */ async #getMasterKeyBuffer(account, suffix) {
292
+ const [buffer] = await this.#getMasterKeyBuffers(account, [
293
+ suffix
294
+ ]);
295
+ return buffer;
296
+ }
297
+ /**
298
+ * Fetches multiple stored master key buffers for an account in a single transaction, returning them in the order of
299
+ * the requested storage suffixes.
300
+ * @throws If any of the keys is missing (i.e. the account does not exist in the key store).
301
+ */ async #getMasterKeyBuffers(account, suffixes) {
302
+ const buffers = await this.#db.transactionAsync(()=>Promise.all(suffixes.map((suffix)=>this.#keys.getAsync(`${account.toString()}-${suffix}`))));
303
+ if (!buffers.every((buffer)=>buffer !== undefined)) {
304
+ throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
305
+ }
306
+ return buffers;
307
+ }
248
308
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/key-store",
3
- "version": "0.0.1-commit.f504929",
3
+ "version": "0.0.1-commit.f5a9928",
4
4
  "type": "module",
5
5
  "exports": "./dest/index.js",
6
6
  "typedocOptions": {
@@ -57,10 +57,10 @@
57
57
  ]
58
58
  },
59
59
  "dependencies": {
60
- "@aztec/constants": "0.0.1-commit.f504929",
61
- "@aztec/foundation": "0.0.1-commit.f504929",
62
- "@aztec/kv-store": "0.0.1-commit.f504929",
63
- "@aztec/stdlib": "0.0.1-commit.f504929",
60
+ "@aztec/constants": "0.0.1-commit.f5a9928",
61
+ "@aztec/foundation": "0.0.1-commit.f5a9928",
62
+ "@aztec/kv-store": "0.0.1-commit.f5a9928",
63
+ "@aztec/stdlib": "0.0.1-commit.f5a9928",
64
64
  "tslib": "^2.4.0"
65
65
  },
66
66
  "devDependencies": {
@@ -0,0 +1,24 @@
1
+ import type { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
2
+ import type { PublicKey } from '@aztec/stdlib/keys';
3
+
4
+ /**
5
+ * The four master privacy secret keys the key store holds for an account: the nullifier-hiding, incoming-viewing,
6
+ * outgoing-viewing, and tagging keys.
7
+ */
8
+ export type AccountPrivacySecretKeys = {
9
+ masterNullifierHidingSecretKey: GrumpkinScalar;
10
+ masterIncomingViewingSecretKey: GrumpkinScalar;
11
+ masterOutgoingViewingSecretKey: GrumpkinScalar;
12
+ masterTaggingSecretKey: GrumpkinScalar;
13
+ };
14
+
15
+ /**
16
+ * The keys needed to register an account: the four privacy secret keys the key store holds, plus the *public*
17
+ * message-signing and fallback keys. The message-signing and fallback secret keys are withheld from the key store (and
18
+ * hence from PXE, which embeds it), since it is not trusted to hold them: only their public keys are needed (to
19
+ * reconstruct the account's address).
20
+ */
21
+ export type AccountPrivacyKeys = AccountPrivacySecretKeys & {
22
+ masterMessageSigningPublicKey: PublicKey;
23
+ masterFallbackPublicKey: PublicKey;
24
+ };
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
+ export * from './account_privacy_keys.js';
1
2
  export * from './key_store.js';
package/src/key_store.ts CHANGED
@@ -12,16 +12,75 @@ import {
12
12
  KEY_PREFIXES,
13
13
  type KeyPrefix,
14
14
  type PublicKey,
15
+ PublicKeys,
15
16
  computeAppSecretKey,
16
- deriveKeys,
17
17
  derivePublicKeyFromSecretKey,
18
+ hashPublicKey,
18
19
  } from '@aztec/stdlib/keys';
19
20
 
21
+ import type { AccountPrivacyKeys, AccountPrivacySecretKeys } from './account_privacy_keys.js';
22
+
20
23
  /** Maps a key prefix to the storage suffix for the corresponding master secret key. */
21
24
  function secretKeyStorageSuffix(prefix: KeyPrefix): string {
22
25
  return prefix === 'n' ? 'nhk_m' : `${prefix}sk_m`;
23
26
  }
24
27
 
28
+ /**
29
+ * Computes the public counterparts of an account's four privacy secret keys and assembles its {@link PublicKeys} struct
30
+ * (used to derive the address), from the {@link AccountPrivacyKeys} passed to {@link KeyStore.addAccount}.
31
+ *
32
+ * The message-signing and fallback keys are already supplied as public keys, since the key store never holds their
33
+ * secrets.
34
+ */
35
+ async function completeAccountKeys(keys: AccountPrivacyKeys) {
36
+ const {
37
+ masterNullifierHidingSecretKey,
38
+ masterIncomingViewingSecretKey,
39
+ masterOutgoingViewingSecretKey,
40
+ masterTaggingSecretKey,
41
+ masterMessageSigningPublicKey,
42
+ masterFallbackPublicKey,
43
+ } = keys;
44
+
45
+ const masterNullifierHidingPublicKey = await derivePublicKeyFromSecretKey(masterNullifierHidingSecretKey);
46
+ const masterIncomingViewingPublicKey = await derivePublicKeyFromSecretKey(masterIncomingViewingSecretKey);
47
+ const masterOutgoingViewingPublicKey = await derivePublicKeyFromSecretKey(masterOutgoingViewingSecretKey);
48
+ const masterTaggingPublicKey = await derivePublicKeyFromSecretKey(masterTaggingSecretKey);
49
+
50
+ for (const [name, publicKey] of Object.entries({
51
+ masterNullifierHidingPublicKey,
52
+ masterIncomingViewingPublicKey,
53
+ masterOutgoingViewingPublicKey,
54
+ masterTaggingPublicKey,
55
+ masterMessageSigningPublicKey,
56
+ masterFallbackPublicKey,
57
+ })) {
58
+ if (publicKey.isInfinite) {
59
+ throw new Error(`Cannot register an account with an infinity ${name}.`);
60
+ }
61
+ }
62
+
63
+ const publicKeys = new PublicKeys(
64
+ await hashPublicKey(masterNullifierHidingPublicKey),
65
+ masterIncomingViewingPublicKey,
66
+ await hashPublicKey(masterOutgoingViewingPublicKey),
67
+ await hashPublicKey(masterTaggingPublicKey),
68
+ await hashPublicKey(masterMessageSigningPublicKey),
69
+ await hashPublicKey(masterFallbackPublicKey),
70
+ );
71
+
72
+ return {
73
+ masterNullifierHidingSecretKey,
74
+ masterIncomingViewingSecretKey,
75
+ masterOutgoingViewingSecretKey,
76
+ masterTaggingSecretKey,
77
+ masterNullifierHidingPublicKey,
78
+ masterOutgoingViewingPublicKey,
79
+ masterTaggingPublicKey,
80
+ publicKeys,
81
+ };
82
+ }
83
+
25
84
  /**
26
85
  * Used for managing keys. Can hold keys of multiple accounts.
27
86
  */
@@ -36,61 +95,21 @@ export class KeyStore {
36
95
  }
37
96
 
38
97
  /**
39
- * Creates a new account from a randomly generated secret key.
40
- * @returns A promise that resolves to the newly created account's CompleteAddress.
41
- */
42
- public createAccount(): Promise<CompleteAddress> {
43
- const sk = Fr.random();
44
- const partialAddress = Fr.random();
45
- return this.addAccount(sk, partialAddress);
46
- }
47
-
48
- /**
49
- * Adds an account to the key store from the provided secret key.
50
- * @param sk - The secret key of the account.
98
+ * Adds an account to the key store.
99
+ *
100
+ * The key store holds the four privacy secret keys (nullifier-hiding, incoming-viewing, outgoing-viewing, tagging),
101
+ * but only the *public* message-signing and fallback keys: their secret keys are withheld, since the key store (and
102
+ * PXE, which embeds it) is not trusted to hold them. The public keys are still needed to reconstruct the account's
103
+ * address, which commits to all six master public keys.
104
+ *
105
+ * @param keys - The account's privacy keys: four secret keys plus the message-signing and fallback public keys.
51
106
  * @param partialAddress - The partial address of the account.
52
107
  * @returns The account's complete address.
108
+ * @throws If any of the account's six master public keys would be the point at infinity.
53
109
  */
54
- public async addAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
55
- const {
56
- masterNullifierHidingKey,
57
- masterIncomingViewingSecretKey,
58
- masterOutgoingViewingSecretKey,
59
- masterTaggingSecretKey,
60
- publicKeys,
61
- } = await deriveKeys(sk);
62
-
63
- const completeAddress = await CompleteAddress.fromSecretKeyAndPartialAddress(sk, partialAddress);
64
- const { address: account } = completeAddress;
65
-
66
- // Compute hashes before transaction
67
- const masterNullifierPublicKeyHash = await publicKeys.masterNullifierPublicKey.hash();
68
- const masterIncomingViewingPublicKeyHash = await publicKeys.masterIncomingViewingPublicKey.hash();
69
- const masterOutgoingViewingPublicKeyHash = await publicKeys.masterOutgoingViewingPublicKey.hash();
70
- const masterTaggingPublicKeyHash = await publicKeys.masterTaggingPublicKey.hash();
71
-
72
- await this.#db.transactionAsync(async () => {
73
- // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
74
- await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
75
- await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
76
- await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
77
- await this.#keys.set(`${account.toString()}-nhk_m`, masterNullifierHidingKey.toBuffer());
78
-
79
- await this.#keys.set(`${account.toString()}-npk_m`, publicKeys.masterNullifierPublicKey.toBuffer());
80
- await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.masterIncomingViewingPublicKey.toBuffer());
81
- await this.#keys.set(`${account.toString()}-ovpk_m`, publicKeys.masterOutgoingViewingPublicKey.toBuffer());
82
- await this.#keys.set(`${account.toString()}-tpk_m`, publicKeys.masterTaggingPublicKey.toBuffer());
83
-
84
- // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
85
- // using the #getKeyPrefixAndAccount function later on
86
- await this.#keys.set(`${account.toString()}-npk_m_hash`, masterNullifierPublicKeyHash.toBuffer());
87
- await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
88
- await this.#keys.set(`${account.toString()}-ovpk_m_hash`, masterOutgoingViewingPublicKeyHash.toBuffer());
89
- await this.#keys.set(`${account.toString()}-tpk_m_hash`, masterTaggingPublicKeyHash.toBuffer());
90
- });
91
-
92
- // At last, we return the newly derived account address
93
- return completeAddress;
110
+ public async addAccount(keys: AccountPrivacyKeys, partialAddress: PartialAddress): Promise<CompleteAddress> {
111
+ const accountKeys = await completeAccountKeys(keys);
112
+ return this.#storeAccountKeys(accountKeys, partialAddress);
94
113
  }
95
114
 
96
115
  /**
@@ -98,15 +117,15 @@ export class KeyStore {
98
117
  * @returns A Promise that resolves to an array of account addresses.
99
118
  */
100
119
  public async getAccounts(): Promise<AztecAddress[]> {
101
- const allMapKeys = await toArray(this.#keys.keysAsync());
120
+ const allMapKeys = await this.#db.transactionAsync(() => toArray(this.#keys.keysAsync()));
102
121
  // We return account addresses based on the map keys that end with '-ivsk_m'
103
122
  const accounts = allMapKeys.filter(key => key.endsWith('-ivsk_m')).map(key => key.split('-')[0]);
104
- return accounts.map(account => AztecAddress.fromString(account));
123
+ return accounts.map(account => AztecAddress.fromStringUnsafe(account));
105
124
  }
106
125
 
107
126
  /** Checks whether an account is registered in the key store. */
108
127
  public async hasAccount(account: AztecAddress): Promise<boolean> {
109
- return !!(await this.#keys.getAsync(`${account.toString()}-ivsk_m`));
128
+ return !!(await this.#db.transactionAsync(() => this.#keys.getAsync(`${account.toString()}-ivsk_m`)));
110
129
  }
111
130
 
112
131
  /**
@@ -116,124 +135,105 @@ export class KeyStore {
116
135
  * @param contractAddress - The contract address to silo the secret key in the key validation request with.
117
136
  * @returns The key validation request.
118
137
  */
119
- public async getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise<KeyValidationRequest> {
120
- const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
138
+ public getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise<KeyValidationRequest> {
139
+ return this.#db.transactionAsync(async () => {
140
+ const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
141
+
142
+ // Load the stored master public key point. The returned KVR carries only the hash, but we
143
+ // use the point here as a witness for two integrity checks below: (1) it matches the supplied
144
+ // hash, and (2) it matches the value derived from the stored secret key.
145
+ const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
146
+ if (!pkMBuffer) {
147
+ throw new Error(
148
+ `Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
149
+ );
150
+ }
121
151
 
122
- // Now we find the master public key for the account
123
- const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
124
- if (!pkMBuffer) {
125
- throw new Error(
126
- `Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
127
- );
128
- }
152
+ const pkM = Point.fromBuffer(pkMBuffer);
129
153
 
130
- const pkM = Point.fromBuffer(pkMBuffer);
131
- const computedPkMHash = await pkM.hash();
132
- if (!computedPkMHash.equals(pkMHash)) {
133
- throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
134
- }
154
+ // Now we find the secret key for the public key
155
+ const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
156
+ const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
157
+ if (!skMBuffer) {
158
+ throw new Error(
159
+ `Could not find ${skStorageSuffix} for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
160
+ );
161
+ }
135
162
 
136
- // Now we find the secret key for the public key
137
- const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
138
- const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
139
- if (!skMBuffer) {
140
- throw new Error(
141
- `Could not find ${skStorageSuffix} for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
142
- );
143
- }
163
+ const skM = GrumpkinScalar.fromBuffer(skMBuffer);
144
164
 
145
- const skM = GrumpkinScalar.fromBuffer(skMBuffer);
165
+ // The remaining awaits are non-DB computations. They are safe because no further IDB operations follow them.
166
+ const computedPkMHash = await hashPublicKey(pkM);
167
+ if (!computedPkMHash.equals(pkMHash)) {
168
+ throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
169
+ }
146
170
 
147
- // We sanity check that it's possible to derive the public key from the secret key
148
- const derivedPkM = await derivePublicKeyFromSecretKey(skM);
149
- if (!derivedPkM.equals(pkM)) {
150
- throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
151
- }
171
+ const derivedPkM = await derivePublicKeyFromSecretKey(skM);
172
+ if (!derivedPkM.equals(pkM)) {
173
+ throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
174
+ }
152
175
 
153
- // At last we silo the secret key and return the key validation request
154
- const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix!);
176
+ const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix!);
155
177
 
156
- return new KeyValidationRequest(pkM, skApp);
178
+ return new KeyValidationRequest(pkMHash, skApp);
179
+ });
157
180
  }
158
181
 
159
182
  /**
160
183
  * Gets the master nullifier public key for a given account.
161
184
  * @throws If the account does not exist in the key store.
162
- * @param account - The account address for which to retrieve the master nullifier public key.
163
- * @returns The master nullifier public key for the account.
164
185
  */
165
- public async getMasterNullifierPublicKey(account: AztecAddress): Promise<PublicKey> {
166
- const masterNullifierPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-npk_m`);
167
- if (!masterNullifierPublicKeyBuffer) {
168
- throw new Error(
169
- `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
170
- );
171
- }
172
- return Point.fromBuffer(masterNullifierPublicKeyBuffer);
186
+ public async getMasterNullifierHidingPublicKey(account: AztecAddress): Promise<PublicKey> {
187
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'npk_m'));
173
188
  }
174
189
 
175
190
  /**
176
191
  * Gets the master incoming viewing public key for a given account.
177
192
  * @throws If the account does not exist in the key store.
178
- * @param account - The account address for which to retrieve the master incoming viewing public key.
179
- * @returns The master incoming viewing public key for the account.
180
193
  */
181
194
  public async getMasterIncomingViewingPublicKey(account: AztecAddress): Promise<PublicKey> {
182
- const masterIncomingViewingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ivpk_m`);
183
- if (!masterIncomingViewingPublicKeyBuffer) {
184
- throw new Error(
185
- `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
186
- );
187
- }
188
- return Point.fromBuffer(masterIncomingViewingPublicKeyBuffer);
195
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'ivpk_m'));
189
196
  }
190
197
 
191
198
  /**
192
199
  * Retrieves the master outgoing viewing public key.
193
200
  * @throws If the account does not exist in the key store.
194
- * @param account - The account to retrieve the master outgoing viewing key for.
195
- * @returns A Promise that resolves to the master outgoing viewing key.
196
201
  */
197
202
  public async getMasterOutgoingViewingPublicKey(account: AztecAddress): Promise<PublicKey> {
198
- const masterOutgoingViewingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ovpk_m`);
199
- if (!masterOutgoingViewingPublicKeyBuffer) {
200
- throw new Error(
201
- `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
202
- );
203
- }
204
- return Point.fromBuffer(masterOutgoingViewingPublicKeyBuffer);
203
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'ovpk_m'));
205
204
  }
206
205
 
207
206
  /**
208
207
  * Retrieves the master tagging public key.
209
208
  * @throws If the account does not exist in the key store.
210
- * @param account - The account to retrieve the master tagging key for.
211
- * @returns A Promise that resolves to the master tagging key.
212
209
  */
213
210
  public async getMasterTaggingPublicKey(account: AztecAddress): Promise<PublicKey> {
214
- const masterTaggingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-tpk_m`);
215
- if (!masterTaggingPublicKeyBuffer) {
216
- throw new Error(
217
- `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
218
- );
219
- }
220
- return Point.fromBuffer(masterTaggingPublicKeyBuffer);
211
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'tpk_m'));
221
212
  }
222
213
 
223
214
  /**
224
215
  * Retrieves master incoming viewing secret key.
225
216
  * @throws If the account does not exist in the key store.
226
- * @param account - The account to retrieve the master incoming viewing secret key for.
227
- * @returns A Promise that resolves to the master incoming viewing secret key.
228
217
  */
229
218
  public async getMasterIncomingViewingSecretKey(account: AztecAddress): Promise<GrumpkinScalar> {
230
- const masterIncomingViewingSecretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ivsk_m`);
231
- if (!masterIncomingViewingSecretKeyBuffer) {
232
- throw new Error(
233
- `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
234
- );
235
- }
236
- return GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer);
219
+ return GrumpkinScalar.fromBuffer(await this.#getMasterKeyBuffer(account, 'ivsk_m'));
220
+ }
221
+
222
+ /**
223
+ * Retrieves the four privacy secret keys the key store holds for an account. Paired with {@link addAccount}, this
224
+ * allows exporting an account's privacy secret keys, e.g. to re-register it on another PXE. The message-signing and
225
+ * fallback secret keys are not held by the key store and so are not returned.
226
+ *
227
+ * @throws If the account does not exist in the key store.
228
+ */
229
+ public async getAccountSecretKeys(account: AztecAddress): Promise<AccountPrivacySecretKeys> {
230
+ const [nhkM, ivskM, ovskM, tskM] = await this.#getMasterKeyBuffers(account, ['nhk_m', 'ivsk_m', 'ovsk_m', 'tsk_m']);
231
+ return {
232
+ masterNullifierHidingSecretKey: GrumpkinScalar.fromBuffer(nhkM),
233
+ masterIncomingViewingSecretKey: GrumpkinScalar.fromBuffer(ivskM),
234
+ masterOutgoingViewingSecretKey: GrumpkinScalar.fromBuffer(ovskM),
235
+ masterTaggingSecretKey: GrumpkinScalar.fromBuffer(tskM),
236
+ };
237
237
  }
238
238
 
239
239
  /**
@@ -244,13 +244,7 @@ export class KeyStore {
244
244
  * @returns A Promise that resolves to the application outgoing viewing secret key.
245
245
  */
246
246
  public async getAppOutgoingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise<Fr> {
247
- const masterOutgoingViewingSecretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ovsk_m`);
248
- if (!masterOutgoingViewingSecretKeyBuffer) {
249
- throw new Error(
250
- `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
251
- );
252
- }
253
- const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(masterOutgoingViewingSecretKeyBuffer);
247
+ const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(await this.#getMasterKeyBuffer(account, 'ovsk_m'));
254
248
 
255
249
  return poseidon2HashWithSeparator(
256
250
  [masterOutgoingViewingSecretKey.hi, masterOutgoingViewingSecretKey.lo, app],
@@ -259,30 +253,41 @@ export class KeyStore {
259
253
  }
260
254
 
261
255
  /**
262
- * Retrieves the sk_m corresponding to the pk_m.
263
- * @throws If the provided public key is not associated with any of the registered accounts.
264
- * @param pkM - The master public key to get secret key for.
256
+ * Retrieves the sk_m corresponding to the given pk_m hash.
257
+ * @throws If the provided hash is not associated with any of the registered accounts.
258
+ * @param pkMHash - The master public key hash to get secret key for.
265
259
  * @returns A Promise that resolves to sk_m.
266
260
  * @dev Used when feeding the sk_m to the kernel circuit for keys verification.
267
261
  */
268
- public async getMasterSecretKey(pkM: PublicKey): Promise<GrumpkinScalar> {
269
- const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkM);
270
-
271
- const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
272
- const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
273
- if (!secretKeyBuffer) {
274
- throw new Error(
275
- `Could not find ${skStorageSuffix} for ${keyPrefix}pk_m ${pkM.toString()}. This should not happen.`,
276
- );
277
- }
262
+ public getMasterSecretKey(pkMHash: Fr): Promise<GrumpkinScalar> {
263
+ return this.#db.transactionAsync(async () => {
264
+ const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
265
+
266
+ const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
267
+ const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
268
+ if (!secretKeyBuffer) {
269
+ throw new Error(
270
+ `Could not find ${skStorageSuffix} for ${keyPrefix}pk_m_hash ${pkMHash.toString()}. This should not happen.`,
271
+ );
272
+ }
278
273
 
279
- const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
280
- const derivedpkM = await derivePublicKeyFromSecretKey(skM);
281
- if (!derivedpkM.equals(pkM)) {
282
- throw new Error(`Could not find ${skStorageSuffix} for ${keyPrefix}pkM ${pkM.toString()} in secret keys buffer.`);
283
- }
274
+ const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
275
+
276
+ // Non-DB computation — safe because no further IDB operations follow.
277
+ // Integrity check: confirm the stored secret key still derives the requested hash. The check
278
+ // is hash-based rather than point-equal because the on-disk identifier is `pk_m_hash`;
279
+ // cryptographic collision resistance of `hashPublicKey` makes this equivalent to a
280
+ // direct point comparison in practice.
281
+ const derivedPkM = await derivePublicKeyFromSecretKey(skM);
282
+ const derivedPkMHash = await hashPublicKey(derivedPkM);
283
+ if (!derivedPkMHash.equals(pkMHash)) {
284
+ throw new Error(
285
+ `Could not find ${skStorageSuffix} for ${keyPrefix}pk_m_hash ${pkMHash.toString()} in secret keys buffer.`,
286
+ );
287
+ }
284
288
 
285
- return Promise.resolve(skM);
289
+ return skM;
290
+ });
286
291
  }
287
292
 
288
293
  /**
@@ -291,15 +296,17 @@ export class KeyStore {
291
296
  * @param pkMHash - The master public key hash to look for.
292
297
  * @returns True if the account has a key with the given hash.
293
298
  */
294
- public async accountHasKey(account: AztecAddress, pkMHash: Fr): Promise<boolean> {
295
- const pkMHashBuffer = serializeToBuffer(pkMHash);
296
- for (const prefix of KEY_PREFIXES) {
297
- const stored = await this.#keys.getAsync(`${account.toString()}-${prefix}pk_m_hash`);
298
- if (stored && Buffer.from(stored).equals(pkMHashBuffer)) {
299
- return true;
299
+ public accountHasKey(account: AztecAddress, pkMHash: Fr): Promise<boolean> {
300
+ return this.#db.transactionAsync(async () => {
301
+ const pkMHashBuffer = serializeToBuffer(pkMHash);
302
+ for (const prefix of KEY_PREFIXES) {
303
+ const stored = await this.#keys.getAsync(`${account.toString()}-${prefix}pk_m_hash`);
304
+ if (stored && Buffer.from(stored).equals(pkMHashBuffer)) {
305
+ return true;
306
+ }
300
307
  }
301
- }
302
- return false;
308
+ return false;
309
+ });
303
310
  }
304
311
 
305
312
  /**
@@ -315,7 +322,7 @@ export class KeyStore {
315
322
  if (Buffer.from(val).equals(valueBuffer)) {
316
323
  for (const prefix of KEY_PREFIXES) {
317
324
  if (key.includes(`-${prefix}`)) {
318
- const account = AztecAddress.fromString(key.split('-')[0]);
325
+ const account = AztecAddress.fromStringUnsafe(key.split('-')[0]);
319
326
  return [prefix, account];
320
327
  }
321
328
  }
@@ -323,4 +330,83 @@ export class KeyStore {
323
330
  }
324
331
  throw new Error(`Could not find key prefix.`);
325
332
  }
333
+
334
+ /**
335
+ * Persists a completed set of account keys and returns the resulting complete address.
336
+ */
337
+ async #storeAccountKeys(
338
+ accountKeys: Awaited<ReturnType<typeof completeAccountKeys>>,
339
+ partialAddress: PartialAddress,
340
+ ): Promise<CompleteAddress> {
341
+ const {
342
+ masterNullifierHidingSecretKey,
343
+ masterIncomingViewingSecretKey,
344
+ masterOutgoingViewingSecretKey,
345
+ masterTaggingSecretKey,
346
+ masterNullifierHidingPublicKey,
347
+ masterOutgoingViewingPublicKey,
348
+ masterTaggingPublicKey,
349
+ publicKeys,
350
+ } = accountKeys;
351
+
352
+ const completeAddress = await CompleteAddress.fromPublicKeysAndPartialAddress(publicKeys, partialAddress);
353
+ const { address: account } = completeAddress;
354
+
355
+ // completeAccountKeys has already guaranteed these master public keys are non-infinity, which the kernel cannot
356
+ // check but the address relies on.
357
+
358
+ // The npk/ovpk/tpk hashes are already in publicKeys; ivpk_m_hash is computed for indexing.
359
+ const masterIncomingViewingPublicKeyHash = await hashPublicKey(publicKeys.ivpkM);
360
+
361
+ await this.#db.transactionAsync(async () => {
362
+ // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m.
363
+ //
364
+ // The message-signing and fallback keys are not stored: their secret keys are withheld from the key store, and
365
+ // their public keys are only needed transiently to compute the address (they live in the AddressStore).
366
+ await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
367
+ await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
368
+ await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
369
+ await this.#keys.set(`${account.toString()}-nhk_m`, masterNullifierHidingSecretKey.toBuffer());
370
+
371
+ await this.#keys.set(`${account.toString()}-npk_m`, masterNullifierHidingPublicKey.toBuffer());
372
+ await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.ivpkM.toBuffer());
373
+ await this.#keys.set(`${account.toString()}-ovpk_m`, masterOutgoingViewingPublicKey.toBuffer());
374
+ await this.#keys.set(`${account.toString()}-tpk_m`, masterTaggingPublicKey.toBuffer());
375
+
376
+ // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
377
+ // using the #getKeyPrefixAndAccount function later on
378
+ await this.#keys.set(`${account.toString()}-npk_m_hash`, publicKeys.npkMHash.toBuffer());
379
+ await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
380
+ await this.#keys.set(`${account.toString()}-ovpk_m_hash`, publicKeys.ovpkMHash.toBuffer());
381
+ await this.#keys.set(`${account.toString()}-tpk_m_hash`, publicKeys.tpkMHash.toBuffer());
382
+ });
383
+
384
+ return completeAddress;
385
+ }
386
+
387
+ /**
388
+ * Fetches a stored master key buffer for an account by its storage suffix (e.g. `npk_m`, `ivsk_m`).
389
+ * @throws If the account does not exist in the key store.
390
+ */
391
+ async #getMasterKeyBuffer(account: AztecAddress, suffix: string): Promise<Buffer> {
392
+ const [buffer] = await this.#getMasterKeyBuffers(account, [suffix]);
393
+ return buffer;
394
+ }
395
+
396
+ /**
397
+ * Fetches multiple stored master key buffers for an account in a single transaction, returning them in the order of
398
+ * the requested storage suffixes.
399
+ * @throws If any of the keys is missing (i.e. the account does not exist in the key store).
400
+ */
401
+ async #getMasterKeyBuffers(account: AztecAddress, suffixes: string[]): Promise<Buffer[]> {
402
+ const buffers = await this.#db.transactionAsync(() =>
403
+ Promise.all(suffixes.map(suffix => this.#keys.getAsync(`${account.toString()}-${suffix}`))),
404
+ );
405
+ if (!buffers.every((buffer): buffer is Buffer => buffer !== undefined)) {
406
+ throw new Error(
407
+ `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
408
+ );
409
+ }
410
+ return buffers;
411
+ }
326
412
  }