@onekeyfe/hwk-ledger-adapter 1.2.1 → 1.2.2-alpha.1

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/index.mjs CHANGED
@@ -734,6 +734,10 @@ var _LedgerAdapter = class _LedgerAdapter {
734
734
  // The Ledger device itself still requires a per-call confirmation — that's
735
735
  // the Ledger app's safety boundary, not ours to bypass.
736
736
  this._btcHighIndexConfirmedThisSession = false;
737
+ // Runtime relay configuration mutates connector-wide DMK state. Serialize
738
+ // the complete configure → genuine check → clear lifecycle so concurrent
739
+ // callers cannot reset or overwrite each other's one-shot relay.
740
+ this._deviceAuthenticityQueueTail = Promise.resolve();
737
741
  // Shared across concurrent callers — only `cancel()` aborts.
738
742
  this._doConnectAbortController = null;
739
743
  this._installProgressLastEmittedValue = -Infinity;
@@ -1284,6 +1288,75 @@ var _LedgerAdapter = class _LedgerAdapter {
1284
1288
  return this.errorToFailure(err);
1285
1289
  }
1286
1290
  }
1291
+ /**
1292
+ * Runs Ledger's official genuine check (DMK GenuineCheckDeviceAction) over the
1293
+ * SAME secure-channel backend as app install
1294
+ * (wss://scriptrunner.api.live.ledger.com/update/genuine). It returns Ledger's
1295
+ * HSM verdict (`verified`) and a stable per-device id = sha3-256 of the device
1296
+ * attestation public key, which DMK reads inside that session. The id survives
1297
+ * wipe/recovery and cannot be forged from a seed.
1298
+ *
1299
+ * Requires network access to Ledger's backend and an on-device
1300
+ * "Allow secure connection" confirmation the first time.
1301
+ */
1302
+ async verifyDeviceAuthenticity(connectId, params = {}) {
1303
+ const waitForPrevious = this._deviceAuthenticityQueueTail;
1304
+ let releaseQueue = () => void 0;
1305
+ this._deviceAuthenticityQueueTail = new Promise((resolve) => {
1306
+ releaseQueue = resolve;
1307
+ });
1308
+ await waitForPrevious;
1309
+ try {
1310
+ return await this._verifyDeviceAuthenticityExclusive(connectId, params);
1311
+ } finally {
1312
+ releaseQueue();
1313
+ }
1314
+ }
1315
+ async _verifyDeviceAuthenticityExclusive(connectId, params) {
1316
+ const relayUrl = params.ledgerGenuineCheckWebSocketUrl;
1317
+ try {
1318
+ if (relayUrl) {
1319
+ if (!this.connector.configure) {
1320
+ return failure2(
1321
+ HardwareErrorCode3.MethodNotSupported,
1322
+ "This Ledger connector does not support genuine-check relay configuration"
1323
+ );
1324
+ }
1325
+ await this.connector.configure({ ledgerGenuineCheckWebSocketUrl: relayUrl });
1326
+ this.resetState();
1327
+ }
1328
+ const result = await this.connectorCall(connectId, "getDeviceGenuineCheck", {});
1329
+ if (!result.isGenuine) {
1330
+ return success({
1331
+ vendor: "ledger",
1332
+ verified: false
1333
+ });
1334
+ }
1335
+ if (!result.deviceId) {
1336
+ return failure2(
1337
+ HardwareErrorCode3.UnknownError,
1338
+ `Genuine check completed (isGenuine=${result.isGenuine}) but no deviceId was captured`
1339
+ );
1340
+ }
1341
+ return success({
1342
+ vendor: "ledger",
1343
+ verified: result.isGenuine,
1344
+ deviceId: result.deviceId
1345
+ });
1346
+ } catch (err) {
1347
+ return this.errorToFailure(err);
1348
+ } finally {
1349
+ if (relayUrl) {
1350
+ try {
1351
+ await this.connector.configure?.({ ledgerGenuineCheckWebSocketUrl: void 0 });
1352
+ this.resetState();
1353
+ } catch {
1354
+ this.connector.reset();
1355
+ this.resetState();
1356
+ }
1357
+ }
1358
+ }
1359
+ }
1287
1360
  on(event, listener) {
1288
1361
  this.emitter.on(event, listener);
1289
1362
  }
@@ -4035,6 +4108,30 @@ var HARDWARE_ERROR_CODE_VALUES = new Set(
4035
4108
  Object.values(HardwareErrorCode8).filter((value) => typeof value === "number")
4036
4109
  );
4037
4110
  var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
4111
+ var LEDGER_RELAY_ALLOWED_ROOT_DOMAINS = ["onekeytest.com", "onekey.com"];
4112
+ function isAllowedLedgerRelayHost(hostname) {
4113
+ return LEDGER_RELAY_ALLOWED_ROOT_DOMAINS.some(
4114
+ (root) => hostname === root || hostname.endsWith(`.${root}`)
4115
+ );
4116
+ }
4117
+ var LEDGER_RELAY_PATH_PREFIX = "/v1/ledger/session/";
4118
+ var LEDGER_RELAY_TOKEN_PATTERN = /^[A-Za-z0-9_-]{32,256}$/;
4119
+ var MAX_LEDGER_RELAY_URL_LENGTH = 2048;
4120
+ function assertAllowedLedgerRelayUrl(rawUrl) {
4121
+ if (!rawUrl || rawUrl.length > MAX_LEDGER_RELAY_URL_LENGTH) {
4122
+ throw new Error("Ledger genuine-check relay URL is invalid");
4123
+ }
4124
+ let parsed;
4125
+ try {
4126
+ parsed = new URL(rawUrl);
4127
+ } catch {
4128
+ throw new Error("Ledger genuine-check relay URL is invalid");
4129
+ }
4130
+ const token = parsed.pathname.startsWith(LEDGER_RELAY_PATH_PREFIX) ? parsed.pathname.slice(LEDGER_RELAY_PATH_PREFIX.length) : "";
4131
+ if (parsed.protocol !== "wss:" || !isAllowedLedgerRelayHost(parsed.hostname) || parsed.port !== "" || parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "" || !LEDGER_RELAY_TOKEN_PATTERN.test(token)) {
4132
+ throw new Error("Ledger genuine-check relay URL is not allowed");
4133
+ }
4134
+ }
4038
4135
  async function defaultLedgerKitImporter(pkg) {
4039
4136
  switch (pkg) {
4040
4137
  case "@ledgerhq/device-management-kit":
@@ -4556,6 +4653,41 @@ var LedgerConnectorBase = class {
4556
4653
  ctx.clearCanceller(sessionId);
4557
4654
  }
4558
4655
  }
4656
+ case "getDeviceGenuineCheck": {
4657
+ try {
4658
+ const dmk = await ctx.getOrCreateDmk();
4659
+ const kit = await ctx.importLedgerKit("@ledgerhq/device-management-kit");
4660
+ const action = dmk.executeDeviceAction({
4661
+ sessionId,
4662
+ deviceAction: new kit.GenuineCheckDeviceAction({ input: {} })
4663
+ });
4664
+ let deviceId;
4665
+ const output = await deviceActionToPromise(
4666
+ action,
4667
+ (interaction) => ctx.emit("ui-event", {
4668
+ type: collapseSignerInteraction(interaction),
4669
+ payload: { sessionId }
4670
+ }),
4671
+ // Generous timeout: covers websocket round-trips + the on-device
4672
+ // "Allow secure connection" tap (the idle watchdog does not pause
4673
+ // for it, only for unlock-device).
4674
+ 5 * 6e4,
4675
+ (cancel) => ctx.registerCanceller(sessionId, cancel),
4676
+ (intermediateValue) => {
4677
+ const iv = intermediateValue;
4678
+ if (iv?.deviceId instanceof Uint8Array && iv.deviceId.length === 32 && !deviceId) {
4679
+ deviceId = Buffer.from(iv.deviceId).toString("hex");
4680
+ }
4681
+ }
4682
+ );
4683
+ return { isGenuine: output.isGenuine, deviceId };
4684
+ } catch (err) {
4685
+ ctx.invalidateSession(sessionId);
4686
+ throw ctx.wrapError(err);
4687
+ } finally {
4688
+ ctx.clearCanceller(sessionId);
4689
+ }
4690
+ }
4559
4691
  default:
4560
4692
  throw new Error(`LedgerConnector: unknown method "${method}"`);
4561
4693
  }
@@ -4588,8 +4720,23 @@ var LedgerConnectorBase = class {
4588
4720
  // IConnector -- Reset
4589
4721
  // ---------------------------------------------------------------------------
4590
4722
  reset() {
4723
+ this._ledgerGenuineCheckWebSocketUrl = void 0;
4591
4724
  this._resetAll();
4592
4725
  }
4726
+ async configure(config) {
4727
+ const nextUrl = config.ledgerGenuineCheckWebSocketUrl;
4728
+ if (nextUrl) {
4729
+ assertAllowedLedgerRelayUrl(nextUrl);
4730
+ if (this._providedDmk) {
4731
+ throw new Error("Cannot change Ledger genuine-check relay on a pre-built DMK");
4732
+ }
4733
+ }
4734
+ if (nextUrl === this._ledgerGenuineCheckWebSocketUrl) {
4735
+ return;
4736
+ }
4737
+ this._resetAll();
4738
+ this._ledgerGenuineCheckWebSocketUrl = nextUrl;
4739
+ }
4593
4740
  // ---------------------------------------------------------------------------
4594
4741
  // Private -- DMK / Manager lifecycle
4595
4742
  // ---------------------------------------------------------------------------
@@ -4615,7 +4762,11 @@ var LedgerConnectorBase = class {
4615
4762
  );
4616
4763
  const transportFactory = await this._createTransport();
4617
4764
  debugLog("[DMK] _getOrCreateDmk: transportFactory type:", typeof transportFactory);
4618
- const dmk = new DeviceManagementKitBuilder().addTransport(transportFactory).build();
4765
+ const builder = new DeviceManagementKitBuilder().addTransport(transportFactory);
4766
+ if (this._ledgerGenuineCheckWebSocketUrl) {
4767
+ builder.addConfig({ webSocketUrl: this._ledgerGenuineCheckWebSocketUrl });
4768
+ }
4769
+ const dmk = builder.build();
4619
4770
  this._dmk = dmk;
4620
4771
  debugLog("[DMK] _getOrCreateDmk: DMK created");
4621
4772
  return dmk;