@aztec/key-store 0.0.0-test.0
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/README.md +3 -0
- package/dest/index.d.ts +2 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +1 -0
- package/dest/key_store.d.ts +99 -0
- package/dest/key_store.d.ts.map +1 -0
- package/dest/key_store.js +219 -0
- package/package.json +81 -0
- package/src/index.ts +1 -0
- package/src/key_store.ts +290 -0
package/README.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
# Key Store
|
|
2
|
+
|
|
3
|
+
A key store is an input component for the [PXEService](../pxe/) to manage keys securely. It provides a secure environment and interfaces for users to manage their keys. When the PXEService requests keys and signatures from the key store, a well-designed key store should prompt users to authorize the requested action before sending any sensitive information to the PXEService. This helps to ensure that sensitive data, such as private keys, is not accessible to unauthorized parties. Additionally, the key store should provide robust protection mechanisms such as encryption, multi-factor authentication, and backup/restore functionalities to prevent data loss or theft.
|
package/dest/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
package/dest/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './key_store.js';
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Fr, GrumpkinScalar } from '@aztec/foundation/fields';
|
|
2
|
+
import { type Bufferable } from '@aztec/foundation/serialize';
|
|
3
|
+
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
4
|
+
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
5
|
+
import { CompleteAddress, type PartialAddress } from '@aztec/stdlib/contract';
|
|
6
|
+
import { KeyValidationRequest } from '@aztec/stdlib/kernel';
|
|
7
|
+
import { type KeyPrefix, type PublicKey } from '@aztec/stdlib/keys';
|
|
8
|
+
/**
|
|
9
|
+
* Used for managing keys. Can hold keys of multiple accounts.
|
|
10
|
+
*/
|
|
11
|
+
export declare class KeyStore {
|
|
12
|
+
#private;
|
|
13
|
+
static readonly SCHEMA_VERSION = 1;
|
|
14
|
+
constructor(database: AztecAsyncKVStore);
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new account from a randomly generated secret key.
|
|
17
|
+
* @returns A promise that resolves to the newly created account's CompleteAddress.
|
|
18
|
+
*/
|
|
19
|
+
createAccount(): Promise<CompleteAddress>;
|
|
20
|
+
/**
|
|
21
|
+
* Adds an account to the key store from the provided secret key.
|
|
22
|
+
* @param sk - The secret key of the account.
|
|
23
|
+
* @param partialAddress - The partial address of the account.
|
|
24
|
+
* @returns The account's complete address.
|
|
25
|
+
*/
|
|
26
|
+
addAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress>;
|
|
27
|
+
/**
|
|
28
|
+
* Retrieves addresses of accounts stored in the key store.
|
|
29
|
+
* @returns A Promise that resolves to an array of account addresses.
|
|
30
|
+
*/
|
|
31
|
+
getAccounts(): Promise<AztecAddress[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Gets the key validation request for a given master public key hash and contract address.
|
|
34
|
+
* @throws If the account corresponding to the master public key hash does not exist in the key store.
|
|
35
|
+
* @param pkMHash - The master public key hash.
|
|
36
|
+
* @param contractAddress - The contract address to silo the secret key in the key validation request with.
|
|
37
|
+
* @returns The key validation request.
|
|
38
|
+
*/
|
|
39
|
+
getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise<KeyValidationRequest>;
|
|
40
|
+
/**
|
|
41
|
+
* Gets the master nullifier public key for a given account.
|
|
42
|
+
* @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
|
+
*/
|
|
46
|
+
getMasterNullifierPublicKey(account: AztecAddress): Promise<PublicKey>;
|
|
47
|
+
/**
|
|
48
|
+
* Gets the master incoming viewing public key for a given account.
|
|
49
|
+
* @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
|
+
*/
|
|
53
|
+
getMasterIncomingViewingPublicKey(account: AztecAddress): Promise<PublicKey>;
|
|
54
|
+
/**
|
|
55
|
+
* Retrieves the master outgoing viewing public key.
|
|
56
|
+
* @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
|
+
*/
|
|
60
|
+
getMasterOutgoingViewingPublicKey(account: AztecAddress): Promise<PublicKey>;
|
|
61
|
+
/**
|
|
62
|
+
* Retrieves the master tagging public key.
|
|
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
|
+
*/
|
|
67
|
+
getMasterTaggingPublicKey(account: AztecAddress): Promise<PublicKey>;
|
|
68
|
+
/**
|
|
69
|
+
* Retrieves master incoming viewing secret key.
|
|
70
|
+
* @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
|
+
*/
|
|
74
|
+
getMasterIncomingViewingSecretKey(account: AztecAddress): Promise<GrumpkinScalar>;
|
|
75
|
+
/**
|
|
76
|
+
* Retrieves application outgoing viewing secret key.
|
|
77
|
+
* @throws If the account does not exist in the key store.
|
|
78
|
+
* @param account - The account to retrieve the application outgoing viewing secret key for.
|
|
79
|
+
* @param app - The application address to retrieve the outgoing viewing secret key for.
|
|
80
|
+
* @returns A Promise that resolves to the application outgoing viewing secret key.
|
|
81
|
+
*/
|
|
82
|
+
getAppOutgoingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise<Fr>;
|
|
83
|
+
/**
|
|
84
|
+
* Retrieves the sk_m corresponding to the pk_m.
|
|
85
|
+
* @throws If the provided public key is not associated with any of the registered accounts.
|
|
86
|
+
* @param pkM - The master public key to get secret key for.
|
|
87
|
+
* @returns A Promise that resolves to sk_m.
|
|
88
|
+
* @dev Used when feeding the sk_m to the kernel circuit for keys verification.
|
|
89
|
+
*/
|
|
90
|
+
getMasterSecretKey(pkM: PublicKey): Promise<GrumpkinScalar>;
|
|
91
|
+
/**
|
|
92
|
+
* Gets the key prefix and account address for a given value.
|
|
93
|
+
* @returns A tuple containing the key prefix and account address.
|
|
94
|
+
* @dev Note that this is quite inefficient but it should not matter because there should never be too many keys
|
|
95
|
+
* in the key store.
|
|
96
|
+
*/
|
|
97
|
+
getKeyPrefixAndAccount(value: Bufferable): Promise<[KeyPrefix, AztecAddress]>;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=key_store.d.ts.map
|
|
@@ -0,0 +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,0BAA0B,CAAC;AAErE,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;AAE5B;;GAEG;AACH,qBAAa,QAAQ;;IACnB,gBAAuB,cAAc,KAAK;gBAG9B,QAAQ,EAAE,iBAAiB;IAIvC;;;OAGG;IACI,aAAa,IAAI,OAAO,CAAC,eAAe,CAAC;IAMhD;;;;;OAKG;IACU,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAsCzF;;;OAGG;IACU,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAOnD;;;;;;OAMG;IACU,uBAAuB,CAAC,OAAO,EAAE,EAAE,EAAE,eAAe,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAuC/G;;;;;OAKG;IACU,2BAA2B,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IAUnF;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IAUzF;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IAUzF;;;;;OAKG;IACU,yBAAyB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IAUjF;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC;IAU9F;;;;;;OAMG;IACU,8BAA8B,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC;IAelG;;;;;;OAMG;IACU,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC;IAmBxE;;;;;OAKG;IACU,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;CAe3F"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { GeneratorIndex } from '@aztec/constants';
|
|
2
|
+
import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto';
|
|
3
|
+
import { Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields';
|
|
4
|
+
import { toArray } from '@aztec/foundation/iterable';
|
|
5
|
+
import { serializeToBuffer } from '@aztec/foundation/serialize';
|
|
6
|
+
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
7
|
+
import { CompleteAddress } from '@aztec/stdlib/contract';
|
|
8
|
+
import { KeyValidationRequest } from '@aztec/stdlib/kernel';
|
|
9
|
+
import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecretKey } from '@aztec/stdlib/keys';
|
|
10
|
+
/**
|
|
11
|
+
* Used for managing keys. Can hold keys of multiple accounts.
|
|
12
|
+
*/ export class KeyStore {
|
|
13
|
+
static SCHEMA_VERSION = 1;
|
|
14
|
+
#keys;
|
|
15
|
+
constructor(database){
|
|
16
|
+
this.#keys = database.openMap('key_store');
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new account from a randomly generated secret key.
|
|
20
|
+
* @returns A promise that resolves to the newly created account's CompleteAddress.
|
|
21
|
+
*/ createAccount() {
|
|
22
|
+
const sk = Fr.random();
|
|
23
|
+
const partialAddress = Fr.random();
|
|
24
|
+
return this.addAccount(sk, partialAddress);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Adds an account to the key store from the provided secret key.
|
|
28
|
+
* @param sk - The secret key of the account.
|
|
29
|
+
* @param partialAddress - The partial address of the account.
|
|
30
|
+
* @returns The account's complete address.
|
|
31
|
+
*/ async addAccount(sk, partialAddress) {
|
|
32
|
+
const { masterNullifierSecretKey, masterIncomingViewingSecretKey, masterOutgoingViewingSecretKey, masterTaggingSecretKey, publicKeys } = await deriveKeys(sk);
|
|
33
|
+
const completeAddress = await CompleteAddress.fromSecretKeyAndPartialAddress(sk, partialAddress);
|
|
34
|
+
const { address: account } = completeAddress;
|
|
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;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Retrieves addresses of accounts stored in the key store.
|
|
59
|
+
* @returns A Promise that resolves to an array of account addresses.
|
|
60
|
+
*/ async getAccounts() {
|
|
61
|
+
const allMapKeys = await toArray(this.#keys.keysAsync());
|
|
62
|
+
// We return account addresses based on the map keys that end with '-ivsk_m'
|
|
63
|
+
const accounts = allMapKeys.filter((key)=>key.endsWith('-ivsk_m')).map((key)=>key.split('-')[0]);
|
|
64
|
+
return accounts.map((account)=>AztecAddress.fromString(account));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Gets the key validation request for a given master public key hash and contract address.
|
|
68
|
+
* @throws If the account corresponding to the master public key hash does not exist in the key store.
|
|
69
|
+
* @param pkMHash - The master public key hash.
|
|
70
|
+
* @param contractAddress - The contract address to silo the secret key in the key validation request with.
|
|
71
|
+
* @returns The key validation request.
|
|
72
|
+
*/ async getKeyValidationRequest(pkMHash, contractAddress) {
|
|
73
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
|
|
74
|
+
// Now we find the master public key for the account
|
|
75
|
+
const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
|
|
76
|
+
if (!pkMBuffer) {
|
|
77
|
+
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()}.`);
|
|
78
|
+
}
|
|
79
|
+
const pkM = Point.fromBuffer(pkMBuffer);
|
|
80
|
+
const computedPkMHash = await pkM.hash();
|
|
81
|
+
if (!computedPkMHash.equals(pkMHash)) {
|
|
82
|
+
throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
|
|
83
|
+
}
|
|
84
|
+
// Now we find the secret key for the public key
|
|
85
|
+
const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}sk_m`);
|
|
86
|
+
if (!skMBuffer) {
|
|
87
|
+
throw new Error(`Could not find ${keyPrefix}sk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
|
|
88
|
+
}
|
|
89
|
+
const skM = GrumpkinScalar.fromBuffer(skMBuffer);
|
|
90
|
+
// We sanity check that it's possible to derive the public key from the secret key
|
|
91
|
+
const derivedPkM = await derivePublicKeyFromSecretKey(skM);
|
|
92
|
+
if (!derivedPkM.equals(pkM)) {
|
|
93
|
+
throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
|
|
94
|
+
}
|
|
95
|
+
// At last we silo the secret key and return the key validation request
|
|
96
|
+
const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix);
|
|
97
|
+
return new KeyValidationRequest(pkM, skApp);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Gets the master nullifier public key for a given account.
|
|
101
|
+
* @throws If the account does not exist in the key store.
|
|
102
|
+
* @param account - The account address for which to retrieve the master nullifier public key.
|
|
103
|
+
* @returns The master nullifier public key for the account.
|
|
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);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Gets the master incoming viewing public key for a given account.
|
|
113
|
+
* @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
|
+
*/ async getMasterIncomingViewingPublicKey(account) {
|
|
117
|
+
const masterIncomingViewingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ivpk_m`);
|
|
118
|
+
if (!masterIncomingViewingPublicKeyBuffer) {
|
|
119
|
+
throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
|
|
120
|
+
}
|
|
121
|
+
return Point.fromBuffer(masterIncomingViewingPublicKeyBuffer);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Retrieves the master outgoing viewing public key.
|
|
125
|
+
* @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
|
+
*/ async getMasterOutgoingViewingPublicKey(account) {
|
|
129
|
+
const masterOutgoingViewingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ovpk_m`);
|
|
130
|
+
if (!masterOutgoingViewingPublicKeyBuffer) {
|
|
131
|
+
throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
|
|
132
|
+
}
|
|
133
|
+
return Point.fromBuffer(masterOutgoingViewingPublicKeyBuffer);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Retrieves the master tagging public key.
|
|
137
|
+
* @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
|
+
*/ async getMasterTaggingPublicKey(account) {
|
|
141
|
+
const masterTaggingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-tpk_m`);
|
|
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
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Retrieves master incoming viewing secret key.
|
|
149
|
+
* @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
|
+
*/ async getMasterIncomingViewingSecretKey(account) {
|
|
153
|
+
const masterIncomingViewingSecretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ivsk_m`);
|
|
154
|
+
if (!masterIncomingViewingSecretKeyBuffer) {
|
|
155
|
+
throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
|
|
156
|
+
}
|
|
157
|
+
return GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Retrieves application outgoing viewing secret key.
|
|
161
|
+
* @throws If the account does not exist in the key store.
|
|
162
|
+
* @param account - The account to retrieve the application outgoing viewing secret key for.
|
|
163
|
+
* @param app - The application address to retrieve the outgoing viewing secret key for.
|
|
164
|
+
* @returns A Promise that resolves to the application outgoing viewing secret key.
|
|
165
|
+
*/ async getAppOutgoingViewingSecretKey(account, app) {
|
|
166
|
+
const masterOutgoingViewingSecretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ovsk_m`);
|
|
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);
|
|
171
|
+
return poseidon2HashWithSeparator([
|
|
172
|
+
masterOutgoingViewingSecretKey.hi,
|
|
173
|
+
masterOutgoingViewingSecretKey.lo,
|
|
174
|
+
app
|
|
175
|
+
], GeneratorIndex.OVSK_M);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Retrieves the sk_m corresponding to the pk_m.
|
|
179
|
+
* @throws If the provided public key is not associated with any of the registered accounts.
|
|
180
|
+
* @param pkM - The master public key to get secret key for.
|
|
181
|
+
* @returns A Promise that resolves to sk_m.
|
|
182
|
+
* @dev Used when feeding the sk_m to the kernel circuit for keys verification.
|
|
183
|
+
*/ async getMasterSecretKey(pkM) {
|
|
184
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkM);
|
|
185
|
+
const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}sk_m`);
|
|
186
|
+
if (!secretKeyBuffer) {
|
|
187
|
+
throw new Error(`Could not find ${keyPrefix}sk_m for ${keyPrefix}pk_m ${pkM.toString()}. This should not happen.`);
|
|
188
|
+
}
|
|
189
|
+
const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
|
|
190
|
+
const derivedpkM = await derivePublicKeyFromSecretKey(skM);
|
|
191
|
+
if (!derivedpkM.equals(pkM)) {
|
|
192
|
+
throw new Error(`Could not find ${keyPrefix}skM for ${keyPrefix}pkM ${pkM.toString()} in secret keys buffer.`);
|
|
193
|
+
}
|
|
194
|
+
return Promise.resolve(skM);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Gets the key prefix and account address for a given value.
|
|
198
|
+
* @returns A tuple containing the key prefix and account address.
|
|
199
|
+
* @dev Note that this is quite inefficient but it should not matter because there should never be too many keys
|
|
200
|
+
* in the key store.
|
|
201
|
+
*/ async getKeyPrefixAndAccount(value) {
|
|
202
|
+
const valueBuffer = serializeToBuffer(value);
|
|
203
|
+
for await (const [key, val] of this.#keys.entriesAsync()){
|
|
204
|
+
// Browser returns Uint8Array, Node.js returns Buffer
|
|
205
|
+
if (Buffer.from(val).equals(valueBuffer)) {
|
|
206
|
+
for (const prefix of KEY_PREFIXES){
|
|
207
|
+
if (key.includes(`-${prefix}`)) {
|
|
208
|
+
const account = AztecAddress.fromString(key.split('-')[0]);
|
|
209
|
+
return [
|
|
210
|
+
prefix,
|
|
211
|
+
account
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
throw new Error(`Could not find key prefix.`);
|
|
218
|
+
}
|
|
219
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aztec/key-store",
|
|
3
|
+
"version": "0.0.0-test.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": "./dest/index.js",
|
|
6
|
+
"typedocOptions": {
|
|
7
|
+
"entryPoints": [
|
|
8
|
+
"./src/index.ts"
|
|
9
|
+
],
|
|
10
|
+
"name": "KeyStore",
|
|
11
|
+
"tsconfig": "./tsconfig.json"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "yarn clean && tsc -b",
|
|
15
|
+
"build:dev": "tsc -b --watch",
|
|
16
|
+
"clean": "rm -rf ./dest .tsbuildinfo",
|
|
17
|
+
"formatting": "run -T prettier --check ./src && run -T eslint ./src",
|
|
18
|
+
"formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src",
|
|
19
|
+
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
|
|
20
|
+
},
|
|
21
|
+
"inherits": [
|
|
22
|
+
"../package.common.json"
|
|
23
|
+
],
|
|
24
|
+
"jest": {
|
|
25
|
+
"moduleNameMapper": {
|
|
26
|
+
"^(\\.{1,2}/.*)\\.[cm]?js$": "$1"
|
|
27
|
+
},
|
|
28
|
+
"testRegex": "./src/.*\\.test\\.(js|mjs|ts)$",
|
|
29
|
+
"rootDir": "./src",
|
|
30
|
+
"transform": {
|
|
31
|
+
"^.+\\.tsx?$": [
|
|
32
|
+
"@swc/jest",
|
|
33
|
+
{
|
|
34
|
+
"jsc": {
|
|
35
|
+
"parser": {
|
|
36
|
+
"syntax": "typescript",
|
|
37
|
+
"decorators": true
|
|
38
|
+
},
|
|
39
|
+
"transform": {
|
|
40
|
+
"decoratorVersion": "2022-03"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"extensionsToTreatAsEsm": [
|
|
47
|
+
".ts"
|
|
48
|
+
],
|
|
49
|
+
"reporters": [
|
|
50
|
+
"default"
|
|
51
|
+
],
|
|
52
|
+
"testTimeout": 120000,
|
|
53
|
+
"setupFiles": [
|
|
54
|
+
"../../foundation/src/jest/setup.mjs"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@aztec/constants": "0.0.0-test.0",
|
|
59
|
+
"@aztec/foundation": "0.0.0-test.0",
|
|
60
|
+
"@aztec/kv-store": "0.0.0-test.0",
|
|
61
|
+
"@aztec/stdlib": "0.0.0-test.0",
|
|
62
|
+
"tslib": "^2.4.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@jest/globals": "^29.5.0",
|
|
66
|
+
"@types/jest": "^29.5.0",
|
|
67
|
+
"@types/node": "^18.7.23",
|
|
68
|
+
"jest": "^29.5.0",
|
|
69
|
+
"ts-node": "^10.9.1",
|
|
70
|
+
"typescript": "^5.0.4"
|
|
71
|
+
},
|
|
72
|
+
"files": [
|
|
73
|
+
"dest",
|
|
74
|
+
"src",
|
|
75
|
+
"!*.test.*"
|
|
76
|
+
],
|
|
77
|
+
"types": "./dest/index.d.ts",
|
|
78
|
+
"engines": {
|
|
79
|
+
"node": ">=18"
|
|
80
|
+
}
|
|
81
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './key_store.js';
|
package/src/key_store.ts
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { GeneratorIndex } from '@aztec/constants';
|
|
2
|
+
import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto';
|
|
3
|
+
import { Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields';
|
|
4
|
+
import { toArray } from '@aztec/foundation/iterable';
|
|
5
|
+
import { type Bufferable, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
6
|
+
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
7
|
+
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
8
|
+
import { CompleteAddress, type PartialAddress } from '@aztec/stdlib/contract';
|
|
9
|
+
import { KeyValidationRequest } from '@aztec/stdlib/kernel';
|
|
10
|
+
import {
|
|
11
|
+
KEY_PREFIXES,
|
|
12
|
+
type KeyPrefix,
|
|
13
|
+
type PublicKey,
|
|
14
|
+
computeAppSecretKey,
|
|
15
|
+
deriveKeys,
|
|
16
|
+
derivePublicKeyFromSecretKey,
|
|
17
|
+
} from '@aztec/stdlib/keys';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Used for managing keys. Can hold keys of multiple accounts.
|
|
21
|
+
*/
|
|
22
|
+
export class KeyStore {
|
|
23
|
+
public static readonly SCHEMA_VERSION = 1;
|
|
24
|
+
#keys: AztecAsyncMap<string, Buffer>;
|
|
25
|
+
|
|
26
|
+
constructor(database: AztecAsyncKVStore) {
|
|
27
|
+
this.#keys = database.openMap('key_store');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates a new account from a randomly generated secret key.
|
|
32
|
+
* @returns A promise that resolves to the newly created account's CompleteAddress.
|
|
33
|
+
*/
|
|
34
|
+
public createAccount(): Promise<CompleteAddress> {
|
|
35
|
+
const sk = Fr.random();
|
|
36
|
+
const partialAddress = Fr.random();
|
|
37
|
+
return this.addAccount(sk, partialAddress);
|
|
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.
|
|
43
|
+
* @param partialAddress - The partial address of the account.
|
|
44
|
+
* @returns The account's complete address.
|
|
45
|
+
*/
|
|
46
|
+
public async addAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
|
|
47
|
+
const {
|
|
48
|
+
masterNullifierSecretKey,
|
|
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;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Retrieves addresses of accounts stored in the key store.
|
|
86
|
+
* @returns A Promise that resolves to an array of account addresses.
|
|
87
|
+
*/
|
|
88
|
+
public async getAccounts(): Promise<AztecAddress[]> {
|
|
89
|
+
const allMapKeys = await toArray(this.#keys.keysAsync());
|
|
90
|
+
// We return account addresses based on the map keys that end with '-ivsk_m'
|
|
91
|
+
const accounts = allMapKeys.filter(key => key.endsWith('-ivsk_m')).map(key => key.split('-')[0]);
|
|
92
|
+
return accounts.map(account => AztecAddress.fromString(account));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Gets the key validation request for a given master public key hash and contract address.
|
|
97
|
+
* @throws If the account corresponding to the master public key hash does not exist in the key store.
|
|
98
|
+
* @param pkMHash - The master public key hash.
|
|
99
|
+
* @param contractAddress - The contract address to silo the secret key in the key validation request with.
|
|
100
|
+
* @returns The key validation request.
|
|
101
|
+
*/
|
|
102
|
+
public async getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise<KeyValidationRequest> {
|
|
103
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
|
|
104
|
+
|
|
105
|
+
// Now we find the master public key for the account
|
|
106
|
+
const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
|
|
107
|
+
if (!pkMBuffer) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const pkM = Point.fromBuffer(pkMBuffer);
|
|
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
|
+
}
|
|
118
|
+
|
|
119
|
+
// Now we find the secret key for the public key
|
|
120
|
+
const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}sk_m`);
|
|
121
|
+
if (!skMBuffer) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Could not find ${keyPrefix}sk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const skM = GrumpkinScalar.fromBuffer(skMBuffer);
|
|
128
|
+
|
|
129
|
+
// We sanity check that it's possible to derive the public key from the secret key
|
|
130
|
+
const derivedPkM = await derivePublicKeyFromSecretKey(skM);
|
|
131
|
+
if (!derivedPkM.equals(pkM)) {
|
|
132
|
+
throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// At last we silo the secret key and return the key validation request
|
|
136
|
+
const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix!);
|
|
137
|
+
|
|
138
|
+
return new KeyValidationRequest(pkM, skApp);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Gets the master nullifier public key for a given account.
|
|
143
|
+
* @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
|
+
*/
|
|
147
|
+
public async getMasterNullifierPublicKey(account: AztecAddress): Promise<PublicKey> {
|
|
148
|
+
const masterNullifierPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-npk_m`);
|
|
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);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Gets the master incoming viewing public key for a given account.
|
|
159
|
+
* @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
|
+
*/
|
|
163
|
+
public async getMasterIncomingViewingPublicKey(account: AztecAddress): Promise<PublicKey> {
|
|
164
|
+
const masterIncomingViewingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ivpk_m`);
|
|
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);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Retrieves the master outgoing viewing public key.
|
|
175
|
+
* @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
|
+
*/
|
|
179
|
+
public async getMasterOutgoingViewingPublicKey(account: AztecAddress): Promise<PublicKey> {
|
|
180
|
+
const masterOutgoingViewingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ovpk_m`);
|
|
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);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Retrieves the master tagging public key.
|
|
191
|
+
* @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
|
+
*/
|
|
195
|
+
public async getMasterTaggingPublicKey(account: AztecAddress): Promise<PublicKey> {
|
|
196
|
+
const masterTaggingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-tpk_m`);
|
|
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);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Retrieves master incoming viewing secret key.
|
|
207
|
+
* @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
|
+
*/
|
|
211
|
+
public async getMasterIncomingViewingSecretKey(account: AztecAddress): Promise<GrumpkinScalar> {
|
|
212
|
+
const masterIncomingViewingSecretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ivsk_m`);
|
|
213
|
+
if (!masterIncomingViewingSecretKeyBuffer) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Retrieves application outgoing viewing secret key.
|
|
223
|
+
* @throws If the account does not exist in the key store.
|
|
224
|
+
* @param account - The account to retrieve the application outgoing viewing secret key for.
|
|
225
|
+
* @param app - The application address to retrieve the outgoing viewing secret key for.
|
|
226
|
+
* @returns A Promise that resolves to the application outgoing viewing secret key.
|
|
227
|
+
*/
|
|
228
|
+
public async getAppOutgoingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise<Fr> {
|
|
229
|
+
const masterOutgoingViewingSecretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ovsk_m`);
|
|
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);
|
|
236
|
+
|
|
237
|
+
return poseidon2HashWithSeparator(
|
|
238
|
+
[masterOutgoingViewingSecretKey.hi, masterOutgoingViewingSecretKey.lo, app],
|
|
239
|
+
GeneratorIndex.OVSK_M,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Retrieves the sk_m corresponding to the pk_m.
|
|
245
|
+
* @throws If the provided public key is not associated with any of the registered accounts.
|
|
246
|
+
* @param pkM - The master public key to get secret key for.
|
|
247
|
+
* @returns A Promise that resolves to sk_m.
|
|
248
|
+
* @dev Used when feeding the sk_m to the kernel circuit for keys verification.
|
|
249
|
+
*/
|
|
250
|
+
public async getMasterSecretKey(pkM: PublicKey): Promise<GrumpkinScalar> {
|
|
251
|
+
const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkM);
|
|
252
|
+
|
|
253
|
+
const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}sk_m`);
|
|
254
|
+
if (!secretKeyBuffer) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
`Could not find ${keyPrefix}sk_m for ${keyPrefix}pk_m ${pkM.toString()}. This should not happen.`,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
|
|
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
|
+
}
|
|
265
|
+
|
|
266
|
+
return Promise.resolve(skM);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Gets the key prefix and account address for a given value.
|
|
271
|
+
* @returns A tuple containing the key prefix and account address.
|
|
272
|
+
* @dev Note that this is quite inefficient but it should not matter because there should never be too many keys
|
|
273
|
+
* in the key store.
|
|
274
|
+
*/
|
|
275
|
+
public async getKeyPrefixAndAccount(value: Bufferable): Promise<[KeyPrefix, AztecAddress]> {
|
|
276
|
+
const valueBuffer = serializeToBuffer(value);
|
|
277
|
+
for await (const [key, val] of this.#keys.entriesAsync()) {
|
|
278
|
+
// Browser returns Uint8Array, Node.js returns Buffer
|
|
279
|
+
if (Buffer.from(val).equals(valueBuffer)) {
|
|
280
|
+
for (const prefix of KEY_PREFIXES) {
|
|
281
|
+
if (key.includes(`-${prefix}`)) {
|
|
282
|
+
const account = AztecAddress.fromString(key.split('-')[0]);
|
|
283
|
+
return [prefix, account];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
throw new Error(`Could not find key prefix.`);
|
|
289
|
+
}
|
|
290
|
+
}
|