@aztec/key-store 0.0.1-commit.e3c1de76 → 0.0.1-commit.e57c76e
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 +14 -5
- package/dest/key_store.d.ts.map +1 -1
- package/dest/key_store.js +97 -59
- package/package.json +5 -5
- package/src/key_store.ts +118 -64
package/dest/key_store.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export declare class KeyStore {
|
|
|
30
30
|
* @returns A Promise that resolves to an array of account addresses.
|
|
31
31
|
*/
|
|
32
32
|
getAccounts(): Promise<AztecAddress[]>;
|
|
33
|
+
/** Checks whether an account is registered in the key store. */
|
|
34
|
+
hasAccount(account: AztecAddress): Promise<boolean>;
|
|
33
35
|
/**
|
|
34
36
|
* Gets the key validation request for a given master public key hash and contract address.
|
|
35
37
|
* @throws If the account corresponding to the master public key hash does not exist in the key store.
|
|
@@ -82,13 +84,20 @@ export declare class KeyStore {
|
|
|
82
84
|
*/
|
|
83
85
|
getAppOutgoingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise<Fr>;
|
|
84
86
|
/**
|
|
85
|
-
* Retrieves the sk_m corresponding to the pk_m.
|
|
86
|
-
* @throws If the provided
|
|
87
|
-
* @param
|
|
87
|
+
* Retrieves the sk_m corresponding to the given pk_m hash.
|
|
88
|
+
* @throws If the provided hash is not associated with any of the registered accounts.
|
|
89
|
+
* @param pkMHash - The master public key hash to get secret key for.
|
|
88
90
|
* @returns A Promise that resolves to sk_m.
|
|
89
91
|
* @dev Used when feeding the sk_m to the kernel circuit for keys verification.
|
|
90
92
|
*/
|
|
91
|
-
getMasterSecretKey(
|
|
93
|
+
getMasterSecretKey(pkMHash: Fr): 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>;
|
|
92
101
|
/**
|
|
93
102
|
* Gets the key prefix and account address for a given value.
|
|
94
103
|
* @returns A tuple containing the key prefix and account address.
|
|
@@ -97,4 +106,4 @@ export declare class KeyStore {
|
|
|
97
106
|
*/
|
|
98
107
|
getKeyPrefixAndAccount(value: Bufferable): Promise<[KeyPrefix, AztecAddress]>;
|
|
99
108
|
}
|
|
100
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
109
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5X3N0b3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMva2V5X3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFTLE1BQU0sbUNBQW1DLENBQUM7QUFFMUUsT0FBTyxFQUFFLEtBQUssVUFBVSxFQUFxQixNQUFNLDZCQUE2QixDQUFDO0FBQ2pGLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFpQixNQUFNLGlCQUFpQixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDNUQsT0FBTyxFQUVMLEtBQUssU0FBUyxFQUNkLEtBQUssU0FBUyxFQUtmLE1BQU0sb0JBQW9CLENBQUM7QUFPNUI7O0dBRUc7QUFDSCxxQkFBYSxRQUFROztJQUNuQixnQkFBdUIsY0FBYyxLQUFLO0lBSTFDLFlBQVksUUFBUSxFQUFFLGlCQUFpQixFQUd0QztJQUVEOzs7T0FHRztJQUNJLGFBQWEsSUFBSSxPQUFPLENBQUMsZUFBZSxDQUFDLENBSS9DO0lBRUQ7Ozs7O09BS0c7SUFDVSxVQUFVLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxjQUFjLEVBQUUsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0ErQ3hGO0lBRUQ7OztPQUdHO0lBQ1UsV0FBVyxJQUFJLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUtsRDtJQUVELGdFQUFnRTtJQUNuRCxVQUFVLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRS9EO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksdUJBQXVCLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxlQUFlLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQTBDeEc7SUFFRDs7Ozs7T0FLRztJQUNVLDJCQUEyQixDQUFDLE9BQU8sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQVFsRjtJQUVEOzs7OztPQUtHO0lBQ1UsaUNBQWlDLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBUXhGO0lBRUQ7Ozs7O09BS0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FReEY7SUFFRDs7Ozs7T0FLRztJQUNVLHlCQUF5QixDQUFDLE9BQU8sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQVFoRjtJQUVEOzs7OztPQUtHO0lBQ1UsaUNBQWlDLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDLENBUTdGO0lBRUQ7Ozs7OztPQU1HO0lBQ1UsOEJBQThCLENBQUMsT0FBTyxFQUFFLFlBQVksRUFBRSxHQUFHLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0Fhakc7SUFFRDs7Ozs7O09BTUc7SUFDSSxrQkFBa0IsQ0FBQyxPQUFPLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0E2QjlEO0lBRUQ7Ozs7O09BS0c7SUFDSSxhQUFhLENBQUMsT0FBTyxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FXekU7SUFFRDs7Ozs7T0FLRztJQUNVLHNCQUFzQixDQUFDLEtBQUssRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLENBQUMsU0FBUyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBY3pGO0NBQ0YifQ==
|
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,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,
|
|
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;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,CA+CxF;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;;;;;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,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;CACF"}
|
package/dest/key_store.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DomainSeparator } from '@aztec/constants';
|
|
2
2
|
import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon';
|
|
3
3
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
4
|
import { GrumpkinScalar, Point } from '@aztec/foundation/curves/grumpkin';
|
|
@@ -7,7 +7,10 @@ import { serializeToBuffer } from '@aztec/foundation/serialize';
|
|
|
7
7
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
8
8
|
import { CompleteAddress } from '@aztec/stdlib/contract';
|
|
9
9
|
import { KeyValidationRequest } from '@aztec/stdlib/kernel';
|
|
10
|
-
import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecretKey } from '@aztec/stdlib/keys';
|
|
10
|
+
import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecretKey, hashPublicKey } 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
|
+
}
|
|
11
14
|
/**
|
|
12
15
|
* Used for managing keys. Can hold keys of multiple accounts.
|
|
13
16
|
*/ export class KeyStore {
|
|
@@ -32,30 +35,32 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
|
|
|
32
35
|
* @param partialAddress - The partial address of the account.
|
|
33
36
|
* @returns The account's complete address.
|
|
34
37
|
*/ async addAccount(sk, partialAddress) {
|
|
35
|
-
const {
|
|
36
|
-
const completeAddress = await CompleteAddress.
|
|
38
|
+
const { masterNullifierHidingKey, masterIncomingViewingSecretKey, masterOutgoingViewingSecretKey, masterTaggingSecretKey, masterNullifierPublicKey, masterOutgoingViewingPublicKey, masterTaggingPublicKey, publicKeys } = await deriveKeys(sk);
|
|
39
|
+
const completeAddress = await CompleteAddress.fromPublicKeysAndPartialAddress(publicKeys, partialAddress);
|
|
37
40
|
const { address: account } = completeAddress;
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
41
|
+
// The kernel cannot check that nhpk/ovpk/tpk are on-curve or non-infinity, so the PXE/key-store
|
|
42
|
+
// must guarantee it before persistence. By design, the above derivation produces points that are on
|
|
43
|
+
// the curve and not at infinity.
|
|
44
|
+
// The npk/ovpk/tpk hashes are already in publicKeys; ivpk_m_hash is computed for indexing.
|
|
45
|
+
const masterIncomingViewingPublicKeyHash = await hashPublicKey(publicKeys.ivpkM);
|
|
46
|
+
// The Message Signing and Fallback Keys don't have a derivation path yet, so we just use the default values for their hashes.
|
|
47
|
+
// So we avoid storing them persistently. The default hash is still required for address derivation
|
|
43
48
|
await this.#db.transactionAsync(async ()=>{
|
|
44
49
|
// Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
|
|
45
50
|
await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
|
|
46
51
|
await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
|
|
47
52
|
await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
|
|
48
|
-
await this.#keys.set(`${account.toString()}-
|
|
49
|
-
await this.#keys.set(`${account.toString()}-npk_m`,
|
|
50
|
-
await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.
|
|
51
|
-
await this.#keys.set(`${account.toString()}-ovpk_m`,
|
|
52
|
-
await this.#keys.set(`${account.toString()}-tpk_m`,
|
|
53
|
+
await this.#keys.set(`${account.toString()}-nhk_m`, masterNullifierHidingKey.toBuffer());
|
|
54
|
+
await this.#keys.set(`${account.toString()}-npk_m`, masterNullifierPublicKey.toBuffer());
|
|
55
|
+
await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.ivpkM.toBuffer());
|
|
56
|
+
await this.#keys.set(`${account.toString()}-ovpk_m`, masterOutgoingViewingPublicKey.toBuffer());
|
|
57
|
+
await this.#keys.set(`${account.toString()}-tpk_m`, masterTaggingPublicKey.toBuffer());
|
|
53
58
|
// We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
|
|
54
59
|
// using the #getKeyPrefixAndAccount function later on
|
|
55
|
-
await this.#keys.set(`${account.toString()}-npk_m_hash`,
|
|
60
|
+
await this.#keys.set(`${account.toString()}-npk_m_hash`, publicKeys.npkMHash.toBuffer());
|
|
56
61
|
await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
|
|
57
|
-
await this.#keys.set(`${account.toString()}-ovpk_m_hash`,
|
|
58
|
-
await this.#keys.set(`${account.toString()}-tpk_m_hash`,
|
|
62
|
+
await this.#keys.set(`${account.toString()}-ovpk_m_hash`, publicKeys.ovpkMHash.toBuffer());
|
|
63
|
+
await this.#keys.set(`${account.toString()}-tpk_m_hash`, publicKeys.tpkMHash.toBuffer());
|
|
59
64
|
});
|
|
60
65
|
// At last, we return the newly derived account address
|
|
61
66
|
return completeAddress;
|
|
@@ -69,38 +74,45 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
|
|
|
69
74
|
const accounts = allMapKeys.filter((key)=>key.endsWith('-ivsk_m')).map((key)=>key.split('-')[0]);
|
|
70
75
|
return accounts.map((account)=>AztecAddress.fromString(account));
|
|
71
76
|
}
|
|
77
|
+
/** Checks whether an account is registered in the key store. */ async hasAccount(account) {
|
|
78
|
+
return !!await this.#keys.getAsync(`${account.toString()}-ivsk_m`);
|
|
79
|
+
}
|
|
72
80
|
/**
|
|
73
81
|
* Gets the key validation request for a given master public key hash and contract address.
|
|
74
82
|
* @throws If the account corresponding to the master public key hash does not exist in the key store.
|
|
75
83
|
* @param pkMHash - The master public key hash.
|
|
76
84
|
* @param contractAddress - The contract address to silo the secret key in the key validation request with.
|
|
77
85
|
* @returns The key validation request.
|
|
78
|
-
*/
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
86
|
+
*/ getKeyValidationRequest(pkMHash, contractAddress) {
|
|
87
|
+
return this.#db.transactionAsync(async ()=>{
|
|
88
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
|
|
89
|
+
// Load the stored master public key point. The returned KVR carries only the hash, but we
|
|
90
|
+
// use the point here as a witness for two integrity checks below: (1) it matches the supplied
|
|
91
|
+
// hash, and (2) it matches the value derived from the stored secret key.
|
|
92
|
+
const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
|
|
93
|
+
if (!pkMBuffer) {
|
|
94
|
+
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()}.`);
|
|
95
|
+
}
|
|
96
|
+
const pkM = Point.fromBuffer(pkMBuffer);
|
|
97
|
+
// Now we find the secret key for the public key
|
|
98
|
+
const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
|
|
99
|
+
const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
|
|
100
|
+
if (!skMBuffer) {
|
|
101
|
+
throw new Error(`Could not find ${skStorageSuffix} for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
|
|
102
|
+
}
|
|
103
|
+
const skM = GrumpkinScalar.fromBuffer(skMBuffer);
|
|
104
|
+
// The remaining awaits are non-DB computations. They are safe because no further IDB operations follow them.
|
|
105
|
+
const computedPkMHash = await hashPublicKey(pkM);
|
|
106
|
+
if (!computedPkMHash.equals(pkMHash)) {
|
|
107
|
+
throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
|
|
108
|
+
}
|
|
109
|
+
const derivedPkM = await derivePublicKeyFromSecretKey(skM);
|
|
110
|
+
if (!derivedPkM.equals(pkM)) {
|
|
111
|
+
throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
|
|
112
|
+
}
|
|
113
|
+
const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix);
|
|
114
|
+
return new KeyValidationRequest(pkMHash, skApp);
|
|
115
|
+
});
|
|
104
116
|
}
|
|
105
117
|
/**
|
|
106
118
|
* Gets the master nullifier public key for a given account.
|
|
@@ -178,26 +190,52 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
|
|
|
178
190
|
masterOutgoingViewingSecretKey.hi,
|
|
179
191
|
masterOutgoingViewingSecretKey.lo,
|
|
180
192
|
app
|
|
181
|
-
],
|
|
193
|
+
], DomainSeparator.OVSK_M);
|
|
182
194
|
}
|
|
183
195
|
/**
|
|
184
|
-
* Retrieves the sk_m corresponding to the pk_m.
|
|
185
|
-
* @throws If the provided
|
|
186
|
-
* @param
|
|
196
|
+
* Retrieves the sk_m corresponding to the given pk_m hash.
|
|
197
|
+
* @throws If the provided hash is not associated with any of the registered accounts.
|
|
198
|
+
* @param pkMHash - The master public key hash to get secret key for.
|
|
187
199
|
* @returns A Promise that resolves to sk_m.
|
|
188
200
|
* @dev Used when feeding the sk_m to the kernel circuit for keys verification.
|
|
189
|
-
*/
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
+
*/ getMasterSecretKey(pkMHash) {
|
|
202
|
+
return this.#db.transactionAsync(async ()=>{
|
|
203
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
|
|
204
|
+
const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
|
|
205
|
+
const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
|
|
206
|
+
if (!secretKeyBuffer) {
|
|
207
|
+
throw new Error(`Could not find ${skStorageSuffix} for ${keyPrefix}pk_m_hash ${pkMHash.toString()}. This should not happen.`);
|
|
208
|
+
}
|
|
209
|
+
const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
|
|
210
|
+
// Non-DB computation — safe because no further IDB operations follow.
|
|
211
|
+
// Integrity check: confirm the stored secret key still derives the requested hash. The check
|
|
212
|
+
// is hash-based rather than point-equal because the on-disk identifier is `pk_m_hash`;
|
|
213
|
+
// cryptographic collision resistance of `hashPublicKey` makes this equivalent to a
|
|
214
|
+
// direct point comparison in practice.
|
|
215
|
+
const derivedPkM = await derivePublicKeyFromSecretKey(skM);
|
|
216
|
+
const derivedPkMHash = await hashPublicKey(derivedPkM);
|
|
217
|
+
if (!derivedPkMHash.equals(pkMHash)) {
|
|
218
|
+
throw new Error(`Could not find ${skStorageSuffix} for ${keyPrefix}pk_m_hash ${pkMHash.toString()} in secret keys buffer.`);
|
|
219
|
+
}
|
|
220
|
+
return skM;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Checks whether a given account has a key matching the provided master public key hash.
|
|
225
|
+
* @param account - The account address to check.
|
|
226
|
+
* @param pkMHash - The master public key hash to look for.
|
|
227
|
+
* @returns True if the account has a key with the given hash.
|
|
228
|
+
*/ accountHasKey(account, pkMHash) {
|
|
229
|
+
return this.#db.transactionAsync(async ()=>{
|
|
230
|
+
const pkMHashBuffer = serializeToBuffer(pkMHash);
|
|
231
|
+
for (const prefix of KEY_PREFIXES){
|
|
232
|
+
const stored = await this.#keys.getAsync(`${account.toString()}-${prefix}pk_m_hash`);
|
|
233
|
+
if (stored && Buffer.from(stored).equals(pkMHashBuffer)) {
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return false;
|
|
238
|
+
});
|
|
201
239
|
}
|
|
202
240
|
/**
|
|
203
241
|
* 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.e57c76e",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": "./dest/index.js",
|
|
6
6
|
"typedocOptions": {
|
|
@@ -57,10 +57,10 @@
|
|
|
57
57
|
]
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@aztec/constants": "0.0.1-commit.
|
|
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.e57c76e",
|
|
61
|
+
"@aztec/foundation": "0.0.1-commit.e57c76e",
|
|
62
|
+
"@aztec/kv-store": "0.0.1-commit.e57c76e",
|
|
63
|
+
"@aztec/stdlib": "0.0.1-commit.e57c76e",
|
|
64
64
|
"tslib": "^2.4.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
package/src/key_store.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DomainSeparator } from '@aztec/constants';
|
|
2
2
|
import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon';
|
|
3
3
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
4
|
import { GrumpkinScalar, Point } from '@aztec/foundation/curves/grumpkin';
|
|
@@ -15,8 +15,14 @@ import {
|
|
|
15
15
|
computeAppSecretKey,
|
|
16
16
|
deriveKeys,
|
|
17
17
|
derivePublicKeyFromSecretKey,
|
|
18
|
+
hashPublicKey,
|
|
18
19
|
} from '@aztec/stdlib/keys';
|
|
19
20
|
|
|
21
|
+
/** Maps a key prefix to the storage suffix for the corresponding master secret key. */
|
|
22
|
+
function secretKeyStorageSuffix(prefix: KeyPrefix): string {
|
|
23
|
+
return prefix === 'n' ? 'nhk_m' : `${prefix}sk_m`;
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
/**
|
|
21
27
|
* Used for managing keys. Can hold keys of multiple accounts.
|
|
22
28
|
*/
|
|
@@ -48,40 +54,47 @@ export class KeyStore {
|
|
|
48
54
|
*/
|
|
49
55
|
public async addAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
|
|
50
56
|
const {
|
|
51
|
-
|
|
57
|
+
masterNullifierHidingKey,
|
|
52
58
|
masterIncomingViewingSecretKey,
|
|
53
59
|
masterOutgoingViewingSecretKey,
|
|
54
60
|
masterTaggingSecretKey,
|
|
61
|
+
masterNullifierPublicKey,
|
|
62
|
+
masterOutgoingViewingPublicKey,
|
|
63
|
+
masterTaggingPublicKey,
|
|
55
64
|
publicKeys,
|
|
56
65
|
} = await deriveKeys(sk);
|
|
57
66
|
|
|
58
|
-
const completeAddress = await CompleteAddress.
|
|
67
|
+
const completeAddress = await CompleteAddress.fromPublicKeysAndPartialAddress(publicKeys, partialAddress);
|
|
59
68
|
const { address: account } = completeAddress;
|
|
60
69
|
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
// The kernel cannot check that nhpk/ovpk/tpk are on-curve or non-infinity, so the PXE/key-store
|
|
71
|
+
// must guarantee it before persistence. By design, the above derivation produces points that are on
|
|
72
|
+
// the curve and not at infinity.
|
|
73
|
+
|
|
74
|
+
// The npk/ovpk/tpk hashes are already in publicKeys; ivpk_m_hash is computed for indexing.
|
|
75
|
+
const masterIncomingViewingPublicKeyHash = await hashPublicKey(publicKeys.ivpkM);
|
|
76
|
+
|
|
77
|
+
// The Message Signing and Fallback Keys don't have a derivation path yet, so we just use the default values for their hashes.
|
|
78
|
+
// So we avoid storing them persistently. The default hash is still required for address derivation
|
|
66
79
|
|
|
67
80
|
await this.#db.transactionAsync(async () => {
|
|
68
81
|
// Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
|
|
69
82
|
await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
|
|
70
83
|
await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
|
|
71
84
|
await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
|
|
72
|
-
await this.#keys.set(`${account.toString()}-
|
|
85
|
+
await this.#keys.set(`${account.toString()}-nhk_m`, masterNullifierHidingKey.toBuffer());
|
|
73
86
|
|
|
74
|
-
await this.#keys.set(`${account.toString()}-npk_m`,
|
|
75
|
-
await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.
|
|
76
|
-
await this.#keys.set(`${account.toString()}-ovpk_m`,
|
|
77
|
-
await this.#keys.set(`${account.toString()}-tpk_m`,
|
|
87
|
+
await this.#keys.set(`${account.toString()}-npk_m`, masterNullifierPublicKey.toBuffer());
|
|
88
|
+
await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.ivpkM.toBuffer());
|
|
89
|
+
await this.#keys.set(`${account.toString()}-ovpk_m`, masterOutgoingViewingPublicKey.toBuffer());
|
|
90
|
+
await this.#keys.set(`${account.toString()}-tpk_m`, masterTaggingPublicKey.toBuffer());
|
|
78
91
|
|
|
79
92
|
// We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
|
|
80
93
|
// using the #getKeyPrefixAndAccount function later on
|
|
81
|
-
await this.#keys.set(`${account.toString()}-npk_m_hash`,
|
|
94
|
+
await this.#keys.set(`${account.toString()}-npk_m_hash`, publicKeys.npkMHash.toBuffer());
|
|
82
95
|
await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
|
|
83
|
-
await this.#keys.set(`${account.toString()}-ovpk_m_hash`,
|
|
84
|
-
await this.#keys.set(`${account.toString()}-tpk_m_hash`,
|
|
96
|
+
await this.#keys.set(`${account.toString()}-ovpk_m_hash`, publicKeys.ovpkMHash.toBuffer());
|
|
97
|
+
await this.#keys.set(`${account.toString()}-tpk_m_hash`, publicKeys.tpkMHash.toBuffer());
|
|
85
98
|
});
|
|
86
99
|
|
|
87
100
|
// At last, we return the newly derived account address
|
|
@@ -99,6 +112,11 @@ export class KeyStore {
|
|
|
99
112
|
return accounts.map(account => AztecAddress.fromString(account));
|
|
100
113
|
}
|
|
101
114
|
|
|
115
|
+
/** Checks whether an account is registered in the key store. */
|
|
116
|
+
public async hasAccount(account: AztecAddress): Promise<boolean> {
|
|
117
|
+
return !!(await this.#keys.getAsync(`${account.toString()}-ivsk_m`));
|
|
118
|
+
}
|
|
119
|
+
|
|
102
120
|
/**
|
|
103
121
|
* Gets the key validation request for a given master public key hash and contract address.
|
|
104
122
|
* @throws If the account corresponding to the master public key hash does not exist in the key store.
|
|
@@ -106,43 +124,48 @@ export class KeyStore {
|
|
|
106
124
|
* @param contractAddress - The contract address to silo the secret key in the key validation request with.
|
|
107
125
|
* @returns The key validation request.
|
|
108
126
|
*/
|
|
109
|
-
public
|
|
110
|
-
|
|
127
|
+
public getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise<KeyValidationRequest> {
|
|
128
|
+
return this.#db.transactionAsync(async () => {
|
|
129
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
|
|
111
130
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
131
|
+
// Load the stored master public key point. The returned KVR carries only the hash, but we
|
|
132
|
+
// use the point here as a witness for two integrity checks below: (1) it matches the supplied
|
|
133
|
+
// hash, and (2) it matches the value derived from the stored secret key.
|
|
134
|
+
const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
|
|
135
|
+
if (!pkMBuffer) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
`Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
119
140
|
|
|
120
|
-
|
|
121
|
-
const computedPkMHash = await pkM.hash();
|
|
122
|
-
if (!computedPkMHash.equals(pkMHash)) {
|
|
123
|
-
throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
|
|
124
|
-
}
|
|
141
|
+
const pkM = Point.fromBuffer(pkMBuffer);
|
|
125
142
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
// Now we find the secret key for the public key
|
|
144
|
+
const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
|
|
145
|
+
const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
|
|
146
|
+
if (!skMBuffer) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`Could not find ${skStorageSuffix} for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
133
151
|
|
|
134
|
-
|
|
152
|
+
const skM = GrumpkinScalar.fromBuffer(skMBuffer);
|
|
135
153
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
154
|
+
// The remaining awaits are non-DB computations. They are safe because no further IDB operations follow them.
|
|
155
|
+
const computedPkMHash = await hashPublicKey(pkM);
|
|
156
|
+
if (!computedPkMHash.equals(pkMHash)) {
|
|
157
|
+
throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
|
|
158
|
+
}
|
|
141
159
|
|
|
142
|
-
|
|
143
|
-
|
|
160
|
+
const derivedPkM = await derivePublicKeyFromSecretKey(skM);
|
|
161
|
+
if (!derivedPkM.equals(pkM)) {
|
|
162
|
+
throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
|
|
163
|
+
}
|
|
144
164
|
|
|
145
|
-
|
|
165
|
+
const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix!);
|
|
166
|
+
|
|
167
|
+
return new KeyValidationRequest(pkMHash, skApp);
|
|
168
|
+
});
|
|
146
169
|
}
|
|
147
170
|
|
|
148
171
|
/**
|
|
@@ -243,34 +266,65 @@ export class KeyStore {
|
|
|
243
266
|
|
|
244
267
|
return poseidon2HashWithSeparator(
|
|
245
268
|
[masterOutgoingViewingSecretKey.hi, masterOutgoingViewingSecretKey.lo, app],
|
|
246
|
-
|
|
269
|
+
DomainSeparator.OVSK_M,
|
|
247
270
|
);
|
|
248
271
|
}
|
|
249
272
|
|
|
250
273
|
/**
|
|
251
|
-
* Retrieves the sk_m corresponding to the pk_m.
|
|
252
|
-
* @throws If the provided
|
|
253
|
-
* @param
|
|
274
|
+
* Retrieves the sk_m corresponding to the given pk_m hash.
|
|
275
|
+
* @throws If the provided hash is not associated with any of the registered accounts.
|
|
276
|
+
* @param pkMHash - The master public key hash to get secret key for.
|
|
254
277
|
* @returns A Promise that resolves to sk_m.
|
|
255
278
|
* @dev Used when feeding the sk_m to the kernel circuit for keys verification.
|
|
256
279
|
*/
|
|
257
|
-
public
|
|
258
|
-
|
|
280
|
+
public getMasterSecretKey(pkMHash: Fr): Promise<GrumpkinScalar> {
|
|
281
|
+
return this.#db.transactionAsync(async () => {
|
|
282
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
|
|
259
283
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
284
|
+
const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
|
|
285
|
+
const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
|
|
286
|
+
if (!secretKeyBuffer) {
|
|
287
|
+
throw new Error(
|
|
288
|
+
`Could not find ${skStorageSuffix} for ${keyPrefix}pk_m_hash ${pkMHash.toString()}. This should not happen.`,
|
|
289
|
+
);
|
|
290
|
+
}
|
|
266
291
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
292
|
+
const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
|
|
293
|
+
|
|
294
|
+
// Non-DB computation — safe because no further IDB operations follow.
|
|
295
|
+
// Integrity check: confirm the stored secret key still derives the requested hash. The check
|
|
296
|
+
// is hash-based rather than point-equal because the on-disk identifier is `pk_m_hash`;
|
|
297
|
+
// cryptographic collision resistance of `hashPublicKey` makes this equivalent to a
|
|
298
|
+
// direct point comparison in practice.
|
|
299
|
+
const derivedPkM = await derivePublicKeyFromSecretKey(skM);
|
|
300
|
+
const derivedPkMHash = await hashPublicKey(derivedPkM);
|
|
301
|
+
if (!derivedPkMHash.equals(pkMHash)) {
|
|
302
|
+
throw new Error(
|
|
303
|
+
`Could not find ${skStorageSuffix} for ${keyPrefix}pk_m_hash ${pkMHash.toString()} in secret keys buffer.`,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
272
306
|
|
|
273
|
-
|
|
307
|
+
return skM;
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Checks whether a given account has a key matching the provided master public key hash.
|
|
313
|
+
* @param account - The account address to check.
|
|
314
|
+
* @param pkMHash - The master public key hash to look for.
|
|
315
|
+
* @returns True if the account has a key with the given hash.
|
|
316
|
+
*/
|
|
317
|
+
public accountHasKey(account: AztecAddress, pkMHash: Fr): Promise<boolean> {
|
|
318
|
+
return this.#db.transactionAsync(async () => {
|
|
319
|
+
const pkMHashBuffer = serializeToBuffer(pkMHash);
|
|
320
|
+
for (const prefix of KEY_PREFIXES) {
|
|
321
|
+
const stored = await this.#keys.getAsync(`${account.toString()}-${prefix}pk_m_hash`);
|
|
322
|
+
if (stored && Buffer.from(stored).equals(pkMHashBuffer)) {
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return false;
|
|
327
|
+
});
|
|
274
328
|
}
|
|
275
329
|
|
|
276
330
|
/**
|