@aztec/key-store 0.0.1-commit.d3ec352c → 0.0.1-commit.d58ff9d0
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.
- package/dest/account_privacy_keys.d.ts +23 -0
- package/dest/account_privacy_keys.d.ts.map +1 -0
- package/dest/account_privacy_keys.js +6 -0
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/key_store.d.ts +37 -26
- package/dest/key_store.d.ts.map +1 -1
- package/dest/key_store.js +214 -125
- package/package.json +6 -6
- package/src/account_privacy_keys.ts +24 -0
- package/src/index.ts +1 -0
- package/src/key_store.ts +278 -156
|
@@ -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,
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDJCQUEyQixDQUFDO0FBQzFDLGNBQWMsZ0JBQWdCLENBQUMifQ==
|
package/dest/index.d.ts.map
CHANGED
|
@@ -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
package/dest/key_store.d.ts
CHANGED
|
@@ -1,34 +1,40 @@
|
|
|
1
|
-
import { Fr
|
|
1
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
|
+
import { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
|
|
2
3
|
import { type Bufferable } from '@aztec/foundation/serialize';
|
|
3
4
|
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
4
5
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
5
6
|
import { CompleteAddress, type PartialAddress } from '@aztec/stdlib/contract';
|
|
6
7
|
import { KeyValidationRequest } from '@aztec/stdlib/kernel';
|
|
7
8
|
import { type KeyPrefix, type PublicKey } from '@aztec/stdlib/keys';
|
|
9
|
+
import type { AccountPrivacyKeys, AccountPrivacySecretKeys } from './account_privacy_keys.js';
|
|
8
10
|
/**
|
|
9
11
|
* Used for managing keys. Can hold keys of multiple accounts.
|
|
10
12
|
*/
|
|
11
13
|
export declare class KeyStore {
|
|
12
14
|
#private;
|
|
13
|
-
static readonly SCHEMA_VERSION
|
|
15
|
+
static readonly SCHEMA_VERSION = 1;
|
|
14
16
|
constructor(database: AztecAsyncKVStore);
|
|
15
17
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
*
|
|
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.
|
|
23
26
|
* @param partialAddress - The partial address of the account.
|
|
24
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.
|
|
25
29
|
*/
|
|
26
|
-
addAccount(
|
|
30
|
+
addAccount(keys: AccountPrivacyKeys, partialAddress: PartialAddress): Promise<CompleteAddress>;
|
|
27
31
|
/**
|
|
28
32
|
* Retrieves addresses of accounts stored in the key store.
|
|
29
33
|
* @returns A Promise that resolves to an array of account addresses.
|
|
30
34
|
*/
|
|
31
35
|
getAccounts(): Promise<AztecAddress[]>;
|
|
36
|
+
/** Checks whether an account is registered in the key store. */
|
|
37
|
+
hasAccount(account: AztecAddress): Promise<boolean>;
|
|
32
38
|
/**
|
|
33
39
|
* Gets the key validation request for a given master public key hash and contract address.
|
|
34
40
|
* @throws If the account corresponding to the master public key hash does not exist in the key store.
|
|
@@ -40,38 +46,36 @@ export declare class KeyStore {
|
|
|
40
46
|
/**
|
|
41
47
|
* Gets the master nullifier public key for a given account.
|
|
42
48
|
* @throws If the account does not exist in the key store.
|
|
43
|
-
* @param account - The account address for which to retrieve the master nullifier public key.
|
|
44
|
-
* @returns The master nullifier public key for the account.
|
|
45
49
|
*/
|
|
46
|
-
|
|
50
|
+
getMasterNullifierHidingPublicKey(account: AztecAddress): Promise<PublicKey>;
|
|
47
51
|
/**
|
|
48
52
|
* Gets the master incoming viewing public key for a given account.
|
|
49
53
|
* @throws If the account does not exist in the key store.
|
|
50
|
-
* @param account - The account address for which to retrieve the master incoming viewing public key.
|
|
51
|
-
* @returns The master incoming viewing public key for the account.
|
|
52
54
|
*/
|
|
53
55
|
getMasterIncomingViewingPublicKey(account: AztecAddress): Promise<PublicKey>;
|
|
54
56
|
/**
|
|
55
57
|
* Retrieves the master outgoing viewing public key.
|
|
56
58
|
* @throws If the account does not exist in the key store.
|
|
57
|
-
* @param account - The account to retrieve the master outgoing viewing key for.
|
|
58
|
-
* @returns A Promise that resolves to the master outgoing viewing key.
|
|
59
59
|
*/
|
|
60
60
|
getMasterOutgoingViewingPublicKey(account: AztecAddress): Promise<PublicKey>;
|
|
61
61
|
/**
|
|
62
62
|
* Retrieves the master tagging public key.
|
|
63
63
|
* @throws If the account does not exist in the key store.
|
|
64
|
-
* @param account - The account to retrieve the master tagging key for.
|
|
65
|
-
* @returns A Promise that resolves to the master tagging key.
|
|
66
64
|
*/
|
|
67
65
|
getMasterTaggingPublicKey(account: AztecAddress): Promise<PublicKey>;
|
|
68
66
|
/**
|
|
69
67
|
* Retrieves master incoming viewing secret key.
|
|
70
68
|
* @throws If the account does not exist in the key store.
|
|
71
|
-
* @param account - The account to retrieve the master incoming viewing secret key for.
|
|
72
|
-
* @returns A Promise that resolves to the master incoming viewing secret key.
|
|
73
69
|
*/
|
|
74
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>;
|
|
75
79
|
/**
|
|
76
80
|
* Retrieves application outgoing viewing secret key.
|
|
77
81
|
* @throws If the account does not exist in the key store.
|
|
@@ -81,13 +85,20 @@ export declare class KeyStore {
|
|
|
81
85
|
*/
|
|
82
86
|
getAppOutgoingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise<Fr>;
|
|
83
87
|
/**
|
|
84
|
-
* Retrieves the sk_m corresponding to the pk_m.
|
|
85
|
-
* @throws If the provided
|
|
86
|
-
* @param
|
|
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.
|
|
87
91
|
* @returns A Promise that resolves to sk_m.
|
|
88
92
|
* @dev Used when feeding the sk_m to the kernel circuit for keys verification.
|
|
89
93
|
*/
|
|
90
|
-
getMasterSecretKey(
|
|
94
|
+
getMasterSecretKey(pkMHash: Fr): Promise<GrumpkinScalar>;
|
|
95
|
+
/**
|
|
96
|
+
* Checks whether a given account has a key matching the provided master public key hash.
|
|
97
|
+
* @param account - The account address to check.
|
|
98
|
+
* @param pkMHash - The master public key hash to look for.
|
|
99
|
+
* @returns True if the account has a key with the given hash.
|
|
100
|
+
*/
|
|
101
|
+
accountHasKey(account: AztecAddress, pkMHash: Fr): Promise<boolean>;
|
|
91
102
|
/**
|
|
92
103
|
* Gets the key prefix and account address for a given value.
|
|
93
104
|
* @returns A tuple containing the key prefix and account address.
|
|
@@ -96,4 +107,4 @@ export declare class KeyStore {
|
|
|
96
107
|
*/
|
|
97
108
|
getKeyPrefixAndAccount(value: Bufferable): Promise<[KeyPrefix, AztecAddress]>;
|
|
98
109
|
}
|
|
99
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
110
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5X3N0b3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMva2V5X3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFTLE1BQU0sbUNBQW1DLENBQUM7QUFFMUUsT0FBTyxFQUFFLEtBQUssVUFBVSxFQUFxQixNQUFNLDZCQUE2QixDQUFDO0FBQ2pGLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFpQixNQUFNLGlCQUFpQixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDNUQsT0FBTyxFQUVMLEtBQUssU0FBUyxFQUNkLEtBQUssU0FBUyxFQUtmLE1BQU0sb0JBQW9CLENBQUM7QUFFNUIsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQStEOUY7O0dBRUc7QUFDSCxxQkFBYSxRQUFROztJQUNuQixnQkFBdUIsY0FBYyxLQUFLO0lBSTFDLFlBQVksUUFBUSxFQUFFLGlCQUFpQixFQUd0QztJQUVEOzs7Ozs7Ozs7Ozs7T0FZRztJQUNVLFVBQVUsQ0FBQyxJQUFJLEVBQUUsa0JBQWtCLEVBQUUsY0FBYyxFQUFFLGNBQWMsR0FBRyxPQUFPLENBQUMsZUFBZSxDQUFDLENBRzFHO0lBRUQ7OztPQUdHO0lBQ1UsV0FBVyxJQUFJLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUtsRDtJQUVELGdFQUFnRTtJQUNuRCxVQUFVLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRS9EO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksdUJBQXVCLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxlQUFlLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQTBDeEc7SUFFRDs7O09BR0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFeEY7SUFFRDs7O09BR0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFeEY7SUFFRDs7O09BR0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFeEY7SUFFRDs7O09BR0c7SUFDVSx5QkFBeUIsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFaEY7SUFFRDs7O09BR0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FFN0Y7SUFFRDs7Ozs7O09BTUc7SUFDVSxvQkFBb0IsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxDQVExRjtJQUVEOzs7Ozs7T0FNRztJQUNVLDhCQUE4QixDQUFDLE9BQU8sRUFBRSxZQUFZLEVBQUUsR0FBRyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDLENBT2pHO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksa0JBQWtCLENBQUMsT0FBTyxFQUFFLEVBQUUsR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDLENBNkI5RDtJQUVEOzs7OztPQUtHO0lBQ0ksYUFBYSxDQUFDLE9BQU8sRUFBRSxZQUFZLEVBQUUsT0FBTyxFQUFFLEVBQUUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBV3pFO0lBRUQ7Ozs7O09BS0c7SUFDVSxzQkFBc0IsQ0FBQyxLQUFLLEVBQUUsVUFBVSxHQUFHLE9BQU8sQ0FBQyxDQUFDLFNBQVMsRUFBRSxZQUFZLENBQUMsQ0FBQyxDQWN6RjtDQWdGRiJ9
|
package/dest/key_store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"key_store.d.ts","sourceRoot":"","sources":["../src/key_store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,EAAE,EAAE,cAAc,EAAS,MAAM,
|
|
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,67 +1,88 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto';
|
|
3
|
-
import {
|
|
1
|
+
import { DomainSeparator } from '@aztec/constants';
|
|
2
|
+
import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon';
|
|
3
|
+
import { GrumpkinScalar, Point } from '@aztec/foundation/curves/grumpkin';
|
|
4
4
|
import { toArray } from '@aztec/foundation/iterable';
|
|
5
5
|
import { serializeToBuffer } from '@aztec/foundation/serialize';
|
|
6
6
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
7
7
|
import { CompleteAddress } from '@aztec/stdlib/contract';
|
|
8
8
|
import { KeyValidationRequest } from '@aztec/stdlib/kernel';
|
|
9
|
-
import { KEY_PREFIXES,
|
|
9
|
+
import { KEY_PREFIXES, PublicKeys, computeAppSecretKey, derivePublicKeyFromSecretKey, hashPublicKey } from '@aztec/stdlib/keys';
|
|
10
|
+
/** Maps a key prefix to the storage suffix for the corresponding master secret key. */ function secretKeyStorageSuffix(prefix) {
|
|
11
|
+
return prefix === 'n' ? 'nhk_m' : `${prefix}sk_m`;
|
|
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
|
+
}
|
|
10
49
|
/**
|
|
11
50
|
* Used for managing keys. Can hold keys of multiple accounts.
|
|
12
51
|
*/ export class KeyStore {
|
|
13
52
|
static SCHEMA_VERSION = 1;
|
|
53
|
+
#db;
|
|
14
54
|
#keys;
|
|
15
55
|
constructor(database){
|
|
56
|
+
this.#db = database;
|
|
16
57
|
this.#keys = database.openMap('key_store');
|
|
17
58
|
}
|
|
18
59
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
* Adds an account to the key store from the provided secret key.
|
|
28
|
-
* @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.
|
|
29
68
|
* @param partialAddress - The partial address of the account.
|
|
30
69
|
* @returns The account's complete address.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
// Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
|
|
36
|
-
await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
|
|
37
|
-
await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
|
|
38
|
-
await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
|
|
39
|
-
await this.#keys.set(`${account.toString()}-nsk_m`, masterNullifierSecretKey.toBuffer());
|
|
40
|
-
await this.#keys.set(`${account.toString()}-npk_m`, publicKeys.masterNullifierPublicKey.toBuffer());
|
|
41
|
-
await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.masterIncomingViewingPublicKey.toBuffer());
|
|
42
|
-
await this.#keys.set(`${account.toString()}-ovpk_m`, publicKeys.masterOutgoingViewingPublicKey.toBuffer());
|
|
43
|
-
await this.#keys.set(`${account.toString()}-tpk_m`, publicKeys.masterTaggingPublicKey.toBuffer());
|
|
44
|
-
// We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
|
|
45
|
-
// using the #getKeyPrefixAndAccount function later on
|
|
46
|
-
const masterNullifierPublicKeyHash = await publicKeys.masterNullifierPublicKey.hash();
|
|
47
|
-
await this.#keys.set(`${account.toString()}-npk_m_hash`, masterNullifierPublicKeyHash.toBuffer());
|
|
48
|
-
const masterIncomingViewingPublicKeyHash = await publicKeys.masterIncomingViewingPublicKey.hash();
|
|
49
|
-
await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
|
|
50
|
-
const masterOutgoingViewingPublicKeyHash = await publicKeys.masterOutgoingViewingPublicKey.hash();
|
|
51
|
-
await this.#keys.set(`${account.toString()}-ovpk_m_hash`, masterOutgoingViewingPublicKeyHash.toBuffer());
|
|
52
|
-
const masterTaggingPublicKeyHash = await publicKeys.masterTaggingPublicKey.hash();
|
|
53
|
-
await this.#keys.set(`${account.toString()}-tpk_m_hash`, masterTaggingPublicKeyHash.toBuffer());
|
|
54
|
-
// At last, we return the newly derived account address
|
|
55
|
-
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);
|
|
56
74
|
}
|
|
57
75
|
/**
|
|
58
76
|
* Retrieves addresses of accounts stored in the key store.
|
|
59
77
|
* @returns A Promise that resolves to an array of account addresses.
|
|
60
78
|
*/ async getAccounts() {
|
|
61
|
-
const allMapKeys = await toArray(this.#keys.keysAsync());
|
|
79
|
+
const allMapKeys = await this.#db.transactionAsync(()=>toArray(this.#keys.keysAsync()));
|
|
62
80
|
// We return account addresses based on the map keys that end with '-ivsk_m'
|
|
63
81
|
const accounts = allMapKeys.filter((key)=>key.endsWith('-ivsk_m')).map((key)=>key.split('-')[0]);
|
|
64
|
-
return accounts.map((account)=>AztecAddress.
|
|
82
|
+
return accounts.map((account)=>AztecAddress.fromStringUnsafe(account));
|
|
83
|
+
}
|
|
84
|
+
/** Checks whether an account is registered in the key store. */ async hasAccount(account) {
|
|
85
|
+
return !!await this.#db.transactionAsync(()=>this.#keys.getAsync(`${account.toString()}-ivsk_m`));
|
|
65
86
|
}
|
|
66
87
|
/**
|
|
67
88
|
* Gets the key validation request for a given master public key hash and contract address.
|
|
@@ -69,92 +90,86 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
|
|
|
69
90
|
* @param pkMHash - The master public key hash.
|
|
70
91
|
* @param contractAddress - The contract address to silo the secret key in the key validation request with.
|
|
71
92
|
* @returns The key validation request.
|
|
72
|
-
*/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
+
});
|
|
98
123
|
}
|
|
99
124
|
/**
|
|
100
125
|
* Gets the master nullifier public key for a given account.
|
|
101
126
|
* @throws If the account does not exist in the key store.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
*/ async getMasterNullifierPublicKey(account) {
|
|
105
|
-
const masterNullifierPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-npk_m`);
|
|
106
|
-
if (!masterNullifierPublicKeyBuffer) {
|
|
107
|
-
throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
|
|
108
|
-
}
|
|
109
|
-
return Point.fromBuffer(masterNullifierPublicKeyBuffer);
|
|
127
|
+
*/ async getMasterNullifierHidingPublicKey(account) {
|
|
128
|
+
return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'npk_m'));
|
|
110
129
|
}
|
|
111
130
|
/**
|
|
112
131
|
* Gets the master incoming viewing public key for a given account.
|
|
113
132
|
* @throws If the account does not exist in the key store.
|
|
114
|
-
* @param account - The account address for which to retrieve the master incoming viewing public key.
|
|
115
|
-
* @returns The master incoming viewing public key for the account.
|
|
116
133
|
*/ async getMasterIncomingViewingPublicKey(account) {
|
|
117
|
-
|
|
118
|
-
if (!masterIncomingViewingPublicKeyBuffer) {
|
|
119
|
-
throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
|
|
120
|
-
}
|
|
121
|
-
return Point.fromBuffer(masterIncomingViewingPublicKeyBuffer);
|
|
134
|
+
return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'ivpk_m'));
|
|
122
135
|
}
|
|
123
136
|
/**
|
|
124
137
|
* Retrieves the master outgoing viewing public key.
|
|
125
138
|
* @throws If the account does not exist in the key store.
|
|
126
|
-
* @param account - The account to retrieve the master outgoing viewing key for.
|
|
127
|
-
* @returns A Promise that resolves to the master outgoing viewing key.
|
|
128
139
|
*/ async getMasterOutgoingViewingPublicKey(account) {
|
|
129
|
-
|
|
130
|
-
if (!masterOutgoingViewingPublicKeyBuffer) {
|
|
131
|
-
throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
|
|
132
|
-
}
|
|
133
|
-
return Point.fromBuffer(masterOutgoingViewingPublicKeyBuffer);
|
|
140
|
+
return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'ovpk_m'));
|
|
134
141
|
}
|
|
135
142
|
/**
|
|
136
143
|
* Retrieves the master tagging public key.
|
|
137
144
|
* @throws If the account does not exist in the key store.
|
|
138
|
-
* @param account - The account to retrieve the master tagging key for.
|
|
139
|
-
* @returns A Promise that resolves to the master tagging key.
|
|
140
145
|
*/ async getMasterTaggingPublicKey(account) {
|
|
141
|
-
|
|
142
|
-
if (!masterTaggingPublicKeyBuffer) {
|
|
143
|
-
throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
|
|
144
|
-
}
|
|
145
|
-
return Point.fromBuffer(masterTaggingPublicKeyBuffer);
|
|
146
|
+
return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'tpk_m'));
|
|
146
147
|
}
|
|
147
148
|
/**
|
|
148
149
|
* Retrieves master incoming viewing secret key.
|
|
149
150
|
* @throws If the account does not exist in the key store.
|
|
150
|
-
* @param account - The account to retrieve the master incoming viewing secret key for.
|
|
151
|
-
* @returns A Promise that resolves to the master incoming viewing secret key.
|
|
152
151
|
*/ async getMasterIncomingViewingSecretKey(account) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
+
};
|
|
158
173
|
}
|
|
159
174
|
/**
|
|
160
175
|
* Retrieves application outgoing viewing secret key.
|
|
@@ -163,35 +178,57 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
|
|
|
163
178
|
* @param app - The application address to retrieve the outgoing viewing secret key for.
|
|
164
179
|
* @returns A Promise that resolves to the application outgoing viewing secret key.
|
|
165
180
|
*/ async getAppOutgoingViewingSecretKey(account, app) {
|
|
166
|
-
const
|
|
167
|
-
if (!masterOutgoingViewingSecretKeyBuffer) {
|
|
168
|
-
throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
|
|
169
|
-
}
|
|
170
|
-
const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(masterOutgoingViewingSecretKeyBuffer);
|
|
181
|
+
const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(await this.#getMasterKeyBuffer(account, 'ovsk_m'));
|
|
171
182
|
return poseidon2HashWithSeparator([
|
|
172
183
|
masterOutgoingViewingSecretKey.hi,
|
|
173
184
|
masterOutgoingViewingSecretKey.lo,
|
|
174
185
|
app
|
|
175
|
-
],
|
|
186
|
+
], DomainSeparator.OVSK_M);
|
|
176
187
|
}
|
|
177
188
|
/**
|
|
178
|
-
* Retrieves the sk_m corresponding to the pk_m.
|
|
179
|
-
* @throws If the provided
|
|
180
|
-
* @param
|
|
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.
|
|
181
192
|
* @returns A Promise that resolves to sk_m.
|
|
182
193
|
* @dev Used when feeding the sk_m to the kernel circuit for keys verification.
|
|
183
|
-
*/
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
+
});
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Checks whether a given account has a key matching the provided master public key hash.
|
|
218
|
+
* @param account - The account address to check.
|
|
219
|
+
* @param pkMHash - The master public key hash to look for.
|
|
220
|
+
* @returns True if the account has a key with the given hash.
|
|
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
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
});
|
|
195
232
|
}
|
|
196
233
|
/**
|
|
197
234
|
* Gets the key prefix and account address for a given value.
|
|
@@ -205,7 +242,7 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
|
|
|
205
242
|
if (Buffer.from(val).equals(valueBuffer)) {
|
|
206
243
|
for (const prefix of KEY_PREFIXES){
|
|
207
244
|
if (key.includes(`-${prefix}`)) {
|
|
208
|
-
const account = AztecAddress.
|
|
245
|
+
const account = AztecAddress.fromStringUnsafe(key.split('-')[0]);
|
|
209
246
|
return [
|
|
210
247
|
prefix,
|
|
211
248
|
account
|
|
@@ -216,4 +253,56 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
|
|
|
216
253
|
}
|
|
217
254
|
throw new Error(`Could not find key prefix.`);
|
|
218
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
|
+
}
|
|
219
308
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/key-store",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.d58ff9d0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": "./dest/index.js",
|
|
6
6
|
"typedocOptions": {
|
|
@@ -57,17 +57,17 @@
|
|
|
57
57
|
]
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@aztec/constants": "0.0.1-commit.
|
|
61
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
62
|
-
"@aztec/kv-store": "0.0.1-commit.
|
|
63
|
-
"@aztec/stdlib": "0.0.1-commit.
|
|
60
|
+
"@aztec/constants": "0.0.1-commit.d58ff9d0",
|
|
61
|
+
"@aztec/foundation": "0.0.1-commit.d58ff9d0",
|
|
62
|
+
"@aztec/kv-store": "0.0.1-commit.d58ff9d0",
|
|
63
|
+
"@aztec/stdlib": "0.0.1-commit.d58ff9d0",
|
|
64
64
|
"tslib": "^2.4.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@jest/globals": "^30.0.0",
|
|
68
68
|
"@types/jest": "^30.0.0",
|
|
69
69
|
"@types/node": "^22.15.17",
|
|
70
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
70
|
+
"@typescript/native-preview": "7.0.0-dev.20260113.1",
|
|
71
71
|
"jest": "^30.0.0",
|
|
72
72
|
"ts-node": "^10.9.1",
|
|
73
73
|
"typescript": "^5.3.3"
|
|
@@ -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
package/src/key_store.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto';
|
|
3
|
-
import { Fr
|
|
1
|
+
import { DomainSeparator } from '@aztec/constants';
|
|
2
|
+
import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon';
|
|
3
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
|
+
import { GrumpkinScalar, Point } from '@aztec/foundation/curves/grumpkin';
|
|
4
5
|
import { toArray } from '@aztec/foundation/iterable';
|
|
5
6
|
import { type Bufferable, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
6
7
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
@@ -11,74 +12,104 @@ import {
|
|
|
11
12
|
KEY_PREFIXES,
|
|
12
13
|
type KeyPrefix,
|
|
13
14
|
type PublicKey,
|
|
15
|
+
PublicKeys,
|
|
14
16
|
computeAppSecretKey,
|
|
15
|
-
deriveKeys,
|
|
16
17
|
derivePublicKeyFromSecretKey,
|
|
18
|
+
hashPublicKey,
|
|
17
19
|
} from '@aztec/stdlib/keys';
|
|
18
20
|
|
|
21
|
+
import type { AccountPrivacyKeys, AccountPrivacySecretKeys } from './account_privacy_keys.js';
|
|
22
|
+
|
|
23
|
+
/** Maps a key prefix to the storage suffix for the corresponding master secret key. */
|
|
24
|
+
function secretKeyStorageSuffix(prefix: KeyPrefix): string {
|
|
25
|
+
return prefix === 'n' ? 'nhk_m' : `${prefix}sk_m`;
|
|
26
|
+
}
|
|
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
|
+
|
|
19
84
|
/**
|
|
20
85
|
* Used for managing keys. Can hold keys of multiple accounts.
|
|
21
86
|
*/
|
|
22
87
|
export class KeyStore {
|
|
23
88
|
public static readonly SCHEMA_VERSION = 1;
|
|
89
|
+
#db: AztecAsyncKVStore;
|
|
24
90
|
#keys: AztecAsyncMap<string, Buffer>;
|
|
25
91
|
|
|
26
92
|
constructor(database: AztecAsyncKVStore) {
|
|
93
|
+
this.#db = database;
|
|
27
94
|
this.#keys = database.openMap('key_store');
|
|
28
95
|
}
|
|
29
96
|
|
|
30
97
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Adds an account to the key store from the provided secret key.
|
|
42
|
-
* @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.
|
|
43
106
|
* @param partialAddress - The partial address of the account.
|
|
44
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.
|
|
45
109
|
*/
|
|
46
|
-
public async addAccount(
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
masterIncomingViewingSecretKey,
|
|
50
|
-
masterOutgoingViewingSecretKey,
|
|
51
|
-
masterTaggingSecretKey,
|
|
52
|
-
publicKeys,
|
|
53
|
-
} = await deriveKeys(sk);
|
|
54
|
-
|
|
55
|
-
const completeAddress = await CompleteAddress.fromSecretKeyAndPartialAddress(sk, partialAddress);
|
|
56
|
-
const { address: account } = completeAddress;
|
|
57
|
-
|
|
58
|
-
// Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
|
|
59
|
-
await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
|
|
60
|
-
await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
|
|
61
|
-
await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
|
|
62
|
-
await this.#keys.set(`${account.toString()}-nsk_m`, masterNullifierSecretKey.toBuffer());
|
|
63
|
-
|
|
64
|
-
await this.#keys.set(`${account.toString()}-npk_m`, publicKeys.masterNullifierPublicKey.toBuffer());
|
|
65
|
-
await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.masterIncomingViewingPublicKey.toBuffer());
|
|
66
|
-
await this.#keys.set(`${account.toString()}-ovpk_m`, publicKeys.masterOutgoingViewingPublicKey.toBuffer());
|
|
67
|
-
await this.#keys.set(`${account.toString()}-tpk_m`, publicKeys.masterTaggingPublicKey.toBuffer());
|
|
68
|
-
|
|
69
|
-
// We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
|
|
70
|
-
// using the #getKeyPrefixAndAccount function later on
|
|
71
|
-
const masterNullifierPublicKeyHash = await publicKeys.masterNullifierPublicKey.hash();
|
|
72
|
-
await this.#keys.set(`${account.toString()}-npk_m_hash`, masterNullifierPublicKeyHash.toBuffer());
|
|
73
|
-
const masterIncomingViewingPublicKeyHash = await publicKeys.masterIncomingViewingPublicKey.hash();
|
|
74
|
-
await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
|
|
75
|
-
const masterOutgoingViewingPublicKeyHash = await publicKeys.masterOutgoingViewingPublicKey.hash();
|
|
76
|
-
await this.#keys.set(`${account.toString()}-ovpk_m_hash`, masterOutgoingViewingPublicKeyHash.toBuffer());
|
|
77
|
-
const masterTaggingPublicKeyHash = await publicKeys.masterTaggingPublicKey.hash();
|
|
78
|
-
await this.#keys.set(`${account.toString()}-tpk_m_hash`, masterTaggingPublicKeyHash.toBuffer());
|
|
79
|
-
|
|
80
|
-
// At last, we return the newly derived account address
|
|
81
|
-
return completeAddress;
|
|
110
|
+
public async addAccount(keys: AccountPrivacyKeys, partialAddress: PartialAddress): Promise<CompleteAddress> {
|
|
111
|
+
const accountKeys = await completeAccountKeys(keys);
|
|
112
|
+
return this.#storeAccountKeys(accountKeys, partialAddress);
|
|
82
113
|
}
|
|
83
114
|
|
|
84
115
|
/**
|
|
@@ -86,10 +117,15 @@ export class KeyStore {
|
|
|
86
117
|
* @returns A Promise that resolves to an array of account addresses.
|
|
87
118
|
*/
|
|
88
119
|
public async getAccounts(): Promise<AztecAddress[]> {
|
|
89
|
-
const allMapKeys = await toArray(this.#keys.keysAsync());
|
|
120
|
+
const allMapKeys = await this.#db.transactionAsync(() => toArray(this.#keys.keysAsync()));
|
|
90
121
|
// We return account addresses based on the map keys that end with '-ivsk_m'
|
|
91
122
|
const accounts = allMapKeys.filter(key => key.endsWith('-ivsk_m')).map(key => key.split('-')[0]);
|
|
92
|
-
return accounts.map(account => AztecAddress.
|
|
123
|
+
return accounts.map(account => AztecAddress.fromStringUnsafe(account));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Checks whether an account is registered in the key store. */
|
|
127
|
+
public async hasAccount(account: AztecAddress): Promise<boolean> {
|
|
128
|
+
return !!(await this.#db.transactionAsync(() => this.#keys.getAsync(`${account.toString()}-ivsk_m`)));
|
|
93
129
|
}
|
|
94
130
|
|
|
95
131
|
/**
|
|
@@ -99,123 +135,105 @@ export class KeyStore {
|
|
|
99
135
|
* @param contractAddress - The contract address to silo the secret key in the key validation request with.
|
|
100
136
|
* @returns The key validation request.
|
|
101
137
|
*/
|
|
102
|
-
public
|
|
103
|
-
|
|
138
|
+
public getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise<KeyValidationRequest> {
|
|
139
|
+
return this.#db.transactionAsync(async () => {
|
|
140
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
|
|
104
141
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
+
}
|
|
112
151
|
|
|
113
|
-
|
|
114
|
-
const computedPkMHash = await pkM.hash();
|
|
115
|
-
if (!computedPkMHash.equals(pkMHash)) {
|
|
116
|
-
throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
|
|
117
|
-
}
|
|
152
|
+
const pkM = Point.fromBuffer(pkMBuffer);
|
|
118
153
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
+
}
|
|
126
162
|
|
|
127
|
-
|
|
163
|
+
const skM = GrumpkinScalar.fromBuffer(skMBuffer);
|
|
128
164
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
+
}
|
|
170
|
+
|
|
171
|
+
const derivedPkM = await derivePublicKeyFromSecretKey(skM);
|
|
172
|
+
if (!derivedPkM.equals(pkM)) {
|
|
173
|
+
throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
|
|
174
|
+
}
|
|
134
175
|
|
|
135
|
-
|
|
136
|
-
const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix!);
|
|
176
|
+
const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix!);
|
|
137
177
|
|
|
138
|
-
|
|
178
|
+
return new KeyValidationRequest(pkMHash, skApp);
|
|
179
|
+
});
|
|
139
180
|
}
|
|
140
181
|
|
|
141
182
|
/**
|
|
142
183
|
* Gets the master nullifier public key for a given account.
|
|
143
184
|
* @throws If the account does not exist in the key store.
|
|
144
|
-
* @param account - The account address for which to retrieve the master nullifier public key.
|
|
145
|
-
* @returns The master nullifier public key for the account.
|
|
146
185
|
*/
|
|
147
|
-
public async
|
|
148
|
-
|
|
149
|
-
if (!masterNullifierPublicKeyBuffer) {
|
|
150
|
-
throw new Error(
|
|
151
|
-
`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
return Point.fromBuffer(masterNullifierPublicKeyBuffer);
|
|
186
|
+
public async getMasterNullifierHidingPublicKey(account: AztecAddress): Promise<PublicKey> {
|
|
187
|
+
return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'npk_m'));
|
|
155
188
|
}
|
|
156
189
|
|
|
157
190
|
/**
|
|
158
191
|
* Gets the master incoming viewing public key for a given account.
|
|
159
192
|
* @throws If the account does not exist in the key store.
|
|
160
|
-
* @param account - The account address for which to retrieve the master incoming viewing public key.
|
|
161
|
-
* @returns The master incoming viewing public key for the account.
|
|
162
193
|
*/
|
|
163
194
|
public async getMasterIncomingViewingPublicKey(account: AztecAddress): Promise<PublicKey> {
|
|
164
|
-
|
|
165
|
-
if (!masterIncomingViewingPublicKeyBuffer) {
|
|
166
|
-
throw new Error(
|
|
167
|
-
`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
return Point.fromBuffer(masterIncomingViewingPublicKeyBuffer);
|
|
195
|
+
return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'ivpk_m'));
|
|
171
196
|
}
|
|
172
197
|
|
|
173
198
|
/**
|
|
174
199
|
* Retrieves the master outgoing viewing public key.
|
|
175
200
|
* @throws If the account does not exist in the key store.
|
|
176
|
-
* @param account - The account to retrieve the master outgoing viewing key for.
|
|
177
|
-
* @returns A Promise that resolves to the master outgoing viewing key.
|
|
178
201
|
*/
|
|
179
202
|
public async getMasterOutgoingViewingPublicKey(account: AztecAddress): Promise<PublicKey> {
|
|
180
|
-
|
|
181
|
-
if (!masterOutgoingViewingPublicKeyBuffer) {
|
|
182
|
-
throw new Error(
|
|
183
|
-
`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
return Point.fromBuffer(masterOutgoingViewingPublicKeyBuffer);
|
|
203
|
+
return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'ovpk_m'));
|
|
187
204
|
}
|
|
188
205
|
|
|
189
206
|
/**
|
|
190
207
|
* Retrieves the master tagging public key.
|
|
191
208
|
* @throws If the account does not exist in the key store.
|
|
192
|
-
* @param account - The account to retrieve the master tagging key for.
|
|
193
|
-
* @returns A Promise that resolves to the master tagging key.
|
|
194
209
|
*/
|
|
195
210
|
public async getMasterTaggingPublicKey(account: AztecAddress): Promise<PublicKey> {
|
|
196
|
-
|
|
197
|
-
if (!masterTaggingPublicKeyBuffer) {
|
|
198
|
-
throw new Error(
|
|
199
|
-
`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
return Point.fromBuffer(masterTaggingPublicKeyBuffer);
|
|
211
|
+
return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'tpk_m'));
|
|
203
212
|
}
|
|
204
213
|
|
|
205
214
|
/**
|
|
206
215
|
* Retrieves master incoming viewing secret key.
|
|
207
216
|
* @throws If the account does not exist in the key store.
|
|
208
|
-
* @param account - The account to retrieve the master incoming viewing secret key for.
|
|
209
|
-
* @returns A Promise that resolves to the master incoming viewing secret key.
|
|
210
217
|
*/
|
|
211
218
|
public async getMasterIncomingViewingSecretKey(account: AztecAddress): Promise<GrumpkinScalar> {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
+
};
|
|
219
237
|
}
|
|
220
238
|
|
|
221
239
|
/**
|
|
@@ -226,44 +244,69 @@ export class KeyStore {
|
|
|
226
244
|
* @returns A Promise that resolves to the application outgoing viewing secret key.
|
|
227
245
|
*/
|
|
228
246
|
public async getAppOutgoingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise<Fr> {
|
|
229
|
-
const
|
|
230
|
-
if (!masterOutgoingViewingSecretKeyBuffer) {
|
|
231
|
-
throw new Error(
|
|
232
|
-
`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(masterOutgoingViewingSecretKeyBuffer);
|
|
247
|
+
const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(await this.#getMasterKeyBuffer(account, 'ovsk_m'));
|
|
236
248
|
|
|
237
249
|
return poseidon2HashWithSeparator(
|
|
238
250
|
[masterOutgoingViewingSecretKey.hi, masterOutgoingViewingSecretKey.lo, app],
|
|
239
|
-
|
|
251
|
+
DomainSeparator.OVSK_M,
|
|
240
252
|
);
|
|
241
253
|
}
|
|
242
254
|
|
|
243
255
|
/**
|
|
244
|
-
* Retrieves the sk_m corresponding to the pk_m.
|
|
245
|
-
* @throws If the provided
|
|
246
|
-
* @param
|
|
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.
|
|
247
259
|
* @returns A Promise that resolves to sk_m.
|
|
248
260
|
* @dev Used when feeding the sk_m to the kernel circuit for keys verification.
|
|
249
261
|
*/
|
|
250
|
-
public
|
|
251
|
-
|
|
262
|
+
public getMasterSecretKey(pkMHash: Fr): Promise<GrumpkinScalar> {
|
|
263
|
+
return this.#db.transactionAsync(async () => {
|
|
264
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
|
|
252
265
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
+
}
|
|
259
273
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
+
}
|
|
265
288
|
|
|
266
|
-
|
|
289
|
+
return skM;
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Checks whether a given account has a key matching the provided master public key hash.
|
|
295
|
+
* @param account - The account address to check.
|
|
296
|
+
* @param pkMHash - The master public key hash to look for.
|
|
297
|
+
* @returns True if the account has a key with the given hash.
|
|
298
|
+
*/
|
|
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
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return false;
|
|
309
|
+
});
|
|
267
310
|
}
|
|
268
311
|
|
|
269
312
|
/**
|
|
@@ -279,7 +322,7 @@ export class KeyStore {
|
|
|
279
322
|
if (Buffer.from(val).equals(valueBuffer)) {
|
|
280
323
|
for (const prefix of KEY_PREFIXES) {
|
|
281
324
|
if (key.includes(`-${prefix}`)) {
|
|
282
|
-
const account = AztecAddress.
|
|
325
|
+
const account = AztecAddress.fromStringUnsafe(key.split('-')[0]);
|
|
283
326
|
return [prefix, account];
|
|
284
327
|
}
|
|
285
328
|
}
|
|
@@ -287,4 +330,83 @@ export class KeyStore {
|
|
|
287
330
|
}
|
|
288
331
|
throw new Error(`Could not find key prefix.`);
|
|
289
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
|
+
}
|
|
290
412
|
}
|