@aztec/key-store 0.0.1-commit.9b94fc1 → 0.0.1-commit.9badcec54
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/key_store.d.ts +13 -3
- package/dest/key_store.d.ts.map +1 -1
- package/dest/key_store.js +93 -58
- package/package.json +8 -8
- package/src/key_store.ts +112 -66
package/dest/key_store.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
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';
|
|
@@ -10,7 +11,7 @@ import { type KeyPrefix, type PublicKey } from '@aztec/stdlib/keys';
|
|
|
10
11
|
*/
|
|
11
12
|
export declare class KeyStore {
|
|
12
13
|
#private;
|
|
13
|
-
static readonly SCHEMA_VERSION
|
|
14
|
+
static readonly SCHEMA_VERSION = 1;
|
|
14
15
|
constructor(database: AztecAsyncKVStore);
|
|
15
16
|
/**
|
|
16
17
|
* Creates a new account from a randomly generated secret key.
|
|
@@ -29,6 +30,8 @@ export declare class KeyStore {
|
|
|
29
30
|
* @returns A Promise that resolves to an array of account addresses.
|
|
30
31
|
*/
|
|
31
32
|
getAccounts(): Promise<AztecAddress[]>;
|
|
33
|
+
/** Checks whether an account is registered in the key store. */
|
|
34
|
+
hasAccount(account: AztecAddress): Promise<boolean>;
|
|
32
35
|
/**
|
|
33
36
|
* Gets the key validation request for a given master public key hash and contract address.
|
|
34
37
|
* @throws If the account corresponding to the master public key hash does not exist in the key store.
|
|
@@ -88,6 +91,13 @@ export declare class KeyStore {
|
|
|
88
91
|
* @dev Used when feeding the sk_m to the kernel circuit for keys verification.
|
|
89
92
|
*/
|
|
90
93
|
getMasterSecretKey(pkM: PublicKey): Promise<GrumpkinScalar>;
|
|
94
|
+
/**
|
|
95
|
+
* Checks whether a given account has a key matching the provided master public key hash.
|
|
96
|
+
* @param account - The account address to check.
|
|
97
|
+
* @param pkMHash - The master public key hash to look for.
|
|
98
|
+
* @returns True if the account has a key with the given hash.
|
|
99
|
+
*/
|
|
100
|
+
accountHasKey(account: AztecAddress, pkMHash: Fr): Promise<boolean>;
|
|
91
101
|
/**
|
|
92
102
|
* Gets the key prefix and account address for a given value.
|
|
93
103
|
* @returns A tuple containing the key prefix and account address.
|
|
@@ -96,4 +106,4 @@ export declare class KeyStore {
|
|
|
96
106
|
*/
|
|
97
107
|
getKeyPrefixAndAccount(value: Bufferable): Promise<[KeyPrefix, AztecAddress]>;
|
|
98
108
|
}
|
|
99
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
109
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5X3N0b3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMva2V5X3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFTLE1BQU0sbUNBQW1DLENBQUM7QUFFMUUsT0FBTyxFQUFFLEtBQUssVUFBVSxFQUFxQixNQUFNLDZCQUE2QixDQUFDO0FBQ2pGLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFpQixNQUFNLGlCQUFpQixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDNUQsT0FBTyxFQUVMLEtBQUssU0FBUyxFQUNkLEtBQUssU0FBUyxFQUlmLE1BQU0sb0JBQW9CLENBQUM7QUFPNUI7O0dBRUc7QUFDSCxxQkFBYSxRQUFROztJQUNuQixnQkFBdUIsY0FBYyxLQUFLO0lBSTFDLFlBQVksUUFBUSxFQUFFLGlCQUFpQixFQUd0QztJQUVEOzs7T0FHRztJQUNJLGFBQWEsSUFBSSxPQUFPLENBQUMsZUFBZSxDQUFDLENBSS9DO0lBRUQ7Ozs7O09BS0c7SUFDVSxVQUFVLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxjQUFjLEVBQUUsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0F3Q3hGO0lBRUQ7OztPQUdHO0lBQ1UsV0FBVyxJQUFJLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUtsRDtJQUVELGdFQUFnRTtJQUNuRCxVQUFVLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRS9EO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksdUJBQXVCLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxlQUFlLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQXdDeEc7SUFFRDs7Ozs7T0FLRztJQUNVLDJCQUEyQixDQUFDLE9BQU8sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQVFsRjtJQUVEOzs7OztPQUtHO0lBQ1UsaUNBQWlDLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBUXhGO0lBRUQ7Ozs7O09BS0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FReEY7SUFFRDs7Ozs7T0FLRztJQUNVLHlCQUF5QixDQUFDLE9BQU8sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQVFoRjtJQUVEOzs7OztPQUtHO0lBQ1UsaUNBQWlDLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDLENBUTdGO0lBRUQ7Ozs7OztPQU1HO0lBQ1UsOEJBQThCLENBQUMsT0FBTyxFQUFFLFlBQVksRUFBRSxHQUFHLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0Fhakc7SUFFRDs7Ozs7O09BTUc7SUFDSSxrQkFBa0IsQ0FBQyxHQUFHLEVBQUUsU0FBUyxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0F3QmpFO0lBRUQ7Ozs7O09BS0c7SUFDSSxhQUFhLENBQUMsT0FBTyxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FXekU7SUFFRDs7Ozs7T0FLRztJQUNVLHNCQUFzQixDQUFDLEtBQUssRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLENBQUMsU0FBUyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBY3pGO0NBQ0YifQ==
|
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,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;IACI,uBAAuB,CAAC,OAAO,EAAE,EAAE,EAAE,eAAe,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAwCxG;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;IACI,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC,CAwBjE;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;CACF"}
|
package/dest/key_store.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
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 { serializeToBuffer } from '@aztec/foundation/serialize';
|
|
6
7
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
7
8
|
import { CompleteAddress } from '@aztec/stdlib/contract';
|
|
8
9
|
import { KeyValidationRequest } from '@aztec/stdlib/kernel';
|
|
9
10
|
import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecretKey } from '@aztec/stdlib/keys';
|
|
11
|
+
/** Maps a key prefix to the storage suffix for the corresponding master secret key. */ function secretKeyStorageSuffix(prefix) {
|
|
12
|
+
return prefix === 'n' ? 'nhk_m' : `${prefix}sk_m`;
|
|
13
|
+
}
|
|
10
14
|
/**
|
|
11
15
|
* Used for managing keys. Can hold keys of multiple accounts.
|
|
12
16
|
*/ export class KeyStore {
|
|
13
17
|
static SCHEMA_VERSION = 1;
|
|
18
|
+
#db;
|
|
14
19
|
#keys;
|
|
15
20
|
constructor(database){
|
|
21
|
+
this.#db = database;
|
|
16
22
|
this.#keys = database.openMap('key_store');
|
|
17
23
|
}
|
|
18
24
|
/**
|
|
@@ -29,28 +35,31 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
|
|
|
29
35
|
* @param partialAddress - The partial address of the account.
|
|
30
36
|
* @returns The account's complete address.
|
|
31
37
|
*/ async addAccount(sk, partialAddress) {
|
|
32
|
-
const {
|
|
38
|
+
const { masterNullifierHidingKey, masterIncomingViewingSecretKey, masterOutgoingViewingSecretKey, masterTaggingSecretKey, publicKeys } = await deriveKeys(sk);
|
|
33
39
|
const completeAddress = await CompleteAddress.fromSecretKeyAndPartialAddress(sk, partialAddress);
|
|
34
40
|
const { address: account } = completeAddress;
|
|
35
|
-
//
|
|
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
|
|
41
|
+
// Compute hashes before transaction
|
|
46
42
|
const masterNullifierPublicKeyHash = await publicKeys.masterNullifierPublicKey.hash();
|
|
47
|
-
await this.#keys.set(`${account.toString()}-npk_m_hash`, masterNullifierPublicKeyHash.toBuffer());
|
|
48
43
|
const masterIncomingViewingPublicKeyHash = await publicKeys.masterIncomingViewingPublicKey.hash();
|
|
49
|
-
await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
|
|
50
44
|
const masterOutgoingViewingPublicKeyHash = await publicKeys.masterOutgoingViewingPublicKey.hash();
|
|
51
|
-
await this.#keys.set(`${account.toString()}-ovpk_m_hash`, masterOutgoingViewingPublicKeyHash.toBuffer());
|
|
52
45
|
const masterTaggingPublicKeyHash = await publicKeys.masterTaggingPublicKey.hash();
|
|
53
|
-
await this.#
|
|
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
|
+
});
|
|
54
63
|
// At last, we return the newly derived account address
|
|
55
64
|
return completeAddress;
|
|
56
65
|
}
|
|
@@ -63,38 +72,43 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
|
|
|
63
72
|
const accounts = allMapKeys.filter((key)=>key.endsWith('-ivsk_m')).map((key)=>key.split('-')[0]);
|
|
64
73
|
return accounts.map((account)=>AztecAddress.fromString(account));
|
|
65
74
|
}
|
|
75
|
+
/** Checks whether an account is registered in the key store. */ async hasAccount(account) {
|
|
76
|
+
return !!await this.#keys.getAsync(`${account.toString()}-ivsk_m`);
|
|
77
|
+
}
|
|
66
78
|
/**
|
|
67
79
|
* Gets the key validation request for a given master public key hash and contract address.
|
|
68
80
|
* @throws If the account corresponding to the master public key hash does not exist in the key store.
|
|
69
81
|
* @param pkMHash - The master public key hash.
|
|
70
82
|
* @param contractAddress - The contract address to silo the secret key in the key validation request with.
|
|
71
83
|
* @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
|
-
|
|
84
|
+
*/ getKeyValidationRequest(pkMHash, contractAddress) {
|
|
85
|
+
return this.#db.transactionAsync(async ()=>{
|
|
86
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
|
|
87
|
+
// Now we find the master public key for the account
|
|
88
|
+
const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
|
|
89
|
+
if (!pkMBuffer) {
|
|
90
|
+
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()}.`);
|
|
91
|
+
}
|
|
92
|
+
const pkM = Point.fromBuffer(pkMBuffer);
|
|
93
|
+
// Now we find the secret key for the public key
|
|
94
|
+
const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
|
|
95
|
+
const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
|
|
96
|
+
if (!skMBuffer) {
|
|
97
|
+
throw new Error(`Could not find ${skStorageSuffix} for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
|
|
98
|
+
}
|
|
99
|
+
const skM = GrumpkinScalar.fromBuffer(skMBuffer);
|
|
100
|
+
// The remaining awaits are non-DB computations. They are safe because no further IDB operations follow them.
|
|
101
|
+
const computedPkMHash = await pkM.hash();
|
|
102
|
+
if (!computedPkMHash.equals(pkMHash)) {
|
|
103
|
+
throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
|
|
104
|
+
}
|
|
105
|
+
const derivedPkM = await derivePublicKeyFromSecretKey(skM);
|
|
106
|
+
if (!derivedPkM.equals(pkM)) {
|
|
107
|
+
throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
|
|
108
|
+
}
|
|
109
|
+
const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix);
|
|
110
|
+
return new KeyValidationRequest(pkM, skApp);
|
|
111
|
+
});
|
|
98
112
|
}
|
|
99
113
|
/**
|
|
100
114
|
* Gets the master nullifier public key for a given account.
|
|
@@ -172,7 +186,7 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
|
|
|
172
186
|
masterOutgoingViewingSecretKey.hi,
|
|
173
187
|
masterOutgoingViewingSecretKey.lo,
|
|
174
188
|
app
|
|
175
|
-
],
|
|
189
|
+
], DomainSeparator.OVSK_M);
|
|
176
190
|
}
|
|
177
191
|
/**
|
|
178
192
|
* Retrieves the sk_m corresponding to the pk_m.
|
|
@@ -180,18 +194,39 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
|
|
|
180
194
|
* @param pkM - The master public key to get secret key for.
|
|
181
195
|
* @returns A Promise that resolves to sk_m.
|
|
182
196
|
* @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
|
-
|
|
197
|
+
*/ getMasterSecretKey(pkM) {
|
|
198
|
+
return this.#db.transactionAsync(async ()=>{
|
|
199
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkM);
|
|
200
|
+
const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
|
|
201
|
+
const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
|
|
202
|
+
if (!secretKeyBuffer) {
|
|
203
|
+
throw new Error(`Could not find ${skStorageSuffix} for ${keyPrefix}pk_m ${pkM.toString()}. This should not happen.`);
|
|
204
|
+
}
|
|
205
|
+
const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
|
|
206
|
+
// Non-DB computation — safe because no further IDB operations follow.
|
|
207
|
+
const derivedpkM = await derivePublicKeyFromSecretKey(skM);
|
|
208
|
+
if (!derivedpkM.equals(pkM)) {
|
|
209
|
+
throw new Error(`Could not find ${skStorageSuffix} for ${keyPrefix}pkM ${pkM.toString()} in secret keys buffer.`);
|
|
210
|
+
}
|
|
211
|
+
return skM;
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Checks whether a given account has a key matching the provided master public key hash.
|
|
216
|
+
* @param account - The account address to check.
|
|
217
|
+
* @param pkMHash - The master public key hash to look for.
|
|
218
|
+
* @returns True if the account has a key with the given hash.
|
|
219
|
+
*/ accountHasKey(account, pkMHash) {
|
|
220
|
+
return this.#db.transactionAsync(async ()=>{
|
|
221
|
+
const pkMHashBuffer = serializeToBuffer(pkMHash);
|
|
222
|
+
for (const prefix of KEY_PREFIXES){
|
|
223
|
+
const stored = await this.#keys.getAsync(`${account.toString()}-${prefix}pk_m_hash`);
|
|
224
|
+
if (stored && Buffer.from(stored).equals(pkMHashBuffer)) {
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return false;
|
|
229
|
+
});
|
|
195
230
|
}
|
|
196
231
|
/**
|
|
197
232
|
* Gets the key prefix and account address for a given value.
|
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.9badcec54",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": "./dest/index.js",
|
|
6
6
|
"typedocOptions": {
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"tsconfig": "./tsconfig.json"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "yarn clean &&
|
|
15
|
-
"build:dev": "
|
|
14
|
+
"build": "yarn clean && ../scripts/tsc.sh",
|
|
15
|
+
"build:dev": "../scripts/tsc.sh --watch",
|
|
16
16
|
"clean": "rm -rf ./dest .tsbuildinfo",
|
|
17
17
|
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
|
|
18
18
|
},
|
|
@@ -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.9badcec54",
|
|
61
|
+
"@aztec/foundation": "0.0.1-commit.9badcec54",
|
|
62
|
+
"@aztec/kv-store": "0.0.1-commit.9badcec54",
|
|
63
|
+
"@aztec/stdlib": "0.0.1-commit.9badcec54",
|
|
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"
|
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';
|
|
@@ -16,14 +17,21 @@ import {
|
|
|
16
17
|
derivePublicKeyFromSecretKey,
|
|
17
18
|
} from '@aztec/stdlib/keys';
|
|
18
19
|
|
|
20
|
+
/** Maps a key prefix to the storage suffix for the corresponding master secret key. */
|
|
21
|
+
function secretKeyStorageSuffix(prefix: KeyPrefix): string {
|
|
22
|
+
return prefix === 'n' ? 'nhk_m' : `${prefix}sk_m`;
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
/**
|
|
20
26
|
* Used for managing keys. Can hold keys of multiple accounts.
|
|
21
27
|
*/
|
|
22
28
|
export class KeyStore {
|
|
23
29
|
public static readonly SCHEMA_VERSION = 1;
|
|
30
|
+
#db: AztecAsyncKVStore;
|
|
24
31
|
#keys: AztecAsyncMap<string, Buffer>;
|
|
25
32
|
|
|
26
33
|
constructor(database: AztecAsyncKVStore) {
|
|
34
|
+
this.#db = database;
|
|
27
35
|
this.#keys = database.openMap('key_store');
|
|
28
36
|
}
|
|
29
37
|
|
|
@@ -45,7 +53,7 @@ export class KeyStore {
|
|
|
45
53
|
*/
|
|
46
54
|
public async addAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
|
|
47
55
|
const {
|
|
48
|
-
|
|
56
|
+
masterNullifierHidingKey,
|
|
49
57
|
masterIncomingViewingSecretKey,
|
|
50
58
|
masterOutgoingViewingSecretKey,
|
|
51
59
|
masterTaggingSecretKey,
|
|
@@ -55,27 +63,31 @@ export class KeyStore {
|
|
|
55
63
|
const completeAddress = await CompleteAddress.fromSecretKeyAndPartialAddress(sk, partialAddress);
|
|
56
64
|
const { address: account } = completeAddress;
|
|
57
65
|
|
|
58
|
-
//
|
|
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
|
|
66
|
+
// Compute hashes before transaction
|
|
71
67
|
const masterNullifierPublicKeyHash = await publicKeys.masterNullifierPublicKey.hash();
|
|
72
|
-
await this.#keys.set(`${account.toString()}-npk_m_hash`, masterNullifierPublicKeyHash.toBuffer());
|
|
73
68
|
const masterIncomingViewingPublicKeyHash = await publicKeys.masterIncomingViewingPublicKey.hash();
|
|
74
|
-
await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
|
|
75
69
|
const masterOutgoingViewingPublicKeyHash = await publicKeys.masterOutgoingViewingPublicKey.hash();
|
|
76
|
-
await this.#keys.set(`${account.toString()}-ovpk_m_hash`, masterOutgoingViewingPublicKeyHash.toBuffer());
|
|
77
70
|
const masterTaggingPublicKeyHash = await publicKeys.masterTaggingPublicKey.hash();
|
|
78
|
-
|
|
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
|
+
});
|
|
79
91
|
|
|
80
92
|
// At last, we return the newly derived account address
|
|
81
93
|
return completeAddress;
|
|
@@ -92,6 +104,11 @@ export class KeyStore {
|
|
|
92
104
|
return accounts.map(account => AztecAddress.fromString(account));
|
|
93
105
|
}
|
|
94
106
|
|
|
107
|
+
/** Checks whether an account is registered in the key store. */
|
|
108
|
+
public async hasAccount(account: AztecAddress): Promise<boolean> {
|
|
109
|
+
return !!(await this.#keys.getAsync(`${account.toString()}-ivsk_m`));
|
|
110
|
+
}
|
|
111
|
+
|
|
95
112
|
/**
|
|
96
113
|
* Gets the key validation request for a given master public key hash and contract address.
|
|
97
114
|
* @throws If the account corresponding to the master public key hash does not exist in the key store.
|
|
@@ -99,43 +116,46 @@ export class KeyStore {
|
|
|
99
116
|
* @param contractAddress - The contract address to silo the secret key in the key validation request with.
|
|
100
117
|
* @returns The key validation request.
|
|
101
118
|
*/
|
|
102
|
-
public
|
|
103
|
-
|
|
119
|
+
public getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise<KeyValidationRequest> {
|
|
120
|
+
return this.#db.transactionAsync(async () => {
|
|
121
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
|
|
104
122
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
123
|
+
// Now we find the master public key for the account
|
|
124
|
+
const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
|
|
125
|
+
if (!pkMBuffer) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
112
130
|
|
|
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
|
-
}
|
|
131
|
+
const pkM = Point.fromBuffer(pkMBuffer);
|
|
118
132
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
// Now we find the secret key for the public key
|
|
134
|
+
const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
|
|
135
|
+
const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
|
|
136
|
+
if (!skMBuffer) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Could not find ${skStorageSuffix} for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
126
141
|
|
|
127
|
-
|
|
142
|
+
const skM = GrumpkinScalar.fromBuffer(skMBuffer);
|
|
128
143
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
144
|
+
// The remaining awaits are non-DB computations. They are safe because no further IDB operations follow them.
|
|
145
|
+
const computedPkMHash = await pkM.hash();
|
|
146
|
+
if (!computedPkMHash.equals(pkMHash)) {
|
|
147
|
+
throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
|
|
148
|
+
}
|
|
134
149
|
|
|
135
|
-
|
|
136
|
-
|
|
150
|
+
const derivedPkM = await derivePublicKeyFromSecretKey(skM);
|
|
151
|
+
if (!derivedPkM.equals(pkM)) {
|
|
152
|
+
throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
|
|
153
|
+
}
|
|
137
154
|
|
|
138
|
-
|
|
155
|
+
const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix!);
|
|
156
|
+
|
|
157
|
+
return new KeyValidationRequest(pkM, skApp);
|
|
158
|
+
});
|
|
139
159
|
}
|
|
140
160
|
|
|
141
161
|
/**
|
|
@@ -236,7 +256,7 @@ export class KeyStore {
|
|
|
236
256
|
|
|
237
257
|
return poseidon2HashWithSeparator(
|
|
238
258
|
[masterOutgoingViewingSecretKey.hi, masterOutgoingViewingSecretKey.lo, app],
|
|
239
|
-
|
|
259
|
+
DomainSeparator.OVSK_M,
|
|
240
260
|
);
|
|
241
261
|
}
|
|
242
262
|
|
|
@@ -247,23 +267,49 @@ export class KeyStore {
|
|
|
247
267
|
* @returns A Promise that resolves to sk_m.
|
|
248
268
|
* @dev Used when feeding the sk_m to the kernel circuit for keys verification.
|
|
249
269
|
*/
|
|
250
|
-
public
|
|
251
|
-
|
|
270
|
+
public getMasterSecretKey(pkM: PublicKey): Promise<GrumpkinScalar> {
|
|
271
|
+
return this.#db.transactionAsync(async () => {
|
|
272
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkM);
|
|
252
273
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
274
|
+
const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
|
|
275
|
+
const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
|
|
276
|
+
if (!secretKeyBuffer) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`Could not find ${skStorageSuffix} for ${keyPrefix}pk_m ${pkM.toString()}. This should not happen.`,
|
|
279
|
+
);
|
|
280
|
+
}
|
|
259
281
|
|
|
260
|
-
|
|
261
|
-
const derivedpkM = await derivePublicKeyFromSecretKey(skM);
|
|
262
|
-
if (!derivedpkM.equals(pkM)) {
|
|
263
|
-
throw new Error(`Could not find ${keyPrefix}skM for ${keyPrefix}pkM ${pkM.toString()} in secret keys buffer.`);
|
|
264
|
-
}
|
|
282
|
+
const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
|
|
265
283
|
|
|
266
|
-
|
|
284
|
+
// Non-DB computation — safe because no further IDB operations follow.
|
|
285
|
+
const derivedpkM = await derivePublicKeyFromSecretKey(skM);
|
|
286
|
+
if (!derivedpkM.equals(pkM)) {
|
|
287
|
+
throw new Error(
|
|
288
|
+
`Could not find ${skStorageSuffix} for ${keyPrefix}pkM ${pkM.toString()} in secret keys buffer.`,
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return skM;
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Checks whether a given account has a key matching the provided master public key hash.
|
|
298
|
+
* @param account - The account address to check.
|
|
299
|
+
* @param pkMHash - The master public key hash to look for.
|
|
300
|
+
* @returns True if the account has a key with the given hash.
|
|
301
|
+
*/
|
|
302
|
+
public accountHasKey(account: AztecAddress, pkMHash: Fr): Promise<boolean> {
|
|
303
|
+
return this.#db.transactionAsync(async () => {
|
|
304
|
+
const pkMHashBuffer = serializeToBuffer(pkMHash);
|
|
305
|
+
for (const prefix of KEY_PREFIXES) {
|
|
306
|
+
const stored = await this.#keys.getAsync(`${account.toString()}-${prefix}pk_m_hash`);
|
|
307
|
+
if (stored && Buffer.from(stored).equals(pkMHashBuffer)) {
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return false;
|
|
312
|
+
});
|
|
267
313
|
}
|
|
268
314
|
|
|
269
315
|
/**
|