@medialane/sdk 0.194.0 → 0.197.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-BLEFV7GP.cjs → chunk-2R7XBA34.cjs} +82 -49
- package/dist/chunk-2R7XBA34.cjs.map +1 -0
- package/dist/{chunk-MOIM2AAQ.js → chunk-6WVUUBFI.js} +81 -50
- package/dist/chunk-6WVUUBFI.js.map +1 -0
- package/dist/{errors-C39AqEX6.d.cts → errors-CZm-NZK0.d.cts} +12 -6
- package/dist/{errors-Cc9W7FFi.d.ts → errors-h6H6cj3Q.d.ts} +12 -6
- package/dist/index.cjs +76 -72
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/starknet/index.cjs +75 -67
- package/dist/starknet/index.d.cts +2 -2
- package/dist/starknet/index.d.ts +2 -2
- package/dist/starknet/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-BLEFV7GP.cjs.map +0 -1
- package/dist/chunk-MOIM2AAQ.js.map +0 -1
|
@@ -1092,6 +1092,9 @@ async function deriveAesKey(prfSecret, hkdfInfo) {
|
|
|
1092
1092
|
["encrypt", "decrypt"]
|
|
1093
1093
|
);
|
|
1094
1094
|
}
|
|
1095
|
+
function generateDeviceKey() {
|
|
1096
|
+
return crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
|
|
1097
|
+
}
|
|
1095
1098
|
function generateStarkKeyPair() {
|
|
1096
1099
|
const privateKeyHex = "0x" + Array.from(starknet.ec.starkCurve.utils.randomPrivateKey()).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1097
1100
|
return { privateKeyHex, publicKeyHex: starknet.ec.starkCurve.getStarkKey(privateKeyHex) };
|
|
@@ -1214,6 +1217,38 @@ function computeAccountAddress(ownerPubkey, salt = 0) {
|
|
|
1214
1217
|
);
|
|
1215
1218
|
}
|
|
1216
1219
|
|
|
1220
|
+
// src/wallet/device-keys.ts
|
|
1221
|
+
var DATABASE = "medialane-wallet";
|
|
1222
|
+
var STORE = "device-keys";
|
|
1223
|
+
function open() {
|
|
1224
|
+
return new Promise((resolve, reject) => {
|
|
1225
|
+
const request = indexedDB.open(DATABASE, 1);
|
|
1226
|
+
request.onupgradeneeded = () => request.result.createObjectStore(STORE);
|
|
1227
|
+
request.onsuccess = () => resolve(request.result);
|
|
1228
|
+
request.onerror = () => reject(request.error);
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
async function run(mode, act) {
|
|
1232
|
+
const db = await open();
|
|
1233
|
+
try {
|
|
1234
|
+
return await new Promise((resolve, reject) => {
|
|
1235
|
+
const request = act(db.transaction(STORE, mode).objectStore(STORE));
|
|
1236
|
+
request.onsuccess = () => resolve(request.result);
|
|
1237
|
+
request.onerror = () => reject(request.error);
|
|
1238
|
+
});
|
|
1239
|
+
} finally {
|
|
1240
|
+
db.close();
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
var indexedDbDeviceKeys = {
|
|
1244
|
+
async save(credentialId, key) {
|
|
1245
|
+
await run("readwrite", (store) => store.put(key, credentialId));
|
|
1246
|
+
},
|
|
1247
|
+
async load(credentialId) {
|
|
1248
|
+
return await run("readonly", (store) => store.get(credentialId)) ?? null;
|
|
1249
|
+
}
|
|
1250
|
+
};
|
|
1251
|
+
|
|
1217
1252
|
// src/wallet/passkey.ts
|
|
1218
1253
|
var PasskeyCancelledError = class extends Error {
|
|
1219
1254
|
constructor(message = "Passkey prompt was cancelled.") {
|
|
@@ -1221,6 +1256,12 @@ var PasskeyCancelledError = class extends Error {
|
|
|
1221
1256
|
this.name = "PasskeyCancelledError";
|
|
1222
1257
|
}
|
|
1223
1258
|
};
|
|
1259
|
+
var DeviceKeyMissingError = class extends Error {
|
|
1260
|
+
constructor() {
|
|
1261
|
+
super("This browser no longer holds the wallet key. Recover the wallet with its recovery key.");
|
|
1262
|
+
this.name = "DeviceKeyMissingError";
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1224
1265
|
var PasskeyUnsupportedError = class extends Error {
|
|
1225
1266
|
constructor(reason) {
|
|
1226
1267
|
super(
|
|
@@ -1253,21 +1294,7 @@ function createPasskeyOwner(config) {
|
|
|
1253
1294
|
if (!api) throw new PasskeyUnsupportedError("no-webauthn");
|
|
1254
1295
|
return api;
|
|
1255
1296
|
};
|
|
1256
|
-
const browserRefusesPrf = async () => {
|
|
1257
|
-
const read = config.clientCapabilities ?? (typeof PublicKeyCredential !== "undefined" && typeof PublicKeyCredential.getClientCapabilities === "function" ? () => PublicKeyCredential.getClientCapabilities() : void 0);
|
|
1258
|
-
if (!read) return false;
|
|
1259
|
-
try {
|
|
1260
|
-
return (await read())["extension:prf"] === false;
|
|
1261
|
-
} catch {
|
|
1262
|
-
return false;
|
|
1263
|
-
}
|
|
1264
|
-
};
|
|
1265
|
-
const ensurePrfPossible = async () => {
|
|
1266
|
-
credentialsApi();
|
|
1267
|
-
if (await browserRefusesPrf()) throw new PasskeyUnsupportedError("no-prf");
|
|
1268
|
-
};
|
|
1269
1297
|
async function registerPasskey() {
|
|
1270
|
-
await ensurePrfPossible();
|
|
1271
1298
|
let credential;
|
|
1272
1299
|
try {
|
|
1273
1300
|
credential = await credentialsApi().create({
|
|
@@ -1281,6 +1308,7 @@ function createPasskeyOwner(config) {
|
|
|
1281
1308
|
{ type: "public-key", alg: -257 }
|
|
1282
1309
|
],
|
|
1283
1310
|
authenticatorSelection: {
|
|
1311
|
+
authenticatorAttachment: "platform",
|
|
1284
1312
|
residentKey: "required",
|
|
1285
1313
|
userVerification: "required"
|
|
1286
1314
|
},
|
|
@@ -1294,53 +1322,55 @@ function createPasskeyOwner(config) {
|
|
|
1294
1322
|
const prf = credential.getClientExtensionResults().prf;
|
|
1295
1323
|
return {
|
|
1296
1324
|
credentialId: encodeBase64(credential.rawId),
|
|
1297
|
-
prfFirst: prf?.results?.first ?? null
|
|
1298
|
-
prfEnabled: prf?.enabled
|
|
1325
|
+
prfFirst: prf?.results?.first ?? null
|
|
1299
1326
|
};
|
|
1300
1327
|
}
|
|
1301
|
-
async function
|
|
1302
|
-
await ensurePrfPossible();
|
|
1303
|
-
let assertion;
|
|
1328
|
+
async function assertPasskey(credentialId, extensions) {
|
|
1304
1329
|
try {
|
|
1305
|
-
|
|
1330
|
+
return await credentialsApi().get({
|
|
1306
1331
|
publicKey: {
|
|
1307
1332
|
challenge: randomBytes(32),
|
|
1308
1333
|
rpId: config.relyingPartyId(),
|
|
1309
1334
|
allowCredentials: [{ type: "public-key", id: decodeBase64(credentialId) }],
|
|
1310
1335
|
userVerification: "required",
|
|
1311
|
-
extensions
|
|
1336
|
+
...extensions ? { extensions } : {}
|
|
1312
1337
|
}
|
|
1313
1338
|
});
|
|
1314
1339
|
} catch (err) {
|
|
1315
1340
|
if (isPasskeyCancellation(err)) throw new PasskeyCancelledError();
|
|
1316
1341
|
throw err;
|
|
1317
1342
|
}
|
|
1343
|
+
}
|
|
1344
|
+
async function prfSecret(credentialId) {
|
|
1345
|
+
const assertion = await assertPasskey(credentialId, {
|
|
1346
|
+
prf: { eval: { first: config.prfSalt } }
|
|
1347
|
+
});
|
|
1318
1348
|
const result = assertion.getClientExtensionResults().prf?.results?.first;
|
|
1319
1349
|
if (!result) throw new PasskeyUnsupportedError("no-prf");
|
|
1320
1350
|
return new Uint8Array(result);
|
|
1321
1351
|
}
|
|
1322
|
-
const
|
|
1323
|
-
|
|
1324
|
-
if (
|
|
1325
|
-
|
|
1326
|
-
try {
|
|
1327
|
-
await signal({ rpId: config.relyingPartyId(), credentialId: base64url });
|
|
1328
|
-
} catch {
|
|
1329
|
-
return;
|
|
1352
|
+
const deviceKeys = config.deviceKeys ?? indexedDbDeviceKeys;
|
|
1353
|
+
async function lockFor(registration) {
|
|
1354
|
+
if (registration.prfFirst) {
|
|
1355
|
+
return { aes: await deriveAesKey(new Uint8Array(registration.prfFirst), config.hkdfInfo) };
|
|
1330
1356
|
}
|
|
1331
|
-
};
|
|
1332
|
-
async function secretFromRegistration(registration) {
|
|
1333
|
-
if (registration.prfFirst) return new Uint8Array(registration.prfFirst);
|
|
1334
1357
|
try {
|
|
1335
|
-
|
|
1336
|
-
return await prfSecret(registration.credentialId);
|
|
1358
|
+
return { aes: await deriveAesKey(await prfSecret(registration.credentialId), config.hkdfInfo) };
|
|
1337
1359
|
} catch (err) {
|
|
1338
|
-
if (err instanceof PasskeyUnsupportedError)
|
|
1339
|
-
throw err;
|
|
1360
|
+
if (!(err instanceof PasskeyUnsupportedError) || err.reason !== "no-prf") throw err;
|
|
1340
1361
|
}
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1362
|
+
const aes = await generateDeviceKey();
|
|
1363
|
+
await deviceKeys.save(registration.credentialId, aes);
|
|
1364
|
+
return { aes, lock: "device" };
|
|
1365
|
+
}
|
|
1366
|
+
async function unlockFor(sealed) {
|
|
1367
|
+
if (sealed.lock !== "device") return deriveAesKey(await prfSecret(sealed.credentialId), config.hkdfInfo);
|
|
1368
|
+
await assertPasskey(sealed.credentialId);
|
|
1369
|
+
const aes = await deviceKeys.load(sealed.credentialId);
|
|
1370
|
+
if (!aes) throw new DeviceKeyMissingError();
|
|
1371
|
+
return aes;
|
|
1372
|
+
}
|
|
1373
|
+
async function seal(aes, privateKeyHex) {
|
|
1344
1374
|
const iv = randomBytes(12);
|
|
1345
1375
|
const ciphertext = await sealPrivateKey(aes, iv, privateKeyHex);
|
|
1346
1376
|
return { iv: encodeBase64(iv), ciphertext: encodeBase64(ciphertext) };
|
|
@@ -1348,36 +1378,37 @@ function createPasskeyOwner(config) {
|
|
|
1348
1378
|
return {
|
|
1349
1379
|
async createOwnerKey() {
|
|
1350
1380
|
const registration = await registerPasskey();
|
|
1351
|
-
const
|
|
1381
|
+
const { aes, lock } = await lockFor(registration);
|
|
1352
1382
|
const { privateKeyHex, publicKeyHex } = generateStarkKeyPair();
|
|
1353
|
-
const { iv, ciphertext } = await seal(
|
|
1383
|
+
const { iv, ciphertext } = await seal(aes, privateKeyHex);
|
|
1354
1384
|
return {
|
|
1355
1385
|
sealed: {
|
|
1356
1386
|
credentialId: registration.credentialId,
|
|
1357
1387
|
ownerPubKey: publicKeyHex,
|
|
1358
1388
|
address: computeAccountAddress(publicKeyHex, 0),
|
|
1359
1389
|
iv,
|
|
1360
|
-
ciphertext
|
|
1390
|
+
ciphertext,
|
|
1391
|
+
...lock ? { lock } : {}
|
|
1361
1392
|
},
|
|
1362
1393
|
privateKeyHex
|
|
1363
1394
|
};
|
|
1364
1395
|
},
|
|
1365
1396
|
async unlockOwnerKey(sealed) {
|
|
1366
|
-
const
|
|
1367
|
-
const aes = await deriveAesKey(secret, config.hkdfInfo);
|
|
1397
|
+
const aes = await unlockFor(sealed);
|
|
1368
1398
|
return unsealPrivateKey(aes, decodeBase64(sealed.iv), decodeBase64(sealed.ciphertext));
|
|
1369
1399
|
},
|
|
1370
1400
|
async sealImportedOwnerKey(recovery) {
|
|
1371
1401
|
const { privateKeyHex, publicKeyHex } = starkKeyPairFromPrivateKey(recovery.privateKey);
|
|
1372
1402
|
const registration = await registerPasskey();
|
|
1373
|
-
const
|
|
1374
|
-
const { iv, ciphertext } = await seal(
|
|
1403
|
+
const { aes, lock } = await lockFor(registration);
|
|
1404
|
+
const { iv, ciphertext } = await seal(aes, privateKeyHex);
|
|
1375
1405
|
return {
|
|
1376
1406
|
credentialId: registration.credentialId,
|
|
1377
1407
|
ownerPubKey: publicKeyHex,
|
|
1378
1408
|
address: recovery.walletAddress,
|
|
1379
1409
|
iv,
|
|
1380
|
-
ciphertext
|
|
1410
|
+
ciphertext,
|
|
1411
|
+
...lock ? { lock } : {}
|
|
1381
1412
|
};
|
|
1382
1413
|
}
|
|
1383
1414
|
};
|
|
@@ -1821,6 +1852,7 @@ var UserFacingError = class extends Error {
|
|
|
1821
1852
|
|
|
1822
1853
|
exports.ApiClient = ApiClient;
|
|
1823
1854
|
exports.DEFAULT_CURRENCY = DEFAULT_CURRENCY;
|
|
1855
|
+
exports.DeviceKeyMissingError = DeviceKeyMissingError;
|
|
1824
1856
|
exports.FeeConfigSchema = FeeConfigSchema;
|
|
1825
1857
|
exports.InvalidStarkPrivateKeyError = InvalidStarkPrivateKeyError;
|
|
1826
1858
|
exports.MedialaneApiError = MedialaneApiError;
|
|
@@ -1884,6 +1916,7 @@ exports.getServicesByCapability = getServicesByCapability;
|
|
|
1884
1916
|
exports.getTokenByAddress = getTokenByAddress;
|
|
1885
1917
|
exports.getTokenBySymbol = getTokenBySymbol;
|
|
1886
1918
|
exports.hasCapability = hasCapability;
|
|
1919
|
+
exports.indexedDbDeviceKeys = indexedDbDeviceKeys;
|
|
1887
1920
|
exports.isServiceId = isServiceId;
|
|
1888
1921
|
exports.listServices = listServices;
|
|
1889
1922
|
exports.normalizeAddress = normalizeAddress;
|
|
@@ -1901,5 +1934,5 @@ exports.starkKeyPairFromPrivateKey = starkKeyPairFromPrivateKey;
|
|
|
1901
1934
|
exports.stringifyBigInts = stringifyBigInts;
|
|
1902
1935
|
exports.u256ToBigInt = u256ToBigInt;
|
|
1903
1936
|
exports.unsealPrivateKey = unsealPrivateKey;
|
|
1904
|
-
//# sourceMappingURL=chunk-
|
|
1905
|
-
//# sourceMappingURL=chunk-
|
|
1937
|
+
//# sourceMappingURL=chunk-2R7XBA34.cjs.map
|
|
1938
|
+
//# sourceMappingURL=chunk-2R7XBA34.cjs.map
|