@dynamic-labs-wallet/browser 0.0.156 → 0.0.158
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/index.cjs.js +332 -240
- package/index.esm.js +334 -238
- package/internal/core/index.js +1 -1
- package/package.json +6 -2
- package/src/backup/encryption/argon2.d.ts +10 -0
- package/src/backup/encryption/argon2.d.ts.map +1 -0
- package/src/backup/encryption/config.d.ts +39 -0
- package/src/backup/encryption/config.d.ts.map +1 -0
- package/src/backup/encryption/constants.d.ts +35 -0
- package/src/backup/encryption/constants.d.ts.map +1 -0
- package/src/backup/encryption/core.d.ts +26 -0
- package/src/backup/encryption/core.d.ts.map +1 -0
- package/src/backup/encryption/pbkdf2.d.ts +10 -0
- package/src/backup/encryption/pbkdf2.d.ts.map +1 -0
- package/src/backup/encryption/types.d.ts +46 -0
- package/src/backup/encryption/types.d.ts.map +1 -0
- package/src/backup/encryption/utils.d.ts +9 -0
- package/src/backup/encryption/utils.d.ts.map +1 -0
- package/src/client.d.ts +1 -1
- package/src/client.d.ts.map +1 -1
- package/src/utils.d.ts +2 -6
- package/src/utils.d.ts.map +1 -1
- package/src/backup/encryption.d.ts +0 -62
- package/src/backup/encryption.d.ts.map +0 -1
package/index.esm.js
CHANGED
|
@@ -2,13 +2,14 @@ import { SigningAlgorithm, MPC_RELAY_PROD_API_URL, getMPCChainConfig, AuthMode,
|
|
|
2
2
|
export * from '@dynamic-labs-wallet/core';
|
|
3
3
|
import { BIP340, ExportableEd25519, Ecdsa, MessageHash, EcdsaKeygenResult, ExportableEd25519KeygenResult, BIP340KeygenResult } from './internal/web';
|
|
4
4
|
export { BIP340, BIP340InitKeygenResult, BIP340KeygenResult, Ecdsa, EcdsaInitKeygenResult, EcdsaKeygenResult, EcdsaPublicKey, EcdsaSignature, Ed25519, Ed25519InitKeygenResult, Ed25519KeygenResult, MessageHash } from './internal/web';
|
|
5
|
-
import { v4 } from 'uuid';
|
|
6
5
|
import { gte } from 'semver';
|
|
7
|
-
import {
|
|
6
|
+
import { v4 } from 'uuid';
|
|
7
|
+
import { Logger, LogLevel } from '@dynamic-labs/logger';
|
|
8
8
|
export { Logger } from '@dynamic-labs/logger';
|
|
9
|
-
import
|
|
9
|
+
import loadArgon2idWasm from 'argon2id';
|
|
10
10
|
import { AxiosError } from 'axios';
|
|
11
11
|
import createHttpError from 'http-errors';
|
|
12
|
+
import { ProviderEnum } from '@dynamic-labs/sdk-api-core';
|
|
12
13
|
|
|
13
14
|
function _extends() {
|
|
14
15
|
_extends = Object.assign || function assign(target) {
|
|
@@ -42,65 +43,118 @@ const getMPCSigner = ({ chainName, baseRelayUrl })=>{
|
|
|
42
43
|
return signatureScheme;
|
|
43
44
|
};
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Encryption version identifiers
|
|
48
|
+
*/ var EncryptionVersion = /*#__PURE__*/ function(EncryptionVersion) {
|
|
49
|
+
EncryptionVersion["V1_LEGACY"] = "v1";
|
|
50
|
+
EncryptionVersion["V2_PBKDF2"] = "v2";
|
|
51
|
+
EncryptionVersion["V3_ARGON2"] = "v3";
|
|
52
|
+
return EncryptionVersion;
|
|
53
|
+
}({});
|
|
54
|
+
/**
|
|
55
|
+
* Current default version for new encryptions
|
|
56
|
+
*/ const ENCRYPTION_VERSION_CURRENT = "v3";
|
|
57
|
+
/**
|
|
58
|
+
* Algorithm constants
|
|
59
|
+
*/ const PBKDF2_ALGORITHM = 'PBKDF2';
|
|
60
|
+
const HASH_ALGORITHM = 'SHA-256'; // Generic hash algorithm constant
|
|
61
|
+
const ARGON2_ALGORITHM = 'Argon2id';
|
|
62
|
+
const AES_GCM_ALGORITHM = 'AES-GCM';
|
|
63
|
+
const AES_GCM_LENGTH = 256;
|
|
64
|
+
/**
|
|
65
|
+
* Argon2 configuration constants, values were chosen based on RFC (https://www.rfc-editor.org/rfc/rfc9106.html#name-parameter-choice)
|
|
66
|
+
* taking into account that this runs in the client, possibly in smartphones with limited resources
|
|
67
|
+
*/ const ARGON2_MEMORY_SIZE = 65536; // 64 MB in KiB
|
|
68
|
+
const ARGON2_ITERATIONS = 3;
|
|
69
|
+
const ARGON2_PARALLELISM = 2;
|
|
70
|
+
const ARGON2_HASH_LENGTH = 32;
|
|
71
|
+
/**
|
|
72
|
+
* PBKDF2 configuration constants
|
|
73
|
+
*/ const PBKDF2_ITERATIONS_V1 = 100000;
|
|
74
|
+
const PBKDF2_ITERATIONS_V2 = 1000000;
|
|
54
75
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Derives a key using Argon2id algorithm
|
|
78
|
+
* @param params - Key derivation parameters
|
|
79
|
+
* @param encryptionConfig - Encryption configuration
|
|
80
|
+
* @returns Promise<CryptoKey>
|
|
81
|
+
*/ const deriveArgon2Key = async ({ password, salt }, encryptionConfig)=>{
|
|
82
|
+
const argon2id = await loadArgon2idWasm();
|
|
83
|
+
const argon2Config = encryptionConfig;
|
|
84
|
+
const passwordBytes = new TextEncoder().encode(password);
|
|
85
|
+
const hash = argon2id({
|
|
86
|
+
password: passwordBytes,
|
|
87
|
+
salt: salt,
|
|
88
|
+
parallelism: argon2Config.parallelism || ARGON2_PARALLELISM,
|
|
89
|
+
passes: argon2Config.iterations,
|
|
90
|
+
memorySize: argon2Config.memorySize || ARGON2_MEMORY_SIZE,
|
|
91
|
+
tagLength: argon2Config.hashLength || ARGON2_HASH_LENGTH
|
|
61
92
|
});
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
case 422:
|
|
70
|
-
throw createHttpError(422, 'Unprocessable content');
|
|
71
|
-
case 500:
|
|
72
|
-
throw createHttpError(500, 'Internal server error');
|
|
73
|
-
default:
|
|
74
|
-
throw createHttpError(500, 'Internal server error');
|
|
75
|
-
}
|
|
93
|
+
return crypto.subtle.importKey('raw', new Uint8Array(hash), {
|
|
94
|
+
name: encryptionConfig.algorithm,
|
|
95
|
+
length: encryptionConfig.algorithmLength
|
|
96
|
+
}, false, [
|
|
97
|
+
'encrypt',
|
|
98
|
+
'decrypt'
|
|
99
|
+
]);
|
|
76
100
|
};
|
|
77
101
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Encryption configuration for each version
|
|
104
|
+
*/ const ENCRYPTION_VERSIONS = {
|
|
105
|
+
[EncryptionVersion.V1_LEGACY]: {
|
|
106
|
+
version: EncryptionVersion.V1_LEGACY,
|
|
107
|
+
algorithm: AES_GCM_ALGORITHM,
|
|
108
|
+
keyDerivation: PBKDF2_ALGORITHM,
|
|
109
|
+
iterations: PBKDF2_ITERATIONS_V1,
|
|
110
|
+
hashAlgorithm: HASH_ALGORITHM,
|
|
111
|
+
algorithmLength: AES_GCM_LENGTH
|
|
112
|
+
},
|
|
113
|
+
[EncryptionVersion.V2_PBKDF2]: {
|
|
114
|
+
version: EncryptionVersion.V2_PBKDF2,
|
|
115
|
+
algorithm: AES_GCM_ALGORITHM,
|
|
116
|
+
keyDerivation: PBKDF2_ALGORITHM,
|
|
117
|
+
iterations: PBKDF2_ITERATIONS_V2,
|
|
118
|
+
hashAlgorithm: HASH_ALGORITHM,
|
|
119
|
+
algorithmLength: AES_GCM_LENGTH
|
|
120
|
+
},
|
|
121
|
+
[EncryptionVersion.V3_ARGON2]: {
|
|
122
|
+
version: EncryptionVersion.V3_ARGON2,
|
|
123
|
+
algorithm: AES_GCM_ALGORITHM,
|
|
124
|
+
keyDerivation: ARGON2_ALGORITHM,
|
|
125
|
+
iterations: ARGON2_ITERATIONS,
|
|
126
|
+
hashAlgorithm: ARGON2_ALGORITHM,
|
|
127
|
+
algorithmLength: AES_GCM_LENGTH,
|
|
128
|
+
memorySize: ARGON2_MEMORY_SIZE,
|
|
129
|
+
parallelism: ARGON2_PARALLELISM,
|
|
130
|
+
hashLength: ARGON2_HASH_LENGTH
|
|
91
131
|
}
|
|
92
132
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Helper function to get encryption configuration by version string
|
|
135
|
+
* @param version - The version string (e.g., 'v1', 'v2', 'v3')
|
|
136
|
+
* @returns The encryption configuration for the specified version
|
|
137
|
+
*/ const getEncryptionConfig = (version)=>{
|
|
138
|
+
// If no version specified, use legacy for backward compatibility
|
|
139
|
+
if (!version) {
|
|
140
|
+
return ENCRYPTION_VERSIONS[EncryptionVersion.V1_LEGACY];
|
|
141
|
+
}
|
|
142
|
+
const config = ENCRYPTION_VERSIONS[version];
|
|
143
|
+
if (!config) {
|
|
144
|
+
throw new Error(`Unsupported encryption version: ${version}`);
|
|
96
145
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
146
|
+
return config;
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* Check if a configuration uses Argon2
|
|
150
|
+
*/ const isArgon2Config = (config)=>{
|
|
151
|
+
return config.keyDerivation === ARGON2_ALGORITHM;
|
|
101
152
|
};
|
|
102
153
|
|
|
103
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Utility functions for encryption operations
|
|
156
|
+
* These functions are separated to avoid circular dependencies
|
|
157
|
+
*/ const bytesToBase64 = (arr)=>{
|
|
104
158
|
return btoa(Array.from(arr, (b)=>String.fromCharCode(b)).join(''));
|
|
105
159
|
};
|
|
106
160
|
const stringToBytes = (str)=>{
|
|
@@ -113,168 +167,13 @@ const base64ToBytes = (base64)=>{
|
|
|
113
167
|
const ensureBase64Padding = (str)=>{
|
|
114
168
|
return str.padEnd(Math.ceil(str.length / 4) * 4, '=');
|
|
115
169
|
};
|
|
116
|
-
const isBrowser = ()=>typeof window !== 'undefined';
|
|
117
|
-
const getClientKeyShareExportFileName = ({ thresholdSignatureScheme, accountAddress })=>{
|
|
118
|
-
return `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${thresholdSignatureScheme}-${accountAddress}.json`;
|
|
119
|
-
};
|
|
120
|
-
const getClientKeyShareBackupInfo = (params)=>{
|
|
121
|
-
var _params_walletProperties, _params_walletProperties_keyShares_;
|
|
122
|
-
const backups = {
|
|
123
|
-
[BackupLocation.DYNAMIC]: [],
|
|
124
|
-
[BackupLocation.GOOGLE_DRIVE]: [],
|
|
125
|
-
[BackupLocation.ICLOUD]: [],
|
|
126
|
-
[BackupLocation.USER]: [],
|
|
127
|
-
[BackupLocation.EXTERNAL]: []
|
|
128
|
-
};
|
|
129
|
-
if (!(params == null ? void 0 : (_params_walletProperties = params.walletProperties) == null ? void 0 : _params_walletProperties.keyShares)) {
|
|
130
|
-
return {
|
|
131
|
-
backups,
|
|
132
|
-
passwordEncrypted: false
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
params.walletProperties.keyShares.forEach((keyShare)=>{
|
|
136
|
-
if (backups[keyShare.backupLocation]) {
|
|
137
|
-
backups[keyShare.backupLocation].push({
|
|
138
|
-
location: keyShare.backupLocation,
|
|
139
|
-
keyShareId: keyShare.id,
|
|
140
|
-
externalKeyShareId: keyShare == null ? void 0 : keyShare.externalKeyShareId
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
const passwordEncrypted = Boolean((_params_walletProperties_keyShares_ = params.walletProperties.keyShares[0]) == null ? void 0 : _params_walletProperties_keyShares_.passwordEncrypted);
|
|
145
|
-
return {
|
|
146
|
-
backups,
|
|
147
|
-
passwordEncrypted
|
|
148
|
-
};
|
|
149
|
-
};
|
|
150
|
-
/**
|
|
151
|
-
* Helper function to merge keyshares and remove duplicates based on pubkey and secretShare
|
|
152
|
-
* @param existingKeyShares - Array of existing keyshares
|
|
153
|
-
* @param newKeyShares - Array of new keyshares to merge
|
|
154
|
-
* @returns Array of merged unique keyshares
|
|
155
|
-
*/ const mergeUniqueKeyShares = (existingKeyShares, newKeyShares)=>{
|
|
156
|
-
const uniqueKeyShares = newKeyShares.filter((newShare)=>!existingKeyShares.some((existingShare)=>{
|
|
157
|
-
if (!(newShare == null ? void 0 : newShare.pubkey) || !(existingShare == null ? void 0 : existingShare.pubkey)) return false;
|
|
158
|
-
return newShare.pubkey.toString() === existingShare.pubkey.toString() && newShare.secretShare === existingShare.secretShare;
|
|
159
|
-
}));
|
|
160
|
-
return [
|
|
161
|
-
...existingKeyShares,
|
|
162
|
-
...uniqueKeyShares
|
|
163
|
-
];
|
|
164
|
-
};
|
|
165
|
-
const timeoutPromise = ({ timeInMs, activity = 'Ceremony' })=>{
|
|
166
|
-
return new Promise((_, reject)=>setTimeout(()=>reject(new Error(`${activity} did not complete in ${timeInMs}ms`)), timeInMs));
|
|
167
|
-
};
|
|
168
|
-
/**
|
|
169
|
-
* Generic helper function to retry a promise-based operations
|
|
170
|
-
*
|
|
171
|
-
* @param operation - The async operation to retry
|
|
172
|
-
* @param config - Configuration options for retry behavior
|
|
173
|
-
* @returns Promise with the operation result
|
|
174
|
-
* @throws Last error encountered after all retries are exhausted
|
|
175
|
-
*/ async function retryPromise(operation, { maxAttempts = 5, retryInterval = 500, operationName = 'operation', logContext = {} } = {}) {
|
|
176
|
-
let attempts = 0;
|
|
177
|
-
while(attempts < maxAttempts){
|
|
178
|
-
try {
|
|
179
|
-
return await operation();
|
|
180
|
-
} catch (error) {
|
|
181
|
-
attempts++;
|
|
182
|
-
if (attempts === maxAttempts) {
|
|
183
|
-
logger.error(`Failed to execute ${operationName} after ${maxAttempts} attempts`, _extends({}, logContext, {
|
|
184
|
-
error: error instanceof Error ? error.message : 'Unknown error'
|
|
185
|
-
}));
|
|
186
|
-
throw error;
|
|
187
|
-
}
|
|
188
|
-
// Calculate exponential backoff delay
|
|
189
|
-
const exponentialDelay = retryInterval * 2 ** (attempts - 1);
|
|
190
|
-
await new Promise((resolve)=>setTimeout(resolve, exponentialDelay));
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
// TypeScript needs this even though it's unreachable
|
|
194
|
-
throw new Error('Unreachable code');
|
|
195
|
-
}
|
|
196
|
-
const formatEvmMessage = (message)=>{
|
|
197
|
-
if (typeof message === 'string' && message.startsWith('0x')) {
|
|
198
|
-
const serializedTxBytes = Uint8Array.from(Buffer.from(message.slice(2), 'hex'));
|
|
199
|
-
return MessageHash.keccak256(serializedTxBytes);
|
|
200
|
-
}
|
|
201
|
-
return MessageHash.keccak256(message);
|
|
202
|
-
};
|
|
203
|
-
const isHexString = (str)=>{
|
|
204
|
-
// Remove 0x prefix if present
|
|
205
|
-
const hex = str.startsWith('0x') ? str.slice(2) : str;
|
|
206
|
-
// Check if string contains only hex characters
|
|
207
|
-
return /^[0-9A-Fa-f]+$/.test(hex);
|
|
208
|
-
};
|
|
209
|
-
const formatSolanaMessage = (message)=>{
|
|
210
|
-
if (typeof message === 'string') {
|
|
211
|
-
if (!isHexString(message)) {
|
|
212
|
-
return Buffer.from(message).toString('hex');
|
|
213
|
-
} else {
|
|
214
|
-
return new Uint8Array(Buffer.from(message, 'hex'));
|
|
215
|
-
}
|
|
216
|
-
} else {
|
|
217
|
-
return message;
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
const formatMessage = (chainName, message)=>{
|
|
221
|
-
switch(chainName){
|
|
222
|
-
case 'EVM':
|
|
223
|
-
return formatEvmMessage(message);
|
|
224
|
-
case 'SVM':
|
|
225
|
-
return formatSolanaMessage(message);
|
|
226
|
-
case 'SUI':
|
|
227
|
-
return message;
|
|
228
|
-
default:
|
|
229
|
-
throw new Error('Unsupported chain name');
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
const getGoogleOAuthAccountId = (verifiedCredentials)=>{
|
|
233
|
-
const googleVerifiedCredential = verifiedCredentials == null ? void 0 : verifiedCredentials.find((credential)=>credential.oauthProvider === ProviderEnum.Google);
|
|
234
|
-
return googleVerifiedCredential == null ? void 0 : googleVerifiedCredential.id;
|
|
235
|
-
};
|
|
236
|
-
const createBackupData = ({ encryptedKeyShares, accountAddress, thresholdSignatureScheme, hasPassword = true })=>{
|
|
237
|
-
return {
|
|
238
|
-
keyShares: encryptedKeyShares,
|
|
239
|
-
metadata: {
|
|
240
|
-
createdAt: new Date().toISOString(),
|
|
241
|
-
accountAddress,
|
|
242
|
-
thresholdSignatureScheme,
|
|
243
|
-
hasPassword,
|
|
244
|
-
encryption: getEncryptionMetadataForVersion(ENCRYPTION_VERSION_CURRENT),
|
|
245
|
-
encryptionVersion: ENCRYPTION_VERSION_CURRENT,
|
|
246
|
-
shareCount: encryptedKeyShares.length
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
};
|
|
250
170
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const
|
|
257
|
-
const ENCRYPTION_VERSIONS = {
|
|
258
|
-
[ENCRYPTION_VERSION_LEGACY]: {
|
|
259
|
-
version: ENCRYPTION_VERSION_LEGACY,
|
|
260
|
-
algorithm: AES_GCM_ALGORITHM,
|
|
261
|
-
keyDerivation: PBKDF2_ALGORITHM,
|
|
262
|
-
iterations: 100000,
|
|
263
|
-
hashAlgorithm: PBKDF2_HASH_ALGORITHM,
|
|
264
|
-
algorithmLength: AES_GCM_LENGTH
|
|
265
|
-
},
|
|
266
|
-
[ENCRYPTION_VERSION_CURRENT]: {
|
|
267
|
-
version: ENCRYPTION_VERSION_CURRENT,
|
|
268
|
-
algorithm: AES_GCM_ALGORITHM,
|
|
269
|
-
keyDerivation: PBKDF2_ALGORITHM,
|
|
270
|
-
iterations: 1000000,
|
|
271
|
-
hashAlgorithm: PBKDF2_HASH_ALGORITHM,
|
|
272
|
-
algorithmLength: AES_GCM_LENGTH
|
|
273
|
-
}
|
|
274
|
-
};
|
|
275
|
-
ENCRYPTION_VERSIONS[ENCRYPTION_VERSION_CURRENT].iterations;
|
|
276
|
-
ENCRYPTION_VERSIONS[ENCRYPTION_VERSION_LEGACY].iterations;
|
|
277
|
-
const getKey = async ({ password, salt, encryptionConfig })=>{
|
|
171
|
+
/**
|
|
172
|
+
* Derives a key using PBKDF2 algorithm
|
|
173
|
+
* @param params - Key derivation parameters
|
|
174
|
+
* @param encryptionConfig - Encryption configuration
|
|
175
|
+
* @returns Promise<CryptoKey>
|
|
176
|
+
*/ const derivePBKDF2Key = async ({ password, salt }, encryptionConfig)=>{
|
|
278
177
|
const passwordBytes = stringToBytes(password);
|
|
279
178
|
const initialKey = await crypto.subtle.importKey('raw', passwordBytes, {
|
|
280
179
|
name: 'PBKDF2'
|
|
@@ -294,23 +193,30 @@ const getKey = async ({ password, salt, encryptionConfig })=>{
|
|
|
294
193
|
'decrypt'
|
|
295
194
|
]);
|
|
296
195
|
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get the appropriate key derivation function based on the encryption config
|
|
199
|
+
*/ const getKey = async (params, encryptionConfig)=>{
|
|
200
|
+
// Use Argon2 for v3, PBKDF2 for v1 and v2
|
|
201
|
+
if (encryptionConfig.keyDerivation === ARGON2_ALGORITHM) {
|
|
202
|
+
return deriveArgon2Key(params, encryptionConfig);
|
|
203
|
+
} else {
|
|
204
|
+
return derivePBKDF2Key(params, encryptionConfig);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
297
207
|
/**
|
|
298
208
|
* Encrypts data using the specified encryption version.
|
|
299
209
|
* Always uses the latest encryption configuration for new encryptions by default.
|
|
300
210
|
*/ const encryptData = async ({ data, password, version = ENCRYPTION_VERSION_CURRENT })=>{
|
|
301
|
-
const encryptionConfig =
|
|
302
|
-
if (!encryptionConfig) {
|
|
303
|
-
throw new Error(`Unsupported encryption version: ${version}`);
|
|
304
|
-
}
|
|
211
|
+
const encryptionConfig = getEncryptionConfig(version);
|
|
305
212
|
try {
|
|
306
213
|
// Generate a random salt and IV
|
|
307
214
|
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
308
215
|
const iv = crypto.getRandomValues(new Uint8Array(12)); // AES-GCM requires 12 bytes
|
|
309
216
|
const key = await getKey({
|
|
310
217
|
password,
|
|
311
|
-
salt
|
|
312
|
-
|
|
313
|
-
});
|
|
218
|
+
salt
|
|
219
|
+
}, encryptionConfig);
|
|
314
220
|
// Convert the input string to bytes
|
|
315
221
|
const dataBytes = new TextEncoder().encode(data);
|
|
316
222
|
// Encrypt the data
|
|
@@ -342,20 +248,13 @@ const getKey = async ({ password, salt, encryptionConfig })=>{
|
|
|
342
248
|
const saltBytes = base64ToBytes(paddedSalt);
|
|
343
249
|
const ivBytes = base64ToBytes(paddedIv);
|
|
344
250
|
const cipherBytes = base64ToBytes(paddedCipher);
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (version && ENCRYPTION_VERSIONS[version]) {
|
|
348
|
-
encryptionConfig = ENCRYPTION_VERSIONS[version];
|
|
349
|
-
} else {
|
|
350
|
-
// Fallback to legacy version for backward compatibility
|
|
351
|
-
encryptionConfig = ENCRYPTION_VERSIONS[ENCRYPTION_VERSION_LEGACY];
|
|
352
|
-
}
|
|
251
|
+
// Determine which encryption configuration to use
|
|
252
|
+
const encryptionConfig = getEncryptionConfig(version);
|
|
353
253
|
try {
|
|
354
254
|
const key = await getKey({
|
|
355
255
|
password,
|
|
356
|
-
salt: saltBytes
|
|
357
|
-
|
|
358
|
-
});
|
|
256
|
+
salt: saltBytes
|
|
257
|
+
}, encryptionConfig);
|
|
359
258
|
const decryptedData = await crypto.subtle.decrypt({
|
|
360
259
|
name: AES_GCM_ALGORITHM,
|
|
361
260
|
iv: ivBytes
|
|
@@ -369,17 +268,21 @@ const getKey = async ({ password, salt, encryptionConfig })=>{
|
|
|
369
268
|
* Gets encryption metadata for a specific version.
|
|
370
269
|
* Used when we need to include metadata in legacy systems or APIs that require it.
|
|
371
270
|
*/ const getEncryptionMetadataForVersion = (version)=>{
|
|
372
|
-
const encryptionConfig =
|
|
373
|
-
|
|
374
|
-
throw new Error(`Unsupported encryption version: ${version}`);
|
|
375
|
-
}
|
|
376
|
-
return {
|
|
271
|
+
const encryptionConfig = getEncryptionConfig(version);
|
|
272
|
+
const metadata = {
|
|
377
273
|
algorithm: encryptionConfig.algorithm,
|
|
378
274
|
keyDerivation: encryptionConfig.keyDerivation,
|
|
379
275
|
iterations: encryptionConfig.iterations,
|
|
380
276
|
hashAlgorithm: encryptionConfig.hashAlgorithm,
|
|
381
277
|
algorithmLength: encryptionConfig.algorithmLength
|
|
382
278
|
};
|
|
279
|
+
// Add Argon2-specific metadata if applicable
|
|
280
|
+
if (isArgon2Config(encryptionConfig)) {
|
|
281
|
+
metadata.memorySize = encryptionConfig.memorySize;
|
|
282
|
+
metadata.parallelism = encryptionConfig.parallelism;
|
|
283
|
+
metadata.hashLength = encryptionConfig.hashLength;
|
|
284
|
+
}
|
|
285
|
+
return metadata;
|
|
383
286
|
};
|
|
384
287
|
|
|
385
288
|
const GOOGLE_DRIVE_UPLOAD_API = 'https://www.googleapis.com';
|
|
@@ -478,6 +381,199 @@ const downloadFileFromGoogleDrive = async ({ accessToken, fileName })=>{
|
|
|
478
381
|
}
|
|
479
382
|
};
|
|
480
383
|
|
|
384
|
+
const handleAxiosError = (error, message, context)=>{
|
|
385
|
+
var _error_response, _error_response1;
|
|
386
|
+
logger.debug("[DynamicWaasWalletClient] Axios error: " + message, {
|
|
387
|
+
error: (_error_response = error.response) == null ? void 0 : _error_response.status,
|
|
388
|
+
message: error.message,
|
|
389
|
+
context
|
|
390
|
+
});
|
|
391
|
+
switch((_error_response1 = error.response) == null ? void 0 : _error_response1.status){
|
|
392
|
+
case 400:
|
|
393
|
+
throw createHttpError(400, 'Invalid request');
|
|
394
|
+
case 401:
|
|
395
|
+
throw createHttpError(401, 'Authorization header or cookie is required');
|
|
396
|
+
case 403:
|
|
397
|
+
throw createHttpError(403, 'Forbidden');
|
|
398
|
+
case 422:
|
|
399
|
+
throw createHttpError(422, 'Unprocessable content');
|
|
400
|
+
case 500:
|
|
401
|
+
throw createHttpError(500, 'Internal server error');
|
|
402
|
+
default:
|
|
403
|
+
throw createHttpError(500, 'Internal server error');
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const logger = new Logger('DynamicWaasWalletClient', LogLevel.DEBUG);
|
|
408
|
+
const setLoggerContext = ({ environmentId, authMode = AuthMode.HEADER, sessionId = undefined, userId = undefined })=>{
|
|
409
|
+
try {
|
|
410
|
+
Logger.setEnvironmentId(environmentId);
|
|
411
|
+
Logger.globalMetaData.set('sid', sessionId);
|
|
412
|
+
Logger.globalMetaData.set('user_id', userId);
|
|
413
|
+
Logger.globalMetaData.set('auth_mode', authMode);
|
|
414
|
+
} catch (error) {
|
|
415
|
+
logError({
|
|
416
|
+
message: '[DynamicWaasWalletClient] Error setting logger context',
|
|
417
|
+
error: error,
|
|
418
|
+
context: {}
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
const logError = ({ message, error, context })=>{
|
|
423
|
+
if (error instanceof AxiosError) {
|
|
424
|
+
handleAxiosError(error, message, context);
|
|
425
|
+
}
|
|
426
|
+
logger.error(message, {
|
|
427
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
428
|
+
context
|
|
429
|
+
});
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const DEFAULT_LOG_LEVEL = LogLevel.DEBUG; //todo: change back to info when done debugging
|
|
433
|
+
const STORAGE_KEY = 'dynamic-waas-wallet-client';
|
|
434
|
+
const CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX = 'dynamicWalletKeyShareBackup';
|
|
435
|
+
const SIGNED_SESSION_ID_MIN_VERSION = '4.25.4';
|
|
436
|
+
// Namespace-specific version requirements
|
|
437
|
+
const SIGNED_SESSION_ID_MIN_VERSION_BY_NAMESPACE = {
|
|
438
|
+
WalletKit: '4.25.4',
|
|
439
|
+
ClientSDK: '0.1.0-alpha.0'
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const isBrowser = ()=>typeof window !== 'undefined';
|
|
443
|
+
const getClientKeyShareExportFileName = ({ thresholdSignatureScheme, accountAddress })=>{
|
|
444
|
+
return `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${thresholdSignatureScheme}-${accountAddress}.json`;
|
|
445
|
+
};
|
|
446
|
+
const getClientKeyShareBackupInfo = (params)=>{
|
|
447
|
+
var _params_walletProperties, _params_walletProperties_keyShares_;
|
|
448
|
+
const backups = {
|
|
449
|
+
[BackupLocation.DYNAMIC]: [],
|
|
450
|
+
[BackupLocation.GOOGLE_DRIVE]: [],
|
|
451
|
+
[BackupLocation.ICLOUD]: [],
|
|
452
|
+
[BackupLocation.USER]: [],
|
|
453
|
+
[BackupLocation.EXTERNAL]: []
|
|
454
|
+
};
|
|
455
|
+
if (!(params == null ? void 0 : (_params_walletProperties = params.walletProperties) == null ? void 0 : _params_walletProperties.keyShares)) {
|
|
456
|
+
return {
|
|
457
|
+
backups,
|
|
458
|
+
passwordEncrypted: false
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
params.walletProperties.keyShares.forEach((keyShare)=>{
|
|
462
|
+
if (backups[keyShare.backupLocation]) {
|
|
463
|
+
backups[keyShare.backupLocation].push({
|
|
464
|
+
location: keyShare.backupLocation,
|
|
465
|
+
keyShareId: keyShare.id,
|
|
466
|
+
externalKeyShareId: keyShare == null ? void 0 : keyShare.externalKeyShareId
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
const passwordEncrypted = Boolean((_params_walletProperties_keyShares_ = params.walletProperties.keyShares[0]) == null ? void 0 : _params_walletProperties_keyShares_.passwordEncrypted);
|
|
471
|
+
return {
|
|
472
|
+
backups,
|
|
473
|
+
passwordEncrypted
|
|
474
|
+
};
|
|
475
|
+
};
|
|
476
|
+
/**
|
|
477
|
+
* Helper function to merge keyshares and remove duplicates based on pubkey and secretShare
|
|
478
|
+
* @param existingKeyShares - Array of existing keyshares
|
|
479
|
+
* @param newKeyShares - Array of new keyshares to merge
|
|
480
|
+
* @returns Array of merged unique keyshares
|
|
481
|
+
*/ const mergeUniqueKeyShares = (existingKeyShares, newKeyShares)=>{
|
|
482
|
+
const uniqueKeyShares = newKeyShares.filter((newShare)=>!existingKeyShares.some((existingShare)=>{
|
|
483
|
+
if (!(newShare == null ? void 0 : newShare.pubkey) || !(existingShare == null ? void 0 : existingShare.pubkey)) return false;
|
|
484
|
+
return newShare.pubkey.toString() === existingShare.pubkey.toString() && newShare.secretShare === existingShare.secretShare;
|
|
485
|
+
}));
|
|
486
|
+
return [
|
|
487
|
+
...existingKeyShares,
|
|
488
|
+
...uniqueKeyShares
|
|
489
|
+
];
|
|
490
|
+
};
|
|
491
|
+
const timeoutPromise = ({ timeInMs, activity = 'Ceremony' })=>{
|
|
492
|
+
return new Promise((_, reject)=>setTimeout(()=>reject(new Error(`${activity} did not complete in ${timeInMs}ms`)), timeInMs));
|
|
493
|
+
};
|
|
494
|
+
/**
|
|
495
|
+
* Generic helper function to retry a promise-based operations
|
|
496
|
+
*
|
|
497
|
+
* @param operation - The async operation to retry
|
|
498
|
+
* @param config - Configuration options for retry behavior
|
|
499
|
+
* @returns Promise with the operation result
|
|
500
|
+
* @throws Last error encountered after all retries are exhausted
|
|
501
|
+
*/ async function retryPromise(operation, { maxAttempts = 5, retryInterval = 500, operationName = 'operation', logContext = {} } = {}) {
|
|
502
|
+
let attempts = 0;
|
|
503
|
+
while(attempts < maxAttempts){
|
|
504
|
+
try {
|
|
505
|
+
return await operation();
|
|
506
|
+
} catch (error) {
|
|
507
|
+
attempts++;
|
|
508
|
+
if (attempts === maxAttempts) {
|
|
509
|
+
logger.error(`Failed to execute ${operationName} after ${maxAttempts} attempts`, _extends({}, logContext, {
|
|
510
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
511
|
+
}));
|
|
512
|
+
throw error;
|
|
513
|
+
}
|
|
514
|
+
// Calculate exponential backoff delay
|
|
515
|
+
const exponentialDelay = retryInterval * 2 ** (attempts - 1);
|
|
516
|
+
await new Promise((resolve)=>setTimeout(resolve, exponentialDelay));
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
// TypeScript needs this even though it's unreachable
|
|
520
|
+
throw new Error('Unreachable code');
|
|
521
|
+
}
|
|
522
|
+
const formatEvmMessage = (message)=>{
|
|
523
|
+
if (typeof message === 'string' && message.startsWith('0x')) {
|
|
524
|
+
const serializedTxBytes = Uint8Array.from(Buffer.from(message.slice(2), 'hex'));
|
|
525
|
+
return MessageHash.keccak256(serializedTxBytes);
|
|
526
|
+
}
|
|
527
|
+
return MessageHash.keccak256(message);
|
|
528
|
+
};
|
|
529
|
+
const isHexString = (str)=>{
|
|
530
|
+
// Remove 0x prefix if present
|
|
531
|
+
const hex = str.startsWith('0x') ? str.slice(2) : str;
|
|
532
|
+
// Check if string contains only hex characters
|
|
533
|
+
return /^[0-9A-Fa-f]+$/.test(hex);
|
|
534
|
+
};
|
|
535
|
+
const formatSolanaMessage = (message)=>{
|
|
536
|
+
if (typeof message === 'string') {
|
|
537
|
+
if (!isHexString(message)) {
|
|
538
|
+
return Buffer.from(message).toString('hex');
|
|
539
|
+
} else {
|
|
540
|
+
return new Uint8Array(Buffer.from(message, 'hex'));
|
|
541
|
+
}
|
|
542
|
+
} else {
|
|
543
|
+
return message;
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
const formatMessage = (chainName, message)=>{
|
|
547
|
+
switch(chainName){
|
|
548
|
+
case 'EVM':
|
|
549
|
+
return formatEvmMessage(message);
|
|
550
|
+
case 'SVM':
|
|
551
|
+
return formatSolanaMessage(message);
|
|
552
|
+
case 'SUI':
|
|
553
|
+
return message;
|
|
554
|
+
default:
|
|
555
|
+
throw new Error('Unsupported chain name');
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
const getGoogleOAuthAccountId = (verifiedCredentials)=>{
|
|
559
|
+
const googleVerifiedCredential = verifiedCredentials == null ? void 0 : verifiedCredentials.find((credential)=>credential.oauthProvider === ProviderEnum.Google);
|
|
560
|
+
return googleVerifiedCredential == null ? void 0 : googleVerifiedCredential.id;
|
|
561
|
+
};
|
|
562
|
+
const createBackupData = ({ encryptedKeyShares, accountAddress, thresholdSignatureScheme, hasPassword = true })=>{
|
|
563
|
+
return {
|
|
564
|
+
keyShares: encryptedKeyShares,
|
|
565
|
+
metadata: {
|
|
566
|
+
createdAt: new Date().toISOString(),
|
|
567
|
+
accountAddress,
|
|
568
|
+
thresholdSignatureScheme,
|
|
569
|
+
hasPassword,
|
|
570
|
+
encryption: getEncryptionMetadataForVersion(ENCRYPTION_VERSION_CURRENT),
|
|
571
|
+
encryptionVersion: ENCRYPTION_VERSION_CURRENT,
|
|
572
|
+
shareCount: encryptedKeyShares.length
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
};
|
|
576
|
+
|
|
481
577
|
/**
|
|
482
578
|
* Uploads a backup to Google Drive App
|
|
483
579
|
* @param accessToken - The access token for the Google Drive API
|
|
@@ -2394,4 +2490,4 @@ const ERROR_VERIFY_TRANSACTION_SIGNATURE = '[DynamicWaasWalletClient]: Error ver
|
|
|
2394
2490
|
const ERROR_EXPORT_PRIVATE_KEY = '[DynamicWaasWalletClient]: Error exporting private key';
|
|
2395
2491
|
const ERROR_IMPORT_PRIVATE_KEY = '[DynamicWaasWalletClient]: Error importing private key';
|
|
2396
2492
|
|
|
2397
|
-
export { DynamicWalletClient, ERROR_ACCOUNT_ADDRESS_REQUIRED, ERROR_CREATE_WALLET_ACCOUNT, ERROR_EXPORT_PRIVATE_KEY, ERROR_IMPORT_PRIVATE_KEY, ERROR_KEYGEN_FAILED, ERROR_SIGN_MESSAGE, ERROR_SIGN_TYPED_DATA, ERROR_VERIFY_MESSAGE_SIGNATURE, ERROR_VERIFY_TRANSACTION_SIGNATURE,
|
|
2493
|
+
export { DynamicWalletClient, ERROR_ACCOUNT_ADDRESS_REQUIRED, ERROR_CREATE_WALLET_ACCOUNT, ERROR_EXPORT_PRIVATE_KEY, ERROR_IMPORT_PRIVATE_KEY, ERROR_KEYGEN_FAILED, ERROR_SIGN_MESSAGE, ERROR_SIGN_TYPED_DATA, ERROR_VERIFY_MESSAGE_SIGNATURE, ERROR_VERIFY_TRANSACTION_SIGNATURE, createBackupData, formatEvmMessage, formatMessage, getClientKeyShareBackupInfo, getClientKeyShareExportFileName, getGoogleOAuthAccountId, getMPCSignatureScheme, getMPCSigner, isBrowser, isHexString, mergeUniqueKeyShares, retryPromise, timeoutPromise };
|
package/internal/core/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.BIP340=exports.Sr25519=exports.ExportableEd25519=exports.Ed25519=exports.Ecdsa=exports.MessageHash=exports.BIP340KeygenResult=exports.BIP340InitKeygenResult=exports.Sr25519KeygenResult=exports.Sr25519InitKeygenResult=exports.ExportableEd25519KeygenResult=exports.ExportableEd25519InitKeygenResult=exports.Ed25519KeygenResult=exports.Ed25519InitKeygenResult=exports.EcdsaSignature=exports.EcdsaPublicKey=exports.EcdsaKeygenResult=exports.EcdsaInitKeygenResult=void 0;let types_1=require("./types"),ecdsa_1=(Object.defineProperty(exports,"EcdsaInitKeygenResult",{
|
|
1
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.BIP340=exports.Sr25519=exports.ExportableEd25519=exports.Ed25519=exports.Ecdsa=exports.MessageHash=exports.BIP340KeygenResult=exports.BIP340InitKeygenResult=exports.Sr25519KeygenResult=exports.Sr25519InitKeygenResult=exports.ExportableEd25519KeygenResult=exports.ExportableEd25519InitKeygenResult=exports.Ed25519KeygenResult=exports.Ed25519InitKeygenResult=exports.EcdsaSignature=exports.EcdsaPublicKey=exports.EcdsaKeygenResult=exports.EcdsaInitKeygenResult=void 0;let types_1=require("./types"),ecdsa_1=(Object.defineProperty(exports,"EcdsaInitKeygenResult",{value:types_1.EcdsaInitKeygenResult}),Object.defineProperty(exports,"EcdsaKeygenResult",{value:types_1.EcdsaKeygenResult}),Object.defineProperty(exports,"EcdsaPublicKey",{value:types_1.EcdsaPublicKey}),Object.defineProperty(exports,"EcdsaSignature",{value:types_1.EcdsaSignature}),Object.defineProperty(exports,"Ed25519InitKeygenResult",{value:types_1.Ed25519InitKeygenResult}),Object.defineProperty(exports,"Ed25519KeygenResult",{value:types_1.Ed25519KeygenResult}),Object.defineProperty(exports,"ExportableEd25519InitKeygenResult",{value:types_1.ExportableEd25519InitKeygenResult}),Object.defineProperty(exports,"ExportableEd25519KeygenResult",{value:types_1.ExportableEd25519KeygenResult}),Object.defineProperty(exports,"Sr25519InitKeygenResult",{value:types_1.Sr25519InitKeygenResult}),Object.defineProperty(exports,"Sr25519KeygenResult",{value:types_1.Sr25519KeygenResult}),Object.defineProperty(exports,"BIP340InitKeygenResult",{value:types_1.BIP340InitKeygenResult}),Object.defineProperty(exports,"BIP340KeygenResult",{value:types_1.BIP340KeygenResult}),Object.defineProperty(exports,"MessageHash",{value:types_1.MessageHash}),require("./ecdsa")),ed25519_1=(Object.defineProperty(exports,"Ecdsa",{value:ecdsa_1.Ecdsa}),require("./ed25519")),ed25519_exportable_1=(Object.defineProperty(exports,"Ed25519",{value:ed25519_1.Ed25519}),require("./ed25519_exportable")),sr25519_1=(Object.defineProperty(exports,"ExportableEd25519",{value:ed25519_exportable_1.ExportableEd25519}),require("./sr25519")),bip340_1=(Object.defineProperty(exports,"Sr25519",{value:sr25519_1.Sr25519}),require("./bip340"));Object.defineProperty(exports,"BIP340",{value:bip340_1.BIP340});
|