@medialane/sdk 0.195.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-7G7YIGXJ.cjs → chunk-2R7XBA34.cjs} +81 -49
- package/dist/chunk-2R7XBA34.cjs.map +1 -0
- package/dist/{chunk-ZCHP5D7S.js → chunk-6WVUUBFI.js} +80 -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-7G7YIGXJ.cjs.map +0 -1
- package/dist/chunk-ZCHP5D7S.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({
|
|
@@ -1295,53 +1322,55 @@ function createPasskeyOwner(config) {
|
|
|
1295
1322
|
const prf = credential.getClientExtensionResults().prf;
|
|
1296
1323
|
return {
|
|
1297
1324
|
credentialId: encodeBase64(credential.rawId),
|
|
1298
|
-
prfFirst: prf?.results?.first ?? null
|
|
1299
|
-
prfEnabled: prf?.enabled
|
|
1325
|
+
prfFirst: prf?.results?.first ?? null
|
|
1300
1326
|
};
|
|
1301
1327
|
}
|
|
1302
|
-
async function
|
|
1303
|
-
await ensurePrfPossible();
|
|
1304
|
-
let assertion;
|
|
1328
|
+
async function assertPasskey(credentialId, extensions) {
|
|
1305
1329
|
try {
|
|
1306
|
-
|
|
1330
|
+
return await credentialsApi().get({
|
|
1307
1331
|
publicKey: {
|
|
1308
1332
|
challenge: randomBytes(32),
|
|
1309
1333
|
rpId: config.relyingPartyId(),
|
|
1310
1334
|
allowCredentials: [{ type: "public-key", id: decodeBase64(credentialId) }],
|
|
1311
1335
|
userVerification: "required",
|
|
1312
|
-
extensions
|
|
1336
|
+
...extensions ? { extensions } : {}
|
|
1313
1337
|
}
|
|
1314
1338
|
});
|
|
1315
1339
|
} catch (err) {
|
|
1316
1340
|
if (isPasskeyCancellation(err)) throw new PasskeyCancelledError();
|
|
1317
1341
|
throw err;
|
|
1318
1342
|
}
|
|
1343
|
+
}
|
|
1344
|
+
async function prfSecret(credentialId) {
|
|
1345
|
+
const assertion = await assertPasskey(credentialId, {
|
|
1346
|
+
prf: { eval: { first: config.prfSalt } }
|
|
1347
|
+
});
|
|
1319
1348
|
const result = assertion.getClientExtensionResults().prf?.results?.first;
|
|
1320
1349
|
if (!result) throw new PasskeyUnsupportedError("no-prf");
|
|
1321
1350
|
return new Uint8Array(result);
|
|
1322
1351
|
}
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
if (
|
|
1326
|
-
|
|
1327
|
-
try {
|
|
1328
|
-
await signal({ rpId: config.relyingPartyId(), credentialId: base64url });
|
|
1329
|
-
} catch {
|
|
1330
|
-
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) };
|
|
1331
1356
|
}
|
|
1332
|
-
};
|
|
1333
|
-
async function secretFromRegistration(registration) {
|
|
1334
|
-
if (registration.prfFirst) return new Uint8Array(registration.prfFirst);
|
|
1335
1357
|
try {
|
|
1336
|
-
|
|
1337
|
-
return await prfSecret(registration.credentialId);
|
|
1358
|
+
return { aes: await deriveAesKey(await prfSecret(registration.credentialId), config.hkdfInfo) };
|
|
1338
1359
|
} catch (err) {
|
|
1339
|
-
if (err instanceof PasskeyUnsupportedError)
|
|
1340
|
-
throw err;
|
|
1360
|
+
if (!(err instanceof PasskeyUnsupportedError) || err.reason !== "no-prf") throw err;
|
|
1341
1361
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
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) {
|
|
1345
1374
|
const iv = randomBytes(12);
|
|
1346
1375
|
const ciphertext = await sealPrivateKey(aes, iv, privateKeyHex);
|
|
1347
1376
|
return { iv: encodeBase64(iv), ciphertext: encodeBase64(ciphertext) };
|
|
@@ -1349,36 +1378,37 @@ function createPasskeyOwner(config) {
|
|
|
1349
1378
|
return {
|
|
1350
1379
|
async createOwnerKey() {
|
|
1351
1380
|
const registration = await registerPasskey();
|
|
1352
|
-
const
|
|
1381
|
+
const { aes, lock } = await lockFor(registration);
|
|
1353
1382
|
const { privateKeyHex, publicKeyHex } = generateStarkKeyPair();
|
|
1354
|
-
const { iv, ciphertext } = await seal(
|
|
1383
|
+
const { iv, ciphertext } = await seal(aes, privateKeyHex);
|
|
1355
1384
|
return {
|
|
1356
1385
|
sealed: {
|
|
1357
1386
|
credentialId: registration.credentialId,
|
|
1358
1387
|
ownerPubKey: publicKeyHex,
|
|
1359
1388
|
address: computeAccountAddress(publicKeyHex, 0),
|
|
1360
1389
|
iv,
|
|
1361
|
-
ciphertext
|
|
1390
|
+
ciphertext,
|
|
1391
|
+
...lock ? { lock } : {}
|
|
1362
1392
|
},
|
|
1363
1393
|
privateKeyHex
|
|
1364
1394
|
};
|
|
1365
1395
|
},
|
|
1366
1396
|
async unlockOwnerKey(sealed) {
|
|
1367
|
-
const
|
|
1368
|
-
const aes = await deriveAesKey(secret, config.hkdfInfo);
|
|
1397
|
+
const aes = await unlockFor(sealed);
|
|
1369
1398
|
return unsealPrivateKey(aes, decodeBase64(sealed.iv), decodeBase64(sealed.ciphertext));
|
|
1370
1399
|
},
|
|
1371
1400
|
async sealImportedOwnerKey(recovery) {
|
|
1372
1401
|
const { privateKeyHex, publicKeyHex } = starkKeyPairFromPrivateKey(recovery.privateKey);
|
|
1373
1402
|
const registration = await registerPasskey();
|
|
1374
|
-
const
|
|
1375
|
-
const { iv, ciphertext } = await seal(
|
|
1403
|
+
const { aes, lock } = await lockFor(registration);
|
|
1404
|
+
const { iv, ciphertext } = await seal(aes, privateKeyHex);
|
|
1376
1405
|
return {
|
|
1377
1406
|
credentialId: registration.credentialId,
|
|
1378
1407
|
ownerPubKey: publicKeyHex,
|
|
1379
1408
|
address: recovery.walletAddress,
|
|
1380
1409
|
iv,
|
|
1381
|
-
ciphertext
|
|
1410
|
+
ciphertext,
|
|
1411
|
+
...lock ? { lock } : {}
|
|
1382
1412
|
};
|
|
1383
1413
|
}
|
|
1384
1414
|
};
|
|
@@ -1822,6 +1852,7 @@ var UserFacingError = class extends Error {
|
|
|
1822
1852
|
|
|
1823
1853
|
exports.ApiClient = ApiClient;
|
|
1824
1854
|
exports.DEFAULT_CURRENCY = DEFAULT_CURRENCY;
|
|
1855
|
+
exports.DeviceKeyMissingError = DeviceKeyMissingError;
|
|
1825
1856
|
exports.FeeConfigSchema = FeeConfigSchema;
|
|
1826
1857
|
exports.InvalidStarkPrivateKeyError = InvalidStarkPrivateKeyError;
|
|
1827
1858
|
exports.MedialaneApiError = MedialaneApiError;
|
|
@@ -1885,6 +1916,7 @@ exports.getServicesByCapability = getServicesByCapability;
|
|
|
1885
1916
|
exports.getTokenByAddress = getTokenByAddress;
|
|
1886
1917
|
exports.getTokenBySymbol = getTokenBySymbol;
|
|
1887
1918
|
exports.hasCapability = hasCapability;
|
|
1919
|
+
exports.indexedDbDeviceKeys = indexedDbDeviceKeys;
|
|
1888
1920
|
exports.isServiceId = isServiceId;
|
|
1889
1921
|
exports.listServices = listServices;
|
|
1890
1922
|
exports.normalizeAddress = normalizeAddress;
|
|
@@ -1902,5 +1934,5 @@ exports.starkKeyPairFromPrivateKey = starkKeyPairFromPrivateKey;
|
|
|
1902
1934
|
exports.stringifyBigInts = stringifyBigInts;
|
|
1903
1935
|
exports.u256ToBigInt = u256ToBigInt;
|
|
1904
1936
|
exports.unsealPrivateKey = unsealPrivateKey;
|
|
1905
|
-
//# sourceMappingURL=chunk-
|
|
1906
|
-
//# sourceMappingURL=chunk-
|
|
1937
|
+
//# sourceMappingURL=chunk-2R7XBA34.cjs.map
|
|
1938
|
+
//# sourceMappingURL=chunk-2R7XBA34.cjs.map
|