@idos-network/enclave 1.5.0 → 2.2.2
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/dist/{base-D5Ywnfua.d.mts → base-CjIdohef.d.mts} +4 -4
- package/dist/{base-D5Ywnfua.d.mts.map → base-CjIdohef.d.mts.map} +1 -1
- package/dist/client-Dqb0xbdb.mjs +2 -0
- package/dist/{client-4H2-Ewlq.mjs.map → client-Dqb0xbdb.mjs.map} +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -2
- package/dist/keys-P2XLU3qP.mjs +2 -0
- package/dist/keys-P2XLU3qP.mjs.map +1 -0
- package/dist/local.d.mts +3 -4
- package/dist/local.d.mts.map +1 -1
- package/dist/local.mjs +1 -252
- package/dist/local.mjs.map +1 -1
- package/dist/mpc/index.mjs +1 -2
- package/package.json +10 -9
- package/src/base.test.ts +1 -1
- package/src/base.ts +5 -1
- package/src/local.test.ts +197 -1
- package/src/local.ts +46 -15
- package/dist/client-4H2-Ewlq.mjs +0 -1197
- package/dist/keys-DLzoEkkD.mjs +0 -187
- package/dist/keys-DLzoEkkD.mjs.map +0 -1
package/dist/local.mjs
CHANGED
|
@@ -1,253 +1,2 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import { t as Client } from "./client-4H2-Ewlq.mjs";
|
|
3
|
-
import { base64Codec, base64Encode, hexDecode, hexEncode, utf8Decode, utf8Encode } from "@idos-network/utils/codecs";
|
|
4
|
-
import { generatePassword } from "@idos-network/utils/cryptography";
|
|
5
|
-
import { decrypt, encrypt, keyDerivation } from "@idos-network/utils/encryption";
|
|
6
|
-
import { LocalStorageStore } from "@idos-network/utils/store";
|
|
7
|
-
import { syncScrypt } from "scrypt-js";
|
|
8
|
-
import nacl from "tweetnacl";
|
|
9
|
-
//#region src/local.ts
|
|
10
|
-
var LocalEnclave = class extends BaseProvider {
|
|
11
|
-
allowedEncryptionStores;
|
|
12
|
-
store;
|
|
13
|
-
storeBase64;
|
|
14
|
-
storeObfuscated;
|
|
15
|
-
storeObfuscatedBase64;
|
|
16
|
-
mpcClientInstance;
|
|
17
|
-
storedEncryptionProfile;
|
|
18
|
-
constructor(options) {
|
|
19
|
-
super(options);
|
|
20
|
-
this.allowedEncryptionStores = options.allowedEncryptionStores ?? ["user"];
|
|
21
|
-
this.store = options.store ?? new LocalStorageStore();
|
|
22
|
-
this.storeObfuscated = this.store.pipeCodec(this.userIdObfuscationCodec());
|
|
23
|
-
this.storeBase64 = this.store.pipeCodec(base64Codec);
|
|
24
|
-
this.storeObfuscatedBase64 = this.store.pipeCodec(this.userIdObfuscationCodec()).pipeCodec(base64Codec);
|
|
25
|
-
if (options.mpcConfiguration) this.mpcClientInstance = new Client(options.mpcConfiguration.nodeUrl, options.mpcConfiguration.contractAddress, options.mpcConfiguration.numMalicious, options.mpcConfiguration.numNodes, options.mpcConfiguration.numToReconstruct, options.walletType ?? "EVM", options.walletAddress ?? "", options.walletPublicKey);
|
|
26
|
-
}
|
|
27
|
-
/** @override parent method to reset the enclave */
|
|
28
|
-
async reset() {
|
|
29
|
-
await super.reset();
|
|
30
|
-
this.storedEncryptionProfile = void 0;
|
|
31
|
-
this.store.reset();
|
|
32
|
-
}
|
|
33
|
-
/** @override parent method to reconfigure the enclave */
|
|
34
|
-
async reconfigure(options = {}) {
|
|
35
|
-
await super.reconfigure(options);
|
|
36
|
-
if (this.mpcClientInstance && options.walletType && options.walletAddress) this.mpcClientInstance.reconfigure(options.walletType, options.walletAddress ?? "", options.walletPublicKey);
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Return a codec that encrypts/decrypts data using a key derived from the provider's user ID.
|
|
40
|
-
*
|
|
41
|
-
* This ensures that data is minimally obfuscated to avoid low-sophistication attacks.
|
|
42
|
-
*/
|
|
43
|
-
userIdObfuscationCodec() {
|
|
44
|
-
const self = this;
|
|
45
|
-
const toKey = (userId, salt) => syncScrypt(utf8Encode(userId), salt, 16384, 8, 1, nacl.secretbox.keyLength);
|
|
46
|
-
return {
|
|
47
|
-
encode(data) {
|
|
48
|
-
if (!self.options.userId) throw new Error("User ID required for encryption");
|
|
49
|
-
const dataBytes = utf8Encode(data);
|
|
50
|
-
const nonce = nacl.randomBytes(nacl.secretbox.nonceLength);
|
|
51
|
-
const salt = nacl.randomBytes(16);
|
|
52
|
-
const key = toKey(self.options.userId, salt);
|
|
53
|
-
const payload = nacl.secretbox(dataBytes, nonce, key);
|
|
54
|
-
const combined = new Uint8Array(salt.length + nonce.length + payload.length);
|
|
55
|
-
combined.set(salt, 0);
|
|
56
|
-
combined.set(nonce, salt.length);
|
|
57
|
-
combined.set(payload, salt.length + nonce.length);
|
|
58
|
-
return `0x${hexEncode(combined)}`;
|
|
59
|
-
},
|
|
60
|
-
decode(payload) {
|
|
61
|
-
if (!self.options.userId) throw new Error("User ID required for decryption");
|
|
62
|
-
if (payload.slice(0, 2) !== "0x") throw new Error(`missing 0x prefix: ${payload}`);
|
|
63
|
-
const combined = hexDecode(payload.slice(2));
|
|
64
|
-
const salt = combined.slice(0, 16);
|
|
65
|
-
const nonce = combined.slice(16, 16 + nacl.secretbox.nonceLength);
|
|
66
|
-
const ciphertext = combined.slice(16 + nacl.secretbox.nonceLength);
|
|
67
|
-
const key = toKey(self.options.userId, salt);
|
|
68
|
-
const result = nacl.secretbox.open(ciphertext, nonce, key);
|
|
69
|
-
if (!result) throw new Error("Failed to decrypt data");
|
|
70
|
-
return utf8Decode(result);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
/** @see parent method extended with loading the profile from the store */
|
|
75
|
-
async load() {
|
|
76
|
-
await super.load();
|
|
77
|
-
const userId = await this.store.get(STORAGE_KEYS.USER_ID);
|
|
78
|
-
if (userId) this.options.userId = userId;
|
|
79
|
-
let password = await this.storeObfuscated.get(STORAGE_KEYS.OBFUSCATED_PASSWORD);
|
|
80
|
-
if (!password) {
|
|
81
|
-
password = await this.store.get(STORAGE_KEYS.DEPRECATED___PASSWORD);
|
|
82
|
-
if (!password) return;
|
|
83
|
-
await this.storeObfuscated.set(STORAGE_KEYS.OBFUSCATED_PASSWORD, password);
|
|
84
|
-
}
|
|
85
|
-
await this.store.delete(STORAGE_KEYS.DEPRECATED___PASSWORD);
|
|
86
|
-
let encryptionSecretKey = await this.storeObfuscatedBase64.get(STORAGE_KEYS.OBFUSCATED_BASE64_ENCRYPTION_SECRET_KEY);
|
|
87
|
-
if (!encryptionSecretKey) {
|
|
88
|
-
encryptionSecretKey = await this.storeBase64.get(STORAGE_KEYS.DEPRECATED___ENCRYPTION_SECRET_KEY);
|
|
89
|
-
if (!encryptionSecretKey) return;
|
|
90
|
-
await this.storeObfuscatedBase64.set(STORAGE_KEYS.OBFUSCATED_BASE64_ENCRYPTION_SECRET_KEY, encryptionSecretKey);
|
|
91
|
-
}
|
|
92
|
-
await this.store.delete(STORAGE_KEYS.DEPRECATED___ENCRYPTION_SECRET_KEY);
|
|
93
|
-
await this.store.delete(STORAGE_KEYS.DEPRECATED___ENCRYPTION_PRIVATE_KEY);
|
|
94
|
-
if (!password || !userId || !encryptionSecretKey) return;
|
|
95
|
-
let encryptionPasswordStore = await this.store.get(STORAGE_KEYS.ENCRYPTION_PASSWORD_STORE);
|
|
96
|
-
if (!encryptionPasswordStore || encryptionPasswordStore === "password") {
|
|
97
|
-
encryptionPasswordStore = "user";
|
|
98
|
-
await this.store.set(STORAGE_KEYS.ENCRYPTION_PASSWORD_STORE, encryptionPasswordStore);
|
|
99
|
-
}
|
|
100
|
-
this.storedEncryptionProfile = {
|
|
101
|
-
userId,
|
|
102
|
-
password,
|
|
103
|
-
keyPair: nacl.box.keyPair.fromSecretKey(encryptionSecretKey),
|
|
104
|
-
encryptionPasswordStore
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Encrypts a message to a receiver.
|
|
109
|
-
* This method also checks if the user is authorized to use the keys.
|
|
110
|
-
*
|
|
111
|
-
* @param message - The message to encrypt.
|
|
112
|
-
* @param receiverPublicKey - The public key of the receiver.
|
|
113
|
-
*
|
|
114
|
-
* @returns The encrypted message.
|
|
115
|
-
*/
|
|
116
|
-
async encrypt(message, receiverPublicKey) {
|
|
117
|
-
const { keyPair } = await this.getPrivateEncryptionProfile();
|
|
118
|
-
return encrypt(message, keyPair.publicKey, receiverPublicKey);
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Decrypts a message from a sender.
|
|
122
|
-
* This method also checks if the user is authorized to use the keys.
|
|
123
|
-
*
|
|
124
|
-
* @param message - The message to decrypt.
|
|
125
|
-
* @param senderPublicKey - The public key of the sender.
|
|
126
|
-
*
|
|
127
|
-
* @returns The decrypted message.
|
|
128
|
-
*/
|
|
129
|
-
async decrypt(message, senderPublicKey) {
|
|
130
|
-
const { keyPair } = await this.getPrivateEncryptionProfile();
|
|
131
|
-
return decrypt(message, keyPair, senderPublicKey);
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* @see BaseProvider#getPrivateEncryptionProfile
|
|
135
|
-
*/
|
|
136
|
-
async getPrivateEncryptionProfile(skipGuard = false) {
|
|
137
|
-
if (this.storedEncryptionProfile) {
|
|
138
|
-
let canBeUsed = this.storedEncryptionProfile.userId === this.userId;
|
|
139
|
-
if (canBeUsed && !skipGuard) canBeUsed = await this.guardKeys();
|
|
140
|
-
if (canBeUsed) return this.storedEncryptionProfile;
|
|
141
|
-
await this.reset();
|
|
142
|
-
}
|
|
143
|
-
let password;
|
|
144
|
-
let encryptionPasswordStore;
|
|
145
|
-
const context = await this.getPasswordContext();
|
|
146
|
-
if (context.encryptionPasswordStore === "user") {
|
|
147
|
-
await this.store.setRememberDuration(context.duration);
|
|
148
|
-
password = context.password;
|
|
149
|
-
encryptionPasswordStore = context.encryptionPasswordStore;
|
|
150
|
-
}
|
|
151
|
-
if (context.encryptionPasswordStore === "mpc") {
|
|
152
|
-
await this.store.setRememberDuration(void 0);
|
|
153
|
-
password = await this.ensureMPCPassword();
|
|
154
|
-
encryptionPasswordStore = "mpc";
|
|
155
|
-
}
|
|
156
|
-
if (!password || !encryptionPasswordStore) throw new Error("Password or encryption password store is not found");
|
|
157
|
-
return this.createEncryptionProfileFromPassword(password, this.userId, encryptionPasswordStore);
|
|
158
|
-
}
|
|
159
|
-
/** @see BaseProvider#ensureUserEncryptionProfile */
|
|
160
|
-
async ensureUserEncryptionProfile() {
|
|
161
|
-
const { keyPair, encryptionPasswordStore, userId } = await this.getPrivateEncryptionProfile();
|
|
162
|
-
return {
|
|
163
|
-
userId,
|
|
164
|
-
userEncryptionPublicKey: base64Encode(keyPair.publicKey),
|
|
165
|
-
encryptionPasswordStore
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* This method needs to check `options` and should derive the password context from it.
|
|
170
|
-
*
|
|
171
|
-
* @returns The password context.
|
|
172
|
-
*/
|
|
173
|
-
async getPasswordContext() {
|
|
174
|
-
throw new Error("Method 'getPasswordContext' has to be implemented in the subclass.");
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Creates and store encryption profile from a password.
|
|
178
|
-
*
|
|
179
|
-
* @param password - The password to use.
|
|
180
|
-
* @param userId - The user id to use.
|
|
181
|
-
* @param encryptionPasswordStore - The encryption password store to use.
|
|
182
|
-
*
|
|
183
|
-
* @returns The encryption profile.
|
|
184
|
-
*/
|
|
185
|
-
async createEncryptionProfileFromPassword(password, userId, encryptionPasswordStore) {
|
|
186
|
-
const secretKey = await keyDerivation(password, userId);
|
|
187
|
-
const keyPair = nacl.box.keyPair.fromSecretKey(secretKey);
|
|
188
|
-
await this.store.set(STORAGE_KEYS.USER_ID, userId);
|
|
189
|
-
await this.storeObfuscated.set(STORAGE_KEYS.OBFUSCATED_PASSWORD, password);
|
|
190
|
-
await this.store.delete(STORAGE_KEYS.DEPRECATED___PASSWORD);
|
|
191
|
-
await this.store.set(STORAGE_KEYS.ENCRYPTION_PASSWORD_STORE, encryptionPasswordStore);
|
|
192
|
-
await this.storeObfuscatedBase64.set(STORAGE_KEYS.OBFUSCATED_BASE64_ENCRYPTION_SECRET_KEY, keyPair.secretKey);
|
|
193
|
-
await this.store.delete(STORAGE_KEYS.DEPRECATED___ENCRYPTION_SECRET_KEY);
|
|
194
|
-
await this.storeBase64.set(STORAGE_KEYS.ENCRYPTION_PUBLIC_KEY, keyPair.publicKey);
|
|
195
|
-
this.storedEncryptionProfile = {
|
|
196
|
-
userId,
|
|
197
|
-
password,
|
|
198
|
-
keyPair,
|
|
199
|
-
encryptionPasswordStore
|
|
200
|
-
};
|
|
201
|
-
return this.storedEncryptionProfile;
|
|
202
|
-
}
|
|
203
|
-
async ensureMPCPassword() {
|
|
204
|
-
if (this.options?.mode !== "new") {
|
|
205
|
-
const { status: downloadStatus, secret: downloadedPassword } = await this.downloadSecret();
|
|
206
|
-
if (downloadStatus === "ok" && downloadedPassword) return utf8Decode(downloadedPassword);
|
|
207
|
-
throw Error("A secret might be stored at MPC ZK nodes, but can't be obtained");
|
|
208
|
-
}
|
|
209
|
-
const password = generatePassword();
|
|
210
|
-
const { status: uploadStatus } = await this.uploadSecret(password);
|
|
211
|
-
if (uploadStatus !== "success") throw Error(`A secret upload failed with status: ${uploadStatus}`);
|
|
212
|
-
return password;
|
|
213
|
-
}
|
|
214
|
-
get mpcClient() {
|
|
215
|
-
if (!this.mpcClientInstance) throw new Error("MPC client is not initialized");
|
|
216
|
-
return this.mpcClientInstance;
|
|
217
|
-
}
|
|
218
|
-
async downloadSecret() {
|
|
219
|
-
if (!this.options.walletAddress) throw new Error("walletAddress is not found");
|
|
220
|
-
const ephemeralKeyPair = nacl.box.keyPair();
|
|
221
|
-
const downloadRequest = this.mpcClient.downloadRequest(ephemeralKeyPair.publicKey);
|
|
222
|
-
const messageToSign = this.mpcClient.downloadMessageToSign(downloadRequest);
|
|
223
|
-
const signedMessage = await this.signTypedData(messageToSign.domain, messageToSign.types, messageToSign.value);
|
|
224
|
-
return this.mpcClient.downloadSecret(this.userId, downloadRequest, signedMessage, ephemeralKeyPair.secretKey);
|
|
225
|
-
}
|
|
226
|
-
async uploadSecret(secret) {
|
|
227
|
-
if (!this.options.walletAddress) {
|
|
228
|
-
console.error("signerAddress is not found");
|
|
229
|
-
return { status: "no-signer-address" };
|
|
230
|
-
}
|
|
231
|
-
const blindedShares = this.mpcClient.getBlindedShares(Buffer.from(secret, "utf8"));
|
|
232
|
-
const uploadRequest = this.mpcClient.uploadRequest(blindedShares);
|
|
233
|
-
const messageToSign = this.mpcClient.uploadMessageToSign(uploadRequest);
|
|
234
|
-
const signedMessage = await this.signTypedData(messageToSign.domain, messageToSign.types, messageToSign.value);
|
|
235
|
-
return this.mpcClient.uploadSecret(this.userId, uploadRequest, signedMessage, blindedShares);
|
|
236
|
-
}
|
|
237
|
-
async addAddressMessageToSign(address, publicKey, addressToAddType) {
|
|
238
|
-
return this.mpcClient.addAddressMessageToSign(address, publicKey, addressToAddType);
|
|
239
|
-
}
|
|
240
|
-
async removeAddressMessageToSign(address, publicKey, addressToRemoveType) {
|
|
241
|
-
return this.mpcClient.removeAddressMessageToSign(address, publicKey, addressToRemoveType);
|
|
242
|
-
}
|
|
243
|
-
async addAddressToMpcSecret(userId, message, signature) {
|
|
244
|
-
return this.mpcClient.addAddress(userId, message, signature);
|
|
245
|
-
}
|
|
246
|
-
async removeAddressFromMpcSecret(userId, message, signature) {
|
|
247
|
-
return this.mpcClient.removeAddress(userId, message, signature);
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
//#endregion
|
|
251
|
-
export { LocalEnclave };
|
|
252
|
-
|
|
1
|
+
import{n as e,t}from"./keys-P2XLU3qP.mjs";import{t as n}from"./client-Dqb0xbdb.mjs";import{base64Codec as r,base64Encode as i,hexDecode as a,hexEncode as o,utf8Decode as s,utf8Encode as c}from"@idos-network/utils/codecs";import{generatePassword as l}from"@idos-network/utils/cryptography";import{decrypt as u,encrypt as d,keyDerivation as f}from"@idos-network/utils/encryption";import{LocalStorageStore as p}from"@idos-network/utils/store";import{syncScrypt as m}from"scrypt-js";import h from"tweetnacl";var g=class extends e{allowedEncryptionStores;store;storeBase64;storeObfuscated;storeObfuscatedBase64;mpcClientInstance;storedEncryptionProfile;constructor(e){super(e),this.allowedEncryptionStores=e.allowedEncryptionStores??[`user`],this.store=e.store??new p,this.storeObfuscated=this.store.pipeCodec(this.userIdObfuscationCodec()),this.storeBase64=this.store.pipeCodec(r),this.storeObfuscatedBase64=this.store.pipeCodec(this.userIdObfuscationCodec()).pipeCodec(r),e.mpcConfiguration&&(this.mpcClientInstance=new n(e.mpcConfiguration.nodeUrl,e.mpcConfiguration.contractAddress,e.mpcConfiguration.numMalicious,e.mpcConfiguration.numNodes,e.mpcConfiguration.numToReconstruct,e.walletType??`EVM`,e.walletAddress??``,e.walletPublicKey))}async reset(){await super.reset(),this.storedEncryptionProfile=void 0,await this.store.reset()}async reconfigure(e={}){await super.reconfigure(e),this.mpcClientInstance&&e.walletType&&e.walletAddress&&this.mpcClientInstance.reconfigure(e.walletType,e.walletAddress??``,e.walletPublicKey)}userIdObfuscationCodec(){let e=this,t=(e,t)=>m(c(e),t,16384,8,1,h.secretbox.keyLength);return{encode(n){if(!e.options.userId)throw Error(`User ID required for encryption`);let r=c(n),i=h.randomBytes(h.secretbox.nonceLength),a=h.randomBytes(16),s=t(e.options.userId,a),l=h.secretbox(r,i,s),u=new Uint8Array(a.length+i.length+l.length);return u.set(a,0),u.set(i,a.length),u.set(l,a.length+i.length),`0x${o(u)}`},decode(n){if(!e.options.userId)throw Error(`User ID required for decryption`);if(n.slice(0,2)!==`0x`)throw Error(`missing 0x prefix: ${n}`);let r=a(n.slice(2)),i=r.slice(0,16),o=r.slice(16,16+h.secretbox.nonceLength),c=r.slice(16+h.secretbox.nonceLength),l=t(e.options.userId,i),u=h.secretbox.open(c,o,l);if(!u)throw Error(`Failed to decrypt data`);return s(u)}}}async load(){await super.load();let e=await this.store.get(t.USER_ID);e&&(this.options.userId=e);let n=await this.storeObfuscated.get(t.OBFUSCATED_PASSWORD);if(!n){if(n=await this.store.get(t.DEPRECATED___PASSWORD),!n)return;await this.storeObfuscated.set(t.OBFUSCATED_PASSWORD,n)}await this.store.delete(t.DEPRECATED___PASSWORD);let r=await this.storeObfuscatedBase64.get(t.OBFUSCATED_BASE64_ENCRYPTION_SECRET_KEY);if(!r){if(r=await this.storeBase64.get(t.DEPRECATED___ENCRYPTION_SECRET_KEY),!r)return;await this.storeObfuscatedBase64.set(t.OBFUSCATED_BASE64_ENCRYPTION_SECRET_KEY,r)}if(await this.store.delete(t.DEPRECATED___ENCRYPTION_SECRET_KEY),await this.store.delete(t.DEPRECATED___ENCRYPTION_PRIVATE_KEY),!n||!e||!r)return;let i=await this.store.get(t.ENCRYPTION_PASSWORD_STORE);(!i||i===`password`)&&(i=`user`,await this.store.set(t.ENCRYPTION_PASSWORD_STORE,i)),this.storedEncryptionProfile={userId:e,password:n,keyPair:h.box.keyPair.fromSecretKey(r),encryptionPasswordStore:i}}async encrypt(e,t){let{keyPair:n}=await this.getPrivateEncryptionProfile();return d(e,n.publicKey,t)}async decrypt(e,t){let{keyPair:n}=await this.getPrivateEncryptionProfile();return u(e,n,t)}async getPrivateEncryptionProfile(e=!1){this.storedEncryptionProfile&&await this.store.hasRememberDurationElapsed()&&await this.reset();let t=this.options.expectedUserEncryptionPublicKey;if(this.storedEncryptionProfile?.userId===this.userId&&(!t||i(this.storedEncryptionProfile.keyPair.publicKey)===t)){if(!e&&!await this.guardKeys())throw Error(`Origin is not authorized to use the keys`);return this.storedEncryptionProfile}if(this.storedEncryptionProfile&&await this.reset(),this.options.encryptionPasswordStore===`mm`)throw Error(`MM encryption profiles require an externally supplied encryption private key`);let n,r,a=await this.getPasswordContext();if(a.encryptionPasswordStore===`user`&&(await this.store.setRememberDuration(a.duration),n=a.password,r=a.encryptionPasswordStore),a.encryptionPasswordStore===`mpc`&&(await this.store.setRememberDuration(void 0),n=await this.ensureMPCPassword(),r=`mpc`),!n||!r)throw Error(`Password or encryption password store is not found`);let o=await this.createEncryptionProfileFromPassword(n,this.userId,r);if(!e&&!await this.guardKeys())throw Error(`Origin is not authorized to use the keys`);return o}async ensureUserEncryptionProfile(){let{keyPair:e,encryptionPasswordStore:t,userId:n}=await this.getPrivateEncryptionProfile();return{userId:n,userEncryptionPublicKey:i(e.publicKey),encryptionPasswordStore:t}}async getPasswordContext(){throw Error(`Method 'getPasswordContext' has to be implemented in the subclass.`)}async createEncryptionProfileFromPassword(e,n,r){let a=await f(e,n),o=h.box.keyPair.fromSecretKey(a),s=this.options.expectedUserEncryptionPublicKey;if(s&&i(o.publicKey)!==s)throw Error(`Derived encryption public key does not match expectedUserEncryptionPublicKey`);return await this.store.set(t.USER_ID,n),await this.storeObfuscated.set(t.OBFUSCATED_PASSWORD,e),await this.store.delete(t.DEPRECATED___PASSWORD),await this.store.set(t.ENCRYPTION_PASSWORD_STORE,r),await this.storeObfuscatedBase64.set(t.OBFUSCATED_BASE64_ENCRYPTION_SECRET_KEY,o.secretKey),await this.store.delete(t.DEPRECATED___ENCRYPTION_SECRET_KEY),await this.storeBase64.set(t.ENCRYPTION_PUBLIC_KEY,o.publicKey),this.storedEncryptionProfile={userId:n,password:e,keyPair:o,encryptionPasswordStore:r},this.storedEncryptionProfile}async ensureMPCPassword(){if(this.options?.mode!==`new`){let{status:e,secret:t}=await this.downloadSecret();if(e===`ok`&&t)return s(t);throw Error(`A secret might be stored at MPC ZK nodes, but can't be obtained`)}let e=l(),{status:t}=await this.uploadSecret(e);if(t!==`success`)throw Error(`A secret upload failed with status: ${t}`);return e}get mpcClient(){if(!this.mpcClientInstance)throw Error(`MPC client is not initialized`);return this.mpcClientInstance}async downloadSecret(){if(!this.options.walletAddress)throw Error(`walletAddress is not found`);let e=h.box.keyPair(),t=this.mpcClient.downloadRequest(e.publicKey),n=this.mpcClient.downloadMessageToSign(t),r=await this.signTypedData(n.domain,n.types,n.value);return this.mpcClient.downloadSecret(this.userId,t,r,e.secretKey)}async uploadSecret(e){if(!this.options.walletAddress)return console.error(`signerAddress is not found`),{status:`no-signer-address`};let t=this.mpcClient.getBlindedShares(Buffer.from(e,`utf8`)),n=this.mpcClient.uploadRequest(t),r=this.mpcClient.uploadMessageToSign(n),i=await this.signTypedData(r.domain,r.types,r.value);return this.mpcClient.uploadSecret(this.userId,n,i,t)}async addAddressMessageToSign(e,t,n){return this.mpcClient.addAddressMessageToSign(e,t,n)}async removeAddressMessageToSign(e,t,n){return this.mpcClient.removeAddressMessageToSign(e,t,n)}async addAddressToMpcSecret(e,t,n){return this.mpcClient.addAddress(e,t,n)}async removeAddressFromMpcSecret(e,t,n){return this.mpcClient.removeAddress(e,t,n)}};export{g as LocalEnclave};
|
|
253
2
|
//# sourceMappingURL=local.mjs.map
|
package/dist/local.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local.mjs","names":["MPCClient"],"sources":["../src/local.ts"],"sourcesContent":["import type { EncryptionPasswordStore } from \"@idos-network/kwil-infra/actions\";\n\nimport {\n base64Codec,\n base64Encode,\n hexDecode,\n hexEncode,\n utf8Decode,\n utf8Encode,\n} from \"@idos-network/utils/codecs\";\nimport { generatePassword } from \"@idos-network/utils/cryptography\";\nimport { decrypt, encrypt, keyDerivation } from \"@idos-network/utils/encryption\";\nimport { LocalStorageStore, type PipeCodecArgs, type Store } from \"@idos-network/utils/store\";\nimport { syncScrypt } from \"scrypt-js\";\nimport nacl from \"tweetnacl\";\n\nimport type {\n AddAddressMessageToSign,\n AddAddressSignatureMessage,\n DownloadMessageToSign,\n RemoveAddressMessageToSign,\n RemoveAddressSignatureMessage,\n UploadMessageToSign,\n} from \"./mpc/types\";\nimport type {\n EnclaveOptions,\n MPCPasswordContext,\n PasswordContext,\n PrivateEncryptionProfile,\n PublicEncryptionProfile,\n} from \"./types\";\n\nimport { BaseProvider } from \"./base\";\nimport { STORAGE_KEYS } from \"./keys\";\nimport { Client as MPCClient } from \"./mpc/client\";\n\nexport interface LocalEnclaveOptions extends EnclaveOptions {\n store?: Store;\n allowedEncryptionStores?: EncryptionPasswordStore[];\n mpcConfiguration?: {\n nodeUrl: string;\n contractAddress: string;\n numMalicious: number;\n numNodes: number;\n numToReconstruct: number;\n };\n}\n\nexport class LocalEnclave<\n K extends LocalEnclaveOptions = LocalEnclaveOptions,\n> extends BaseProvider<K> {\n protected allowedEncryptionStores: EncryptionPasswordStore[];\n\n // Store for data\n protected store: Store;\n protected storeBase64: Store;\n protected storeObfuscated: Store;\n protected storeObfuscatedBase64: Store;\n\n // In case of MPC usage\n protected mpcClientInstance?: MPCClient;\n\n // Stored key pair and user id for that key pair\n // those are most likely loaded from the store\n // during the load() method.\n protected storedEncryptionProfile?: PrivateEncryptionProfile;\n\n constructor(options: K) {\n super(options);\n\n // By default, we only allow password auth method\n this.allowedEncryptionStores = options.allowedEncryptionStores ?? [\"user\"];\n this.store = options.store ?? new LocalStorageStore();\n this.storeObfuscated = this.store.pipeCodec(this.userIdObfuscationCodec());\n this.storeBase64 = this.store.pipeCodec(base64Codec);\n this.storeObfuscatedBase64 = this.store\n .pipeCodec(this.userIdObfuscationCodec())\n .pipeCodec(base64Codec);\n\n if (options.mpcConfiguration) {\n this.mpcClientInstance = new MPCClient(\n options.mpcConfiguration.nodeUrl,\n options.mpcConfiguration.contractAddress,\n options.mpcConfiguration.numMalicious,\n options.mpcConfiguration.numNodes,\n options.mpcConfiguration.numToReconstruct,\n // TODO: This is weird, we should not be forcing this\n options.walletType ?? \"EVM\",\n options.walletAddress ?? \"\",\n options.walletPublicKey,\n );\n }\n }\n\n /** @override parent method to reset the enclave */\n async reset(): Promise<void> {\n await super.reset();\n this.storedEncryptionProfile = undefined;\n this.store.reset();\n }\n\n /** @override parent method to reconfigure the enclave */\n async reconfigure(options: Partial<K> = {}): Promise<void> {\n await super.reconfigure(options);\n // Reconfigure MPC client if any signer information changed\n if (this.mpcClientInstance && options.walletType && options.walletAddress) {\n this.mpcClientInstance.reconfigure(\n options.walletType,\n options.walletAddress ?? \"\",\n options.walletPublicKey,\n );\n }\n }\n\n /**\n * Return a codec that encrypts/decrypts data using a key derived from the provider's user ID.\n *\n * This ensures that data is minimally obfuscated to avoid low-sophistication attacks.\n */\n private userIdObfuscationCodec(): PipeCodecArgs<string> {\n // oxlint-disable-next-line typescript/no-this-alias -- Needed for `this` in nested encode/decode functions\n const self = this;\n\n const toKey = (userId: string, salt: Uint8Array) =>\n syncScrypt(utf8Encode(userId), salt, 16384, 8, 1, nacl.secretbox.keyLength);\n\n return {\n encode(data: string): string {\n if (!self.options.userId) {\n throw new Error(\"User ID required for encryption\");\n }\n\n const dataBytes = utf8Encode(data); // Convert string to Uint8Array\n const nonce = nacl.randomBytes(nacl.secretbox.nonceLength);\n const salt = nacl.randomBytes(16);\n const key = toKey(self.options.userId, salt);\n const payload = nacl.secretbox(dataBytes, nonce, key);\n\n // Store salt + nonce + ciphertext\n const combined = new Uint8Array(salt.length + nonce.length + payload.length);\n combined.set(salt, 0);\n combined.set(nonce, salt.length);\n combined.set(payload, salt.length + nonce.length);\n\n return `0x${hexEncode(combined)}`;\n },\n\n decode(payload: string): string {\n if (!self.options.userId) {\n throw new Error(\"User ID required for decryption\");\n }\n\n if (payload.slice(0, 2) !== \"0x\") {\n throw new Error(`missing 0x prefix: ${payload}`);\n }\n\n const combined = hexDecode(payload.slice(2));\n const salt = combined.slice(0, 16);\n const nonce = combined.slice(16, 16 + nacl.secretbox.nonceLength);\n const ciphertext = combined.slice(16 + nacl.secretbox.nonceLength);\n\n const key = toKey(self.options.userId, salt);\n const result = nacl.secretbox.open(ciphertext, nonce, key);\n\n if (!result) throw new Error(\"Failed to decrypt data\");\n\n return utf8Decode(result); // Convert Uint8Array back to string\n },\n };\n }\n /** @see parent method extended with loading the profile from the store */\n async load(): Promise<void> {\n await super.load();\n\n const userId = await this.store.get<string>(STORAGE_KEYS.USER_ID);\n if (userId) this.options.userId = userId;\n\n let password = await this.storeObfuscated.get<string>(STORAGE_KEYS.OBFUSCATED_PASSWORD);\n if (!password) {\n password = await this.store.get<string>(STORAGE_KEYS.DEPRECATED___PASSWORD);\n if (!password) return;\n\n await this.storeObfuscated.set(STORAGE_KEYS.OBFUSCATED_PASSWORD, password);\n }\n await this.store.delete(STORAGE_KEYS.DEPRECATED___PASSWORD);\n\n let encryptionSecretKey = await this.storeObfuscatedBase64.get<Uint8Array>(\n STORAGE_KEYS.OBFUSCATED_BASE64_ENCRYPTION_SECRET_KEY,\n );\n if (!encryptionSecretKey) {\n encryptionSecretKey = await this.storeBase64.get<Uint8Array>(\n STORAGE_KEYS.DEPRECATED___ENCRYPTION_SECRET_KEY,\n );\n if (!encryptionSecretKey) return;\n\n await this.storeObfuscatedBase64.set(\n STORAGE_KEYS.OBFUSCATED_BASE64_ENCRYPTION_SECRET_KEY,\n encryptionSecretKey,\n );\n }\n await this.store.delete(STORAGE_KEYS.DEPRECATED___ENCRYPTION_SECRET_KEY);\n // Even older version.\n await this.store.delete(STORAGE_KEYS.DEPRECATED___ENCRYPTION_PRIVATE_KEY);\n\n if (!password || !userId || !encryptionSecretKey) {\n return;\n }\n\n let encryptionPasswordStore = await this.store.get<EncryptionPasswordStore>(\n STORAGE_KEYS.ENCRYPTION_PASSWORD_STORE,\n );\n\n // Migration from \"password\" to \"user\"\n // TODO: Remove this after a while\n if (!encryptionPasswordStore || (encryptionPasswordStore as string) === \"password\") {\n encryptionPasswordStore = \"user\";\n await this.store.set<EncryptionPasswordStore>(\n STORAGE_KEYS.ENCRYPTION_PASSWORD_STORE,\n encryptionPasswordStore,\n );\n }\n\n this.storedEncryptionProfile = {\n userId,\n password,\n keyPair: nacl.box.keyPair.fromSecretKey(encryptionSecretKey),\n encryptionPasswordStore,\n };\n }\n\n /**\n * Encrypts a message to a receiver.\n * This method also checks if the user is authorized to use the keys.\n *\n * @param message - The message to encrypt.\n * @param receiverPublicKey - The public key of the receiver.\n *\n * @returns The encrypted message.\n */\n async encrypt(\n message: Uint8Array,\n receiverPublicKey: Uint8Array,\n ): Promise<{ content: Uint8Array; encryptorPublicKey: Uint8Array }> {\n const { keyPair } = await this.getPrivateEncryptionProfile();\n return encrypt(message, keyPair.publicKey, receiverPublicKey);\n }\n\n /**\n * Decrypts a message from a sender.\n * This method also checks if the user is authorized to use the keys.\n *\n * @param message - The message to decrypt.\n * @param senderPublicKey - The public key of the sender.\n *\n * @returns The decrypted message.\n */\n async decrypt(\n message: Uint8Array,\n senderPublicKey: Uint8Array,\n ): Promise<Uint8Array<ArrayBufferLike>> {\n const { keyPair } = await this.getPrivateEncryptionProfile();\n return decrypt(message, keyPair, senderPublicKey);\n }\n\n /**\n * @see BaseProvider#getPrivateEncryptionProfile\n */\n async getPrivateEncryptionProfile(skipGuard = false): Promise<PrivateEncryptionProfile> {\n // In case there is a no key pair, which matches we don't need a new one.\n if (this.storedEncryptionProfile) {\n let canBeUsed = this.storedEncryptionProfile.userId === this.userId;\n\n if (canBeUsed && !skipGuard) {\n // When users matches, we also need to check\n // if origin is authorized to use the keys.\n canBeUsed = await this.guardKeys();\n }\n\n if (canBeUsed) {\n return this.storedEncryptionProfile;\n }\n\n // Something did not match, we need to create a new key pair\n // and reset the enclave.\n await this.reset();\n }\n\n // The stored profile can't be used, or we have to create a new one.\n let password: string | undefined;\n let encryptionPasswordStore: EncryptionPasswordStore | undefined;\n\n const context = await this.getPasswordContext();\n\n if (context.encryptionPasswordStore === \"user\") {\n await this.store.setRememberDuration(context.duration);\n\n password = context.password;\n encryptionPasswordStore = context.encryptionPasswordStore;\n }\n\n if (context.encryptionPasswordStore === \"mpc\") {\n // TODO: This is resetting duration, but should?\n await this.store.setRememberDuration(undefined);\n\n password = await this.ensureMPCPassword();\n encryptionPasswordStore = \"mpc\";\n }\n\n if (!password || !encryptionPasswordStore) {\n throw new Error(\"Password or encryption password store is not found\");\n }\n\n return this.createEncryptionProfileFromPassword(password, this.userId, encryptionPasswordStore);\n }\n\n /** @see BaseProvider#ensureUserEncryptionProfile */\n async ensureUserEncryptionProfile(): Promise<PublicEncryptionProfile> {\n const { keyPair, encryptionPasswordStore, userId } = await this.getPrivateEncryptionProfile();\n\n return {\n userId,\n userEncryptionPublicKey: base64Encode(keyPair.publicKey),\n encryptionPasswordStore: encryptionPasswordStore,\n };\n }\n\n /**\n * This method needs to check `options` and should derive the password context from it.\n *\n * @returns The password context.\n */\n async getPasswordContext(): Promise<PasswordContext | MPCPasswordContext> {\n throw new Error(\"Method 'getPasswordContext' has to be implemented in the subclass.\");\n }\n\n /**\n * Creates and store encryption profile from a password.\n *\n * @param password - The password to use.\n * @param userId - The user id to use.\n * @param encryptionPasswordStore - The encryption password store to use.\n *\n * @returns The encryption profile.\n */\n async createEncryptionProfileFromPassword(\n password: string,\n userId: string,\n encryptionPasswordStore: EncryptionPasswordStore,\n ): Promise<PrivateEncryptionProfile> {\n const secretKey = await keyDerivation(password, userId);\n\n const keyPair = nacl.box.keyPair.fromSecretKey(secretKey);\n\n await this.store.set(STORAGE_KEYS.USER_ID, userId);\n await this.storeObfuscated.set(STORAGE_KEYS.OBFUSCATED_PASSWORD, password);\n await this.store.delete(STORAGE_KEYS.DEPRECATED___PASSWORD);\n await this.store.set(STORAGE_KEYS.ENCRYPTION_PASSWORD_STORE, encryptionPasswordStore);\n await this.storeObfuscatedBase64.set(\n STORAGE_KEYS.OBFUSCATED_BASE64_ENCRYPTION_SECRET_KEY,\n keyPair.secretKey,\n );\n await this.store.delete(STORAGE_KEYS.DEPRECATED___ENCRYPTION_SECRET_KEY);\n await this.storeBase64.set(STORAGE_KEYS.ENCRYPTION_PUBLIC_KEY, keyPair.publicKey);\n\n this.storedEncryptionProfile = {\n userId,\n password,\n keyPair,\n encryptionPasswordStore,\n };\n\n return this.storedEncryptionProfile;\n }\n\n protected async ensureMPCPassword(): Promise<string> {\n if (this.options?.mode !== \"new\") {\n const { status: downloadStatus, secret: downloadedPassword } = await this.downloadSecret();\n\n if (downloadStatus === \"ok\" && downloadedPassword) {\n return utf8Decode(downloadedPassword);\n }\n\n throw Error(\"A secret might be stored at MPC ZK nodes, but can't be obtained\");\n }\n\n const password = generatePassword();\n const { status: uploadStatus } = await this.uploadSecret(password);\n\n if (uploadStatus !== \"success\") {\n throw Error(`A secret upload failed with status: ${uploadStatus}`);\n }\n\n return password;\n }\n\n private get mpcClient(): MPCClient {\n if (!this.mpcClientInstance) {\n throw new Error(\"MPC client is not initialized\");\n }\n return this.mpcClientInstance;\n }\n\n private async downloadSecret(): Promise<{\n status: string;\n secret: Buffer<ArrayBufferLike> | undefined;\n }> {\n // I really don't like this, I guess the walletAddress should be stored in the store.\n if (!this.options.walletAddress) {\n throw new Error(\"walletAddress is not found\");\n }\n\n const ephemeralKeyPair = nacl.box.keyPair();\n\n const downloadRequest = this.mpcClient.downloadRequest(ephemeralKeyPair.publicKey);\n\n const messageToSign = this.mpcClient.downloadMessageToSign(\n downloadRequest,\n ) as DownloadMessageToSign;\n\n const signedMessage = await this.signTypedData(\n messageToSign.domain,\n messageToSign.types,\n messageToSign.value,\n );\n\n return this.mpcClient.downloadSecret(\n this.userId,\n downloadRequest,\n signedMessage,\n ephemeralKeyPair.secretKey,\n );\n }\n\n private async uploadSecret(secret: string): Promise<{ status: string }> {\n if (!this.options.walletAddress) {\n console.error(\"signerAddress is not found\");\n return { status: \"no-signer-address\" };\n }\n\n const blindedShares = this.mpcClient.getBlindedShares(Buffer.from(secret, \"utf8\"));\n const uploadRequest = this.mpcClient.uploadRequest(blindedShares);\n const messageToSign = this.mpcClient.uploadMessageToSign(uploadRequest) as UploadMessageToSign;\n\n const signedMessage = await this.signTypedData(\n // oxlint-disable-next-line typescript/no-explicit-any -- TODO: Change this when we know how to MPC & other chains\n messageToSign.domain as any,\n // oxlint-disable-next-line typescript/no-explicit-any -- TODO: Change this when we know how to MPC & other chains\n messageToSign.types as any,\n // oxlint-disable-next-line typescript/no-explicit-any -- TODO: Change this when we know how to MPC & other chains\n messageToSign.value as any,\n );\n\n return this.mpcClient.uploadSecret(this.userId, uploadRequest, signedMessage, blindedShares);\n }\n\n async addAddressMessageToSign(\n address: string,\n publicKey: string | undefined,\n addressToAddType: string,\n ): Promise<AddAddressMessageToSign> {\n return this.mpcClient.addAddressMessageToSign(\n address,\n publicKey,\n addressToAddType,\n ) as AddAddressMessageToSign;\n }\n\n async removeAddressMessageToSign(\n address: string,\n publicKey: string | undefined,\n addressToRemoveType: string,\n ): Promise<RemoveAddressMessageToSign> {\n return this.mpcClient.removeAddressMessageToSign(\n address,\n publicKey,\n addressToRemoveType,\n ) as RemoveAddressMessageToSign;\n }\n\n async addAddressToMpcSecret(\n userId: string,\n message: AddAddressSignatureMessage,\n signature: string,\n ): Promise<string> {\n return this.mpcClient.addAddress(userId, message, signature);\n }\n\n async removeAddressFromMpcSecret(\n userId: string,\n message: RemoveAddressSignatureMessage,\n signature: string,\n ): Promise<string> {\n return this.mpcClient.removeAddress(userId, message, signature);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"local.mjs","names":["MPCClient"],"sources":["../src/local.ts"],"sourcesContent":["import type { EncryptionPasswordStore } from \"@idos-network/kwil-infra/actions\";\n\nimport {\n base64Codec,\n base64Encode,\n hexDecode,\n hexEncode,\n utf8Decode,\n utf8Encode,\n} from \"@idos-network/utils/codecs\";\nimport { generatePassword } from \"@idos-network/utils/cryptography\";\nimport { decrypt, encrypt, keyDerivation } from \"@idos-network/utils/encryption\";\nimport { LocalStorageStore, type PipeCodecArgs, type Store } from \"@idos-network/utils/store\";\nimport { syncScrypt } from \"scrypt-js\";\nimport nacl from \"tweetnacl\";\n\nimport type {\n AddAddressMessageToSign,\n AddAddressSignatureMessage,\n DownloadMessageToSign,\n RemoveAddressMessageToSign,\n RemoveAddressSignatureMessage,\n UploadMessageToSign,\n} from \"./mpc/types\";\nimport type {\n EnclaveOptions,\n MPCPasswordContext,\n PasswordContext,\n PrivateEncryptionProfile,\n PublicEncryptionProfile,\n} from \"./types\";\n\nimport { BaseProvider } from \"./base\";\nimport { STORAGE_KEYS } from \"./keys\";\nimport { Client as MPCClient } from \"./mpc/client\";\n\nexport interface LocalEnclaveOptions extends EnclaveOptions {\n store?: Store;\n allowedEncryptionStores?: EncryptionPasswordStore[];\n mpcConfiguration?: {\n nodeUrl: string;\n contractAddress: string;\n numMalicious: number;\n numNodes: number;\n numToReconstruct: number;\n };\n}\n\nexport class LocalEnclave<\n K extends LocalEnclaveOptions = LocalEnclaveOptions,\n> extends BaseProvider<K> {\n protected allowedEncryptionStores: EncryptionPasswordStore[];\n\n // Store for data\n protected store: Store;\n protected storeBase64: Store;\n protected storeObfuscated: Store;\n protected storeObfuscatedBase64: Store;\n\n // In case of MPC usage\n protected mpcClientInstance?: MPCClient;\n\n // Stored key pair and user id for that key pair\n // those are most likely loaded from the store\n // during the load() method.\n protected storedEncryptionProfile?: PrivateEncryptionProfile;\n\n constructor(options: K) {\n super(options);\n\n // By default, we only allow password auth method\n this.allowedEncryptionStores = options.allowedEncryptionStores ?? [\"user\"];\n this.store = options.store ?? new LocalStorageStore();\n this.storeObfuscated = this.store.pipeCodec(this.userIdObfuscationCodec());\n this.storeBase64 = this.store.pipeCodec(base64Codec);\n this.storeObfuscatedBase64 = this.store\n .pipeCodec(this.userIdObfuscationCodec())\n .pipeCodec(base64Codec);\n\n if (options.mpcConfiguration) {\n this.mpcClientInstance = new MPCClient(\n options.mpcConfiguration.nodeUrl,\n options.mpcConfiguration.contractAddress,\n options.mpcConfiguration.numMalicious,\n options.mpcConfiguration.numNodes,\n options.mpcConfiguration.numToReconstruct,\n // TODO: This is weird, we should not be forcing this\n options.walletType ?? \"EVM\",\n options.walletAddress ?? \"\",\n options.walletPublicKey,\n );\n }\n }\n\n /** @override parent method to reset the enclave */\n async reset(): Promise<void> {\n await super.reset();\n this.storedEncryptionProfile = undefined;\n await this.store.reset();\n }\n\n /** @override parent method to reconfigure the enclave */\n async reconfigure(options: Partial<K> = {}): Promise<void> {\n await super.reconfigure(options);\n // Reconfigure MPC client if any signer information changed\n if (this.mpcClientInstance && options.walletType && options.walletAddress) {\n this.mpcClientInstance.reconfigure(\n options.walletType,\n options.walletAddress ?? \"\",\n options.walletPublicKey,\n );\n }\n }\n\n /**\n * Return a codec that encrypts/decrypts data using a key derived from the provider's user ID.\n *\n * This ensures that data is minimally obfuscated to avoid low-sophistication attacks.\n */\n private userIdObfuscationCodec(): PipeCodecArgs<string> {\n // oxlint-disable-next-line typescript/no-this-alias -- Needed for `this` in nested encode/decode functions\n const self = this;\n\n const toKey = (userId: string, salt: Uint8Array) =>\n syncScrypt(utf8Encode(userId), salt, 16384, 8, 1, nacl.secretbox.keyLength);\n\n return {\n encode(data: string): string {\n if (!self.options.userId) {\n throw new Error(\"User ID required for encryption\");\n }\n\n const dataBytes = utf8Encode(data); // Convert string to Uint8Array\n const nonce = nacl.randomBytes(nacl.secretbox.nonceLength);\n const salt = nacl.randomBytes(16);\n const key = toKey(self.options.userId, salt);\n const payload = nacl.secretbox(dataBytes, nonce, key);\n\n // Store salt + nonce + ciphertext\n const combined = new Uint8Array(salt.length + nonce.length + payload.length);\n combined.set(salt, 0);\n combined.set(nonce, salt.length);\n combined.set(payload, salt.length + nonce.length);\n\n return `0x${hexEncode(combined)}`;\n },\n\n decode(payload: string): string {\n if (!self.options.userId) {\n throw new Error(\"User ID required for decryption\");\n }\n\n if (payload.slice(0, 2) !== \"0x\") {\n throw new Error(`missing 0x prefix: ${payload}`);\n }\n\n const combined = hexDecode(payload.slice(2));\n const salt = combined.slice(0, 16);\n const nonce = combined.slice(16, 16 + nacl.secretbox.nonceLength);\n const ciphertext = combined.slice(16 + nacl.secretbox.nonceLength);\n\n const key = toKey(self.options.userId, salt);\n const result = nacl.secretbox.open(ciphertext, nonce, key);\n\n if (!result) throw new Error(\"Failed to decrypt data\");\n\n return utf8Decode(result); // Convert Uint8Array back to string\n },\n };\n }\n /** @see parent method extended with loading the profile from the store */\n async load(): Promise<void> {\n await super.load();\n\n const userId = await this.store.get<string>(STORAGE_KEYS.USER_ID);\n if (userId) this.options.userId = userId;\n\n let password = await this.storeObfuscated.get<string>(STORAGE_KEYS.OBFUSCATED_PASSWORD);\n if (!password) {\n password = await this.store.get<string>(STORAGE_KEYS.DEPRECATED___PASSWORD);\n if (!password) return;\n\n await this.storeObfuscated.set(STORAGE_KEYS.OBFUSCATED_PASSWORD, password);\n }\n await this.store.delete(STORAGE_KEYS.DEPRECATED___PASSWORD);\n\n let encryptionSecretKey = await this.storeObfuscatedBase64.get<Uint8Array>(\n STORAGE_KEYS.OBFUSCATED_BASE64_ENCRYPTION_SECRET_KEY,\n );\n if (!encryptionSecretKey) {\n encryptionSecretKey = await this.storeBase64.get<Uint8Array>(\n STORAGE_KEYS.DEPRECATED___ENCRYPTION_SECRET_KEY,\n );\n if (!encryptionSecretKey) return;\n\n await this.storeObfuscatedBase64.set(\n STORAGE_KEYS.OBFUSCATED_BASE64_ENCRYPTION_SECRET_KEY,\n encryptionSecretKey,\n );\n }\n await this.store.delete(STORAGE_KEYS.DEPRECATED___ENCRYPTION_SECRET_KEY);\n // Even older version.\n await this.store.delete(STORAGE_KEYS.DEPRECATED___ENCRYPTION_PRIVATE_KEY);\n\n if (!password || !userId || !encryptionSecretKey) {\n return;\n }\n\n let encryptionPasswordStore = await this.store.get<EncryptionPasswordStore>(\n STORAGE_KEYS.ENCRYPTION_PASSWORD_STORE,\n );\n\n // Migration from \"password\" to \"user\"\n // TODO: Remove this after a while\n if (!encryptionPasswordStore || (encryptionPasswordStore as string) === \"password\") {\n encryptionPasswordStore = \"user\";\n await this.store.set<EncryptionPasswordStore>(\n STORAGE_KEYS.ENCRYPTION_PASSWORD_STORE,\n encryptionPasswordStore,\n );\n }\n\n this.storedEncryptionProfile = {\n userId,\n password,\n keyPair: nacl.box.keyPair.fromSecretKey(encryptionSecretKey),\n encryptionPasswordStore,\n };\n }\n\n /**\n * Encrypts a message to a receiver.\n * This method also checks if the user is authorized to use the keys.\n *\n * @param message - The message to encrypt.\n * @param receiverPublicKey - The public key of the receiver.\n *\n * @returns The encrypted message.\n */\n async encrypt(\n message: Uint8Array,\n receiverPublicKey: Uint8Array,\n ): Promise<{ content: Uint8Array; encryptorPublicKey: Uint8Array }> {\n const { keyPair } = await this.getPrivateEncryptionProfile();\n return encrypt(message, keyPair.publicKey, receiverPublicKey);\n }\n\n /**\n * Decrypts a message from a sender.\n * This method also checks if the user is authorized to use the keys.\n *\n * @param message - The message to decrypt.\n * @param senderPublicKey - The public key of the sender.\n *\n * @returns The decrypted message.\n */\n async decrypt(\n message: Uint8Array,\n senderPublicKey: Uint8Array,\n ): Promise<Uint8Array<ArrayBufferLike>> {\n const { keyPair } = await this.getPrivateEncryptionProfile();\n return decrypt(message, keyPair, senderPublicKey);\n }\n\n /**\n * @see BaseProvider#getPrivateEncryptionProfile\n */\n async getPrivateEncryptionProfile(skipGuard = false): Promise<PrivateEncryptionProfile> {\n // Remember duration is a key-use check. A long-lived iframe keeps this profile\n // in memory, so construction-time store expiry is not enough.\n if (this.storedEncryptionProfile && (await this.store.hasRememberDurationElapsed())) {\n await this.reset();\n }\n\n // A cached key is only reusable while it still matches the canonical public key;\n // after a rotation it falls through to the reset below and gets wiped.\n const expectedPublicKey = this.options.expectedUserEncryptionPublicKey;\n if (\n this.storedEncryptionProfile?.userId === this.userId &&\n (!expectedPublicKey ||\n base64Encode(this.storedEncryptionProfile.keyPair.publicKey) === expectedPublicKey)\n ) {\n // A matching cached profile still needs origin authorization before key use.\n // Denying it must keep the profile and the allowlist; only a different user resets.\n if (!skipGuard && !(await this.guardKeys())) {\n throw new Error(\"Origin is not authorized to use the keys\");\n }\n\n return this.storedEncryptionProfile;\n }\n\n if (this.storedEncryptionProfile) {\n await this.reset();\n }\n\n if (this.options.encryptionPasswordStore === \"mm\") {\n throw new Error(\n \"MM encryption profiles require an externally supplied encryption private key\",\n );\n }\n\n let password: string | undefined;\n let encryptionPasswordStore: EncryptionPasswordStore | undefined;\n\n const context = await this.getPasswordContext();\n\n if (context.encryptionPasswordStore === \"user\") {\n await this.store.setRememberDuration(context.duration);\n\n password = context.password;\n encryptionPasswordStore = context.encryptionPasswordStore;\n }\n\n if (context.encryptionPasswordStore === \"mpc\") {\n // TODO: This is resetting duration, but should?\n await this.store.setRememberDuration(undefined);\n\n password = await this.ensureMPCPassword();\n encryptionPasswordStore = \"mpc\";\n }\n\n if (!password || !encryptionPasswordStore) {\n throw new Error(\"Password or encryption password store is not found\");\n }\n\n const profile = await this.createEncryptionProfileFromPassword(\n password,\n this.userId,\n encryptionPasswordStore,\n );\n\n // Same origin check as the cached path. Password or MPC success is not authorization.\n if (!skipGuard && !(await this.guardKeys())) {\n throw new Error(\"Origin is not authorized to use the keys\");\n }\n\n return profile;\n }\n\n /** @see BaseProvider#ensureUserEncryptionProfile */\n async ensureUserEncryptionProfile(): Promise<PublicEncryptionProfile> {\n const { keyPair, encryptionPasswordStore, userId } = await this.getPrivateEncryptionProfile();\n\n return {\n userId,\n userEncryptionPublicKey: base64Encode(keyPair.publicKey),\n encryptionPasswordStore: encryptionPasswordStore,\n };\n }\n\n /**\n * This method needs to check `options` and should derive the password context from it.\n *\n * @returns The password context.\n */\n async getPasswordContext(): Promise<PasswordContext | MPCPasswordContext> {\n throw new Error(\"Method 'getPasswordContext' has to be implemented in the subclass.\");\n }\n\n /**\n * Creates and store encryption profile from a password.\n *\n * @param password - The password to use.\n * @param userId - The user id to use.\n * @param encryptionPasswordStore - The encryption password store to use.\n *\n * @returns The encryption profile.\n */\n async createEncryptionProfileFromPassword(\n password: string,\n userId: string,\n encryptionPasswordStore: EncryptionPasswordStore,\n ): Promise<PrivateEncryptionProfile> {\n const secretKey = await keyDerivation(password, userId);\n\n const keyPair = nacl.box.keyPair.fromSecretKey(secretKey);\n\n const expectedPublicKey = this.options.expectedUserEncryptionPublicKey;\n if (expectedPublicKey && base64Encode(keyPair.publicKey) !== expectedPublicKey) {\n throw new Error(\n \"Derived encryption public key does not match expectedUserEncryptionPublicKey\",\n );\n }\n\n await this.store.set(STORAGE_KEYS.USER_ID, userId);\n await this.storeObfuscated.set(STORAGE_KEYS.OBFUSCATED_PASSWORD, password);\n await this.store.delete(STORAGE_KEYS.DEPRECATED___PASSWORD);\n await this.store.set(STORAGE_KEYS.ENCRYPTION_PASSWORD_STORE, encryptionPasswordStore);\n await this.storeObfuscatedBase64.set(\n STORAGE_KEYS.OBFUSCATED_BASE64_ENCRYPTION_SECRET_KEY,\n keyPair.secretKey,\n );\n await this.store.delete(STORAGE_KEYS.DEPRECATED___ENCRYPTION_SECRET_KEY);\n await this.storeBase64.set(STORAGE_KEYS.ENCRYPTION_PUBLIC_KEY, keyPair.publicKey);\n\n this.storedEncryptionProfile = {\n userId,\n password,\n keyPair,\n encryptionPasswordStore,\n };\n\n return this.storedEncryptionProfile;\n }\n\n protected async ensureMPCPassword(): Promise<string> {\n if (this.options?.mode !== \"new\") {\n const { status: downloadStatus, secret: downloadedPassword } = await this.downloadSecret();\n\n if (downloadStatus === \"ok\" && downloadedPassword) {\n return utf8Decode(downloadedPassword);\n }\n\n throw Error(\"A secret might be stored at MPC ZK nodes, but can't be obtained\");\n }\n\n const password = generatePassword();\n const { status: uploadStatus } = await this.uploadSecret(password);\n\n if (uploadStatus !== \"success\") {\n throw Error(`A secret upload failed with status: ${uploadStatus}`);\n }\n\n return password;\n }\n\n private get mpcClient(): MPCClient {\n if (!this.mpcClientInstance) {\n throw new Error(\"MPC client is not initialized\");\n }\n return this.mpcClientInstance;\n }\n\n private async downloadSecret(): Promise<{\n status: string;\n secret: Buffer<ArrayBufferLike> | undefined;\n }> {\n // I really don't like this, I guess the walletAddress should be stored in the store.\n if (!this.options.walletAddress) {\n throw new Error(\"walletAddress is not found\");\n }\n\n const ephemeralKeyPair = nacl.box.keyPair();\n\n const downloadRequest = this.mpcClient.downloadRequest(ephemeralKeyPair.publicKey);\n\n const messageToSign = this.mpcClient.downloadMessageToSign(\n downloadRequest,\n ) as DownloadMessageToSign;\n\n const signedMessage = await this.signTypedData(\n messageToSign.domain,\n messageToSign.types,\n messageToSign.value,\n );\n\n return this.mpcClient.downloadSecret(\n this.userId,\n downloadRequest,\n signedMessage,\n ephemeralKeyPair.secretKey,\n );\n }\n\n private async uploadSecret(secret: string): Promise<{ status: string }> {\n if (!this.options.walletAddress) {\n console.error(\"signerAddress is not found\");\n return { status: \"no-signer-address\" };\n }\n\n const blindedShares = this.mpcClient.getBlindedShares(Buffer.from(secret, \"utf8\"));\n const uploadRequest = this.mpcClient.uploadRequest(blindedShares);\n const messageToSign = this.mpcClient.uploadMessageToSign(uploadRequest) as UploadMessageToSign;\n\n const signedMessage = await this.signTypedData(\n // oxlint-disable-next-line typescript/no-explicit-any -- TODO: Change this when we know how to MPC & other chains\n messageToSign.domain as any,\n // oxlint-disable-next-line typescript/no-explicit-any -- TODO: Change this when we know how to MPC & other chains\n messageToSign.types as any,\n // oxlint-disable-next-line typescript/no-explicit-any -- TODO: Change this when we know how to MPC & other chains\n messageToSign.value as any,\n );\n\n return this.mpcClient.uploadSecret(this.userId, uploadRequest, signedMessage, blindedShares);\n }\n\n async addAddressMessageToSign(\n address: string,\n publicKey: string | undefined,\n addressToAddType: string,\n ): Promise<AddAddressMessageToSign> {\n return this.mpcClient.addAddressMessageToSign(\n address,\n publicKey,\n addressToAddType,\n ) as AddAddressMessageToSign;\n }\n\n async removeAddressMessageToSign(\n address: string,\n publicKey: string | undefined,\n addressToRemoveType: string,\n ): Promise<RemoveAddressMessageToSign> {\n return this.mpcClient.removeAddressMessageToSign(\n address,\n publicKey,\n addressToRemoveType,\n ) as RemoveAddressMessageToSign;\n }\n\n async addAddressToMpcSecret(\n userId: string,\n message: AddAddressSignatureMessage,\n signature: string,\n ): Promise<string> {\n return this.mpcClient.addAddress(userId, message, signature);\n }\n\n async removeAddressFromMpcSecret(\n userId: string,\n message: RemoveAddressSignatureMessage,\n signature: string,\n ): Promise<string> {\n return this.mpcClient.removeAddress(userId, message, signature);\n }\n}\n"],"mappings":"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"}
|
package/dist/mpc/index.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export { ADD_ADDRESS_TYPES, Client, DOWNLOAD_TYPES, REMOVE_ADDRESS_TYPES, UPDATE_TYPES, UPLOAD_TYPES };
|
|
1
|
+
import{a as e,i as t,n,o as r,r as i,t as a}from"../client-Dqb0xbdb.mjs";export{n as ADD_ADDRESS_TYPES,a as Client,i as DOWNLOAD_TYPES,t as REMOVE_ADDRESS_TYPES,e as UPDATE_TYPES,r as UPLOAD_TYPES};
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idos-network/enclave",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"description": "idOS Enclave JavaScript SDK",
|
|
5
5
|
"homepage": "https://idos.network",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "github
|
|
9
|
+
"url": "git+https://github.com/idos-network/idos-sdk-js.git",
|
|
10
|
+
"directory": "packages/enclave"
|
|
10
11
|
},
|
|
11
12
|
"files": [
|
|
12
13
|
"dist/**/*",
|
|
@@ -34,22 +35,22 @@
|
|
|
34
35
|
"access": "public"
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
38
|
+
"@idos-network/credentials": "2.2.2",
|
|
39
|
+
"@idos-network/kwil-infra": "2.2.2",
|
|
40
|
+
"@idos-network/utils": "2.2.2",
|
|
37
41
|
"scrypt-js": "^3.0.1",
|
|
38
42
|
"tiny-invariant": "^1.3.3",
|
|
39
43
|
"tweetnacl": "^1.0.3",
|
|
40
|
-
"zod": "4.4
|
|
41
|
-
"@idos-network/credentials": "1.5.0",
|
|
42
|
-
"@idos-network/kwil-infra": "1.5.0",
|
|
43
|
-
"@idos-network/utils": "1.5.0"
|
|
44
|
+
"zod": "4.5.4"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@types/chrome": "^0.2.5",
|
|
47
48
|
"@types/node": "25.9.5",
|
|
48
|
-
"@vitest/coverage-v8": "4.1.
|
|
49
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
49
50
|
"jsdom": "^22.1.0",
|
|
50
|
-
"tsdown": "^0.
|
|
51
|
+
"tsdown": "^0.23.0",
|
|
51
52
|
"typescript": "^5.9.3",
|
|
52
|
-
"vitest": "4.1.
|
|
53
|
+
"vitest": "4.1.11"
|
|
53
54
|
},
|
|
54
55
|
"peerDependencies": {
|
|
55
56
|
"@partisiablockchain/abi-client": "^5.310.0",
|
package/src/base.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { idOSCredential } from "@idos-network/credentials/types";
|
|
1
|
+
import type { idOSCredentialRecord as idOSCredential } from "@idos-network/credentials/types";
|
|
2
2
|
|
|
3
3
|
import { base64Encode, utf8Encode } from "@idos-network/utils/codecs";
|
|
4
4
|
import { describe, expect, it, vi } from "vitest";
|
package/src/base.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { idOSCredential } from "@idos-network/credentials/types";
|
|
1
|
+
import type { idOSCredentialRecord as idOSCredential } from "@idos-network/credentials/types";
|
|
2
2
|
|
|
3
3
|
import { recordFilter } from "@idos-network/credentials/utils";
|
|
4
4
|
import { base64Decode, fromBytesToJson, hexEncode } from "@idos-network/utils/codecs";
|
|
@@ -238,6 +238,10 @@ export abstract class BaseProvider<K extends EnclaveOptions = EnclaveOptions> {
|
|
|
238
238
|
|
|
239
239
|
const decrypted = await Promise.all(
|
|
240
240
|
credentials.map(async (credential: idOSCredential) => {
|
|
241
|
+
if (!credential.content) {
|
|
242
|
+
throw new Error(`Credential ${credential.id} has no inline content`);
|
|
243
|
+
}
|
|
244
|
+
|
|
241
245
|
const content = await this.decrypt(
|
|
242
246
|
base64Decode(credential.content),
|
|
243
247
|
base64Decode(credential.encryptor_public_key),
|