@onekeyfe/hwk-trezor-adapter 1.2.2-alpha.8 → 1.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/index.js +14 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/hardware-js-sdk/hardware-js-sdk/packages/hwk-trezor-adapter/dist/index.js","../src/adapter/TrezorAdapter.ts","../src/deviceAuthenticity/index.ts","../src/deviceAuthenticity/config.ts","../src/deviceAuthenticity/findSubjectContentByOid.ts","../src/deviceAuthenticity/validateCaCertExtensions.ts","../src/deviceAuthenticity/verifySignatures.ts","../src/deviceAuthenticity/x509certificate.ts","../src/deviceAuthenticity/verifyAuthenticityProof.ts","../src/utils/sdkEventBus.ts","../src/utils/debugLog.ts","../src/utils/ethereumDefinitions.ts","../src/utils/solanaTokenDefinition.ts","../src/constants.ts","../src/utils/bleIdentity.ts"],"names":[],"mappings":"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","file":"/home/runner/work/hardware-js-sdk/hardware-js-sdk/packages/hwk-trezor-adapter/dist/index.js","sourcesContent":[null,"import {\n DEVICE,\n DeviceJobQueue,\n HardwareErrorCode,\n TypedEventEmitter,\n UI_REQUEST,\n UI_RESPONSE,\n UiRequestRegistry,\n failure,\n rehydrateConnectorError,\n runAllNetworkGetAddress,\n success,\n} from '@onekeyfe/hwk-adapter-core';\nimport { randomBytes } from '@noble/hashes/utils';\n\nimport { authenticateDeviceFromProof } from '../deviceAuthenticity';\nimport { debugLog } from '../utils/debugLog';\n\nimport type { AuthenticityProof } from '../deviceAuthenticity';\nimport type {\n AllNetworkAddressParams,\n AllNetworkAddressResponse,\n AllNetworkGetAddressParams,\n AllNetworkMethodName,\n BtcAddress,\n BtcGetAddressParams,\n BtcGetPublicKeyParams,\n BtcPublicKey,\n BtcSignMsgParams,\n BtcSignPsbtParams,\n BtcSignTxParams,\n BtcSignature,\n BtcSignedPsbt,\n BtcSignedTx,\n ChainCapability,\n ChainForFingerprint,\n ConnectorCallResult,\n ConnectorDevice,\n ConnectorUiEvent,\n DeviceAuthenticityParams,\n DeviceAuthenticityResult,\n DeviceEventListener,\n DeviceInfo,\n EvmAddress,\n EvmGetAddressParams,\n EvmSignMsgParams,\n EvmSignTxTrezorParams,\n EvmSignTypedDataParams,\n EvmSignature,\n EvmSignedTx,\n HardwareEvent,\n HardwareEventMap,\n IBtcMethods,\n ICommonCallParams,\n IConnector,\n IDeviceManagerMethods,\n IEvmMethods,\n IHardwareWallet,\n ISolMethods,\n ITronMethods,\n Response,\n SolAddress,\n SolGetAddressParams,\n SolSignMsgParams,\n SolSignTxParams,\n SolSignature,\n SolSignedTx,\n TransportType,\n TrezorBrightnessParams,\n TrezorChangePinParams,\n TrezorDeviceSettingsParams,\n TronAddress,\n TronGetAddressParams,\n TronSignMsgParams,\n TronSignTxParams,\n TronSignature,\n TronSignedTx,\n UiResponseEvent,\n} from '@onekeyfe/hwk-adapter-core';\n\n// Valid HardwareErrorCode numbers. Used to reject foreign numeric `code`s (e.g.\n// a DOMException's `code: 20` on a WebUSB AbortError) that must NOT be trusted\n// as HardwareErrorCodes — otherwise they leak through as Unknown.\nconst KNOWN_HARDWARE_ERROR_CODES = new Set<number>(\n Object.values(HardwareErrorCode).filter((value): value is number => typeof value === 'number')\n);\n\ntype NullableCallArg<T> = T | null | undefined;\n\ntype TrezorPassphraseCallParams = {\n passphraseState?: string;\n useEmptyPassphrase?: boolean;\n};\n\ntype TrezorPassphraseRequestContext = {\n passphraseState?: string;\n useEmptyPassphrase?: boolean;\n};\n\ntype TrezorVerifiedPassphraseSession = {\n sessionId: string;\n protocol: 'thp' | 'v1';\n thpSessionId?: string;\n};\n\ntype TrezorCallParams<T> = (T & ICommonCallParams & TrezorPassphraseCallParams) | null | undefined;\n\ntype TrezorCommonParams = (ICommonCallParams & TrezorPassphraseCallParams) | null | undefined;\n\ntype AsyncMethodName<T> = Extract<\n {\n [K in keyof T]-?: NonNullable<T[K]> extends (\n ...args: infer _Args\n ) => Promise<Response<infer _Payload>>\n ? K\n : never;\n }[keyof T],\n string\n>;\n\ntype TrezorWalletMethods = IEvmMethods & IBtcMethods & ISolMethods & ITronMethods;\n\ntype TrezorWalletMethodName = AsyncMethodName<TrezorWalletMethods>;\n\ntype TrezorDeviceManagerMethodName = AsyncMethodName<IDeviceManagerMethods>;\n\ntype TrezorMethodName = TrezorWalletMethodName | TrezorDeviceManagerMethodName;\n\nconst TREZOR_BTC_NETWORK_COIN_MAP: Partial<Record<string, string>> = {\n btc: 'Bitcoin',\n bitcoin: 'Bitcoin',\n tbtc: 'Testnet',\n testnet: 'Testnet',\n sbtc: 'Signet',\n signet: 'Signet',\n ltc: 'Litecoin',\n litecoin: 'Litecoin',\n doge: 'Dogecoin',\n dogecoin: 'Dogecoin',\n bch: 'Bcash',\n bcash: 'Bcash',\n dash: 'Dash',\n};\n\nexport class TrezorAdapter implements IHardwareWallet {\n readonly vendor = 'trezor' as const;\n\n private readonly _connector: IConnector;\n\n private readonly _emitter = new TypedEventEmitter<HardwareEventMap>();\n\n private readonly _devices = new Map<string, DeviceInfo>();\n\n private readonly _sessions = new Map<string, string>();\n\n private readonly _connectingPromises = new Map<string, Promise<string>>();\n\n // connectIds disconnected mid-connect; the session they produce is torn down, not cached.\n private readonly _disconnectRequested = new Set<string>();\n\n private readonly _uiRegistry = new UiRequestRegistry();\n\n private readonly _passphraseRequestContextByConnectId = new Map<\n string,\n TrezorPassphraseRequestContext\n >();\n\n private readonly _verifiedPassphraseSessionsByConnectId = new Map<\n string,\n Map<string, TrezorVerifiedPassphraseSession>\n >();\n\n // Per-device queue with preemption + AbortSignal. Same-device chain calls\n // serialize (Trezor's protocol is single-request-response — interleaving\n // desyncs THP nonce). 'safe' read methods auto-cancel each other; 'confirm'\n // sign methods emit REQUEST_PREEMPTION so the consumer can decide.\n private readonly _jobQueue: DeviceJobQueue;\n\n constructor(connector: IConnector) {\n this._connector = connector;\n // Upstream DeviceJobQueue is now a global FIFO with no built-in\n // preemption — interruptibility lives at the application layer.\n this._jobQueue = new DeviceJobQueue();\n this._registerConnectorEvents();\n }\n\n private static _createDeviceBusyError(method: string): Error {\n // Our own queued request blocks this one → DeviceBusyInternal, not \"another app\".\n return Object.assign(new Error(`Trezor is busy with another request (${method})`), {\n code: HardwareErrorCode.DeviceBusyInternal,\n });\n }\n\n /**\n * Split a Trezor-only `passphraseState` routing hint off the chain params so\n * it never reaches the protobuf layer. The connector/firmware only sees the\n * real op fields; the adapter uses `passphraseState` to align the session.\n */\n private static _normalizeCallArgs(\n connectId: NullableCallArg<string>,\n deviceId: NullableCallArg<string>,\n params: NullableCallArg<unknown>\n ): {\n connectId: string;\n params: unknown;\n } {\n return {\n connectId: connectId || deviceId || '',\n params: params ?? {},\n };\n }\n\n private static _splitCommonParams(params: unknown): {\n passphraseState?: string;\n useEmptyPassphrase?: boolean;\n rest: unknown;\n } {\n if (params && typeof params === 'object') {\n const {\n passphraseState,\n autoInstallApp: _autoInstallApp,\n useEmptyPassphrase,\n ...rest\n } = params as Record<string, unknown>;\n return {\n passphraseState: typeof passphraseState === 'string' ? passphraseState : undefined,\n useEmptyPassphrase:\n typeof useEmptyPassphrase === 'boolean' ? useEmptyPassphrase : undefined,\n rest,\n };\n }\n return { rest: params ?? {} };\n }\n\n private static _withAllNetworkRequestCommonParams(\n item: AllNetworkAddressParams,\n commonParams: TrezorCommonParams\n ): AllNetworkAddressParams {\n const autoInstallApp = commonParams?.autoInstallApp;\n const passphraseState = commonParams?.passphraseState;\n const useEmptyPassphrase = commonParams?.useEmptyPassphrase;\n if (\n autoInstallApp === undefined &&\n passphraseState === undefined &&\n useEmptyPassphrase === undefined\n ) {\n return item;\n }\n return {\n ...item,\n ...(autoInstallApp !== undefined ? { autoInstallApp } : {}),\n ...(passphraseState !== undefined ? { passphraseState } : {}),\n ...(useEmptyPassphrase !== undefined ? { useEmptyPassphrase } : {}),\n };\n }\n\n // Keys are matched after normalizeLogKey, so snake_case/camelCase both hit.\n private static readonly _sensitiveLogKeys = new Set([\n 'credential',\n 'credentials',\n 'entropy',\n 'hoststatickey',\n 'mnemonic',\n 'mnemonics',\n 'passphrase',\n 'passphrasestate',\n 'password',\n 'pin',\n 'privatekey',\n 'seed',\n 'session',\n 'sessionid',\n 'trezorstaticpublickey',\n 'word',\n 'words',\n 'xprv',\n ]);\n\n private static _normalizeLogKey(key: string): string {\n return key.replace(/[_-]/g, '').toLowerCase();\n }\n\n // Mirrors hd-core's logBlockEvent: a signing method's body is the user's\n // transaction, so it is dropped wholesale; other methods redact by key.\n private static _sanitizeForLog(value: unknown, methodName?: string): unknown {\n if (methodName && /sign/i.test(methodName)) return '[redacted]';\n return TrezorAdapter._redactForLog(value, new WeakSet());\n }\n\n private static _redactForLog(value: unknown, seen: WeakSet<object>): unknown {\n if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {\n return `[BINARY:${value.byteLength}]`;\n }\n if (!value || typeof value !== 'object') return value;\n if (seen.has(value)) return '[CIRCULAR]';\n seen.add(value);\n const out = Array.isArray(value)\n ? value.map(item => TrezorAdapter._redactForLog(item, seen))\n : Object.fromEntries(\n Object.entries(value as Record<string, unknown>).map(([key, item]) => [\n key,\n TrezorAdapter._sensitiveLogKeys.has(TrezorAdapter._normalizeLogKey(key)) &&\n item !== null &&\n item !== undefined\n ? '[redacted]'\n : TrezorAdapter._redactForLog(item, seen),\n ])\n );\n seen.delete(value);\n return out;\n }\n\n private static _errorForLog(error: unknown): unknown {\n if (!error || typeof error !== 'object') return error;\n const typed = error as {\n code?: unknown;\n errorCode?: unknown;\n message?: unknown;\n name?: unknown;\n response?: unknown;\n statusCode?: unknown;\n };\n return TrezorAdapter._sanitizeForLog({\n name: typed.name,\n message: typed.message,\n code: typed.code,\n errorCode: typed.errorCode,\n statusCode: typed.statusCode,\n response: typed.response,\n });\n }\n\n private _setPassphraseRequestContext(\n connectId: string,\n context: TrezorPassphraseRequestContext\n ): () => void {\n const previous = this._passphraseRequestContextByConnectId.get(connectId);\n if (context.passphraseState !== undefined || context.useEmptyPassphrase !== undefined) {\n this._passphraseRequestContextByConnectId.set(connectId, context);\n } else {\n this._passphraseRequestContextByConnectId.delete(connectId);\n }\n return () => {\n if (previous) {\n this._passphraseRequestContextByConnectId.set(connectId, previous);\n } else {\n this._passphraseRequestContextByConnectId.delete(connectId);\n }\n };\n }\n\n private static _trezorFailureMessage(error: unknown): {\n code?: unknown;\n message?: unknown;\n } {\n const response = (error as { response?: unknown } | null | undefined)?.response;\n const message = (response as { message?: unknown } | null | undefined)?.message;\n if (!message || typeof message !== 'object') return {};\n const failureMessage = message as { code?: unknown; message?: unknown };\n return {\n code: failureMessage.code,\n message: failureMessage.message,\n };\n }\n\n private static _mapTrezorFailureCode(error: unknown): HardwareErrorCode | undefined {\n const directCode = (error as { code?: unknown } | null | undefined)?.code;\n if (directCode === 'Device_InitializeFailed') {\n return HardwareErrorCode.PinCancelled;\n }\n if (directCode === 'Failure_ActionCancelled') {\n return HardwareErrorCode.UserRejected;\n }\n if (directCode === 'ThpPairingRequired') {\n return HardwareErrorCode.ThpPairingRequired;\n }\n if (directCode === 'Failure_UnexpectedMessage') {\n return HardwareErrorCode.MethodNotSupported;\n }\n const message = (error as { message?: unknown } | null | undefined)?.message;\n if (\n typeof message === 'string' &&\n message.includes(\"Failed to execute 'transfer\") &&\n message.includes(\"on 'USBDevice'\")\n ) {\n return HardwareErrorCode.TransportError;\n }\n const failure = TrezorAdapter._trezorFailureMessage(error);\n if (failure.code === 'Failure_PinCancelled') {\n return HardwareErrorCode.PinCancelled;\n }\n if (failure.code === 'Failure_PinInvalid') {\n return HardwareErrorCode.PinInvalid;\n }\n if (failure.code === 'Failure_PinMismatch') {\n return HardwareErrorCode.PinMismatch;\n }\n if (failure.code === 'Failure_ActionCancelled') {\n return HardwareErrorCode.UserRejected;\n }\n if (failure.code === 'Failure_ProcessError' && failure.message === 'Unsupported script type') {\n return HardwareErrorCode.MethodNotSupported;\n }\n // Device rejected the derivation path (e.g. an index outside its allowed\n // range) — distinct path-specific surface, not a generic \"method\" error.\n if (failure.code === 'Failure_DataError' && failure.message === 'Forbidden key path') {\n return HardwareErrorCode.DevicePathForbidden;\n }\n // Firmware can't process this request (e.g. EIP-712 typed data the device\n // firmware doesn't support) → surface as \"method not supported\".\n if (failure.code === 'Failure_FirmwareError') {\n return HardwareErrorCode.MethodNotSupported;\n }\n // Device firmware doesn't recognize the message (e.g. an older device asked\n // for a coin/app it lacks) → \"method not supported\", not a generic unknown.\n if (failure.code === 'Failure_UnexpectedMessage') {\n return HardwareErrorCode.MethodNotSupported;\n }\n if (failure.code === 'Failure_NotInitialized') {\n return HardwareErrorCode.DeviceNotInitialized;\n }\n // Device busy with its own op, not another app → DeviceBusyInternal.\n if (failure.code === 'Failure_Busy' || failure.code === 'Failure_InProgress') {\n return HardwareErrorCode.DeviceBusyInternal;\n }\n return undefined;\n }\n\n /**\n * Whether a device-side error code means the THP application session we\n * selected is gone (the device evicted it from its limited slot pool). The\n * caller drops the stale cache entry and recreates from scratch.\n *\n * NOTE: the exact eviction code still needs real-device confirmation — match\n * conservatively so a genuine error isn't mistaken for an evicted session\n * (which would silently re-prompt the passphrase).\n */\n private static _isStaleSessionError(code: unknown): boolean {\n if (typeof code !== 'string') return false;\n return (\n code === 'ThpUnallocatedChannel' ||\n /invalid.?session|unallocated.?session|session.?not.?found/i.test(code)\n );\n }\n\n private static _errorCode(error: unknown): unknown {\n return (error as { code?: unknown }).code ?? TrezorAdapter._trezorFailureMessage(error).code;\n }\n\n private static _isConnectionUnavailableError(error: unknown): boolean {\n const { code } = error as { code?: unknown };\n return (\n code === HardwareErrorCode.DeviceNotFound || code === HardwareErrorCode.DeviceDisconnected\n );\n }\n\n private static _isRecoverableProtocolResidueError(error: unknown): boolean {\n const protocolError = error as {\n name?: unknown;\n code?: unknown;\n message?: unknown;\n };\n return (\n protocolError.name === 'TrezorProtocolError' &&\n protocolError.code === 'Malformed protocol format' &&\n protocolError.message === 'Malformed protocol format'\n );\n }\n\n /**\n * Race a promise against an abort signal. Copied from LedgerAdapter:\n * IConnector.call() can't actually be cancelled at the protocol level, but\n * the caller gets the abort immediately and the underlying work completes\n * uselessly in the background.\n */\n private static _abortable<T>(signal: AbortSignal, promise: Promise<T>): Promise<T> {\n if (signal.aborted) {\n return Promise.reject(\n (signal as AbortSignal & { reason?: unknown }).reason ?? new Error('Aborted')\n );\n }\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n reject((signal as AbortSignal & { reason?: unknown }).reason ?? new Error('Aborted'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n promise.then(\n value => {\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n err => {\n signal.removeEventListener('abort', onAbort);\n reject(err);\n }\n );\n });\n }\n\n private async _connectSession(connectId: string, signal?: AbortSignal): Promise<string> {\n const connectOnce = async () => {\n const connectPromise = this._connector.connect(connectId);\n if (!signal) return connectPromise;\n try {\n return await TrezorAdapter._abortable(signal, connectPromise);\n } catch (error) {\n // _abortable rejected (abort/failure) but the underlying connect keeps\n // running — tear down any session it still resolves so it isn't orphaned.\n void connectPromise.then(\n orphan => this._connector.disconnect(orphan.sessionId).catch(() => undefined),\n () => undefined\n );\n throw error;\n }\n };\n let session;\n try {\n session = await connectOnce();\n } catch (error) {\n if (signal?.aborted || !TrezorAdapter._isRecoverableProtocolResidueError(error)) {\n throw error;\n }\n // An interrupted THP handshake may leave exactly one v2 frame queued on\n // the shared USB pipe. The connector intentionally drains it as a loud\n // v1 malformed-frame failure; retry once now that the residue is gone.\n debugLog('[TrezorAdapter] retrying connect after draining a stale THP frame', {\n connectId,\n });\n session = await connectOnce();\n }\n // disconnectDevice() ran mid-connect: tear down the now-unwanted session\n // instead of caching it (else it leaks a limited THP slot).\n if (this._disconnectRequested.delete(connectId)) {\n await this._connector.disconnect(session.sessionId).catch(() => undefined);\n throw Object.assign(new Error('Trezor connection aborted'), {\n code: HardwareErrorCode.UserAborted,\n });\n }\n this._sessions.set(connectId, session.sessionId);\n this._verifiedPassphraseSessionsByConnectId.delete(connectId);\n this._devices.set(connectId, session.deviceInfo);\n return session.sessionId;\n }\n\n get activeTransport(): TransportType | null {\n return this._connector.connectionType;\n }\n\n async init(_config?: unknown): Promise<void> {}\n\n async dispose(): Promise<void> {\n this._unregisterConnectorEvents();\n this._uiRegistry.reset();\n this._jobQueue.clear();\n this._connector.reset();\n this._devices.clear();\n this._sessions.clear();\n this._verifiedPassphraseSessionsByConnectId.clear();\n this._connectingPromises.clear();\n this._emitter.removeAllListeners();\n }\n\n /**\n * Clear cached state without tearing down the adapter or unregistering\n * listeners. Mirrors LedgerAdapter.resetState() — useful for retry-after-\n * error flows. Aborts active jobs and releases UI waits.\n */\n resetState(): void {\n this._uiRegistry.reset();\n this._jobQueue.clear();\n this._devices.clear();\n this._sessions.clear();\n this._verifiedPassphraseSessionsByConnectId.clear();\n this._connectingPromises.clear();\n }\n\n getAvailableTransports(): TransportType[] {\n return this.activeTransport ? [this.activeTransport] : [];\n }\n\n async switchTransport(_type: TransportType): Promise<void> {}\n\n async searchDevices(options?: { waitForAllTransports?: boolean }): Promise<DeviceInfo[]> {\n await this._ensureDevicePermission();\n const scanned = await this._connector.searchDevices(\n options?.waitForAllTransports ? { waitForAll: true } : undefined\n );\n const scannedIds = new Set(scanned.map(d => d.connectId));\n // A rescan that misses a currently-connected device shouldn't evict it —\n // otherwise getDeviceInfo would return DeviceNotFound for an active session.\n for (const connectId of [...this._devices.keys()]) {\n if (!scannedIds.has(connectId) && !this._sessions.has(connectId)) {\n this._devices.delete(connectId);\n }\n }\n for (const device of scanned) {\n const info = this._connectorDeviceToDeviceInfo(device);\n this._devices.set(info.connectId, info);\n }\n return Array.from(this._devices.values());\n }\n\n async connectDevice(connectId: string): Promise<Response<string>> {\n try {\n await this._ensureDevicePermission(connectId);\n await this._ensureSession(connectId);\n return success(connectId);\n } catch (error) {\n return this._errorToFailure(error);\n }\n }\n\n async disconnectDevice(connectId: string): Promise<void> {\n const connecting = this._connectingPromises.has(connectId);\n this._connectingPromises.delete(connectId);\n const sessionId = this._sessions.get(connectId);\n if (!sessionId) {\n // Connect may be in flight (session not recorded yet) — mark it so\n // _connectSession tears down the session it produces instead of caching it.\n if (connecting) this._disconnectRequested.add(connectId);\n return;\n }\n this._sessions.delete(connectId);\n this._verifiedPassphraseSessionsByConnectId.delete(connectId);\n await this._connector.disconnect(sessionId);\n }\n\n async getDeviceInfo(connectId: string, deviceId: string): Promise<Response<DeviceInfo>> {\n const device =\n this._devices.get(connectId) ??\n Array.from(this._devices.values()).find(item => item.deviceId === deviceId);\n if (!device) return failure(HardwareErrorCode.DeviceNotFound, 'Trezor device not found');\n return success(device);\n }\n\n getSupportedChains(): ChainCapability[] {\n // Keep in sync with TrezorConnectorBase.call()'s switch.\n // Chain inclusion means \"at least one method wired\" — per-method gaps:\n // sol: signMessage is explicitly NotSupported (firmware doesn't ship it).\n // tron: signMessage same. signTransaction uses the multi-step\n // TronContractRequest flow.\n return ['btc', 'evm', 'sol', 'tron'];\n }\n\n async allNetworkGetAddress(\n connectId: string,\n deviceId: string,\n params: AllNetworkGetAddressParams\n ): Promise<Response<AllNetworkAddressResponse[]>> {\n let liveDeviceId = '';\n const topLevelFailureIndexes = new Set<number>();\n const isSingleNetworkBundle = params.bundle.every(\n item => item.network === params.bundle[0]?.network\n );\n const getLiveDeviceId = async (): Promise<Response<string>> => {\n if (liveDeviceId) return success(liveDeviceId);\n const deviceIdResponse = await this._getTrezorDeviceId(connectId);\n if (deviceIdResponse.success) {\n liveDeviceId = deviceIdResponse.payload;\n }\n return deviceIdResponse;\n };\n\n return runAllNetworkGetAddress({\n connectId,\n deviceId,\n params,\n normalizeItem: TrezorAdapter._normalizeAllNetworkItem,\n callItem: async ({ method, item, index }) => {\n const expectedDeviceId = TrezorAdapter._getItemDeviceId(item) ?? deviceId;\n if (expectedDeviceId) {\n const fingerprint = await getLiveDeviceId();\n if (!fingerprint.success) {\n topLevelFailureIndexes.add(index);\n return fingerprint;\n }\n if (expectedDeviceId !== fingerprint.payload) {\n topLevelFailureIndexes.add(index);\n return failure(\n HardwareErrorCode.DeviceMismatch,\n `Wrong device: expected ${expectedDeviceId}, got ${fingerprint.payload}`,\n { expected: expectedDeviceId, actual: fingerprint.payload }\n );\n }\n }\n const callItem = TrezorAdapter._withAllNetworkRequestCommonParams(item, params);\n return this._callAllNetworkMethod(connectId, method, callItem);\n },\n attachIdentity: async ({ item, chain, payload }) => {\n if (!liveDeviceId) {\n const fingerprint = await getLiveDeviceId();\n if (!fingerprint.success) {\n return { ...item, success: false, payload: fingerprint.payload };\n }\n }\n return {\n ...item,\n success: true,\n payload: {\n ...payload,\n deviceIdentity: {\n vendor: 'trezor',\n type: 'deviceId',\n value: liveDeviceId,\n },\n chainFingerprint: liveDeviceId,\n chainFingerprintChain: chain,\n },\n };\n },\n // One named boolean per abort reason.\n shouldAbortBundle: (response, { index }) => {\n const isSessionLevelFailure = topLevelFailureIndexes.has(index);\n // Mixed bundles must not abort — other chains can still derive.\n const isWholeChainForbidden =\n isSingleNetworkBundle && response.payload?.code === HardwareErrorCode.DevicePathForbidden;\n return isSessionLevelFailure || isWholeChainForbidden;\n },\n });\n }\n\n getFeatures(connectId: string): Promise<Response<Record<string, unknown>>> {\n return this._callDeviceManagerMethod<Record<string, unknown>>('getFeatures', connectId, {});\n }\n\n /**\n * Device authenticity attestation. Sends a host-generated challenge, has the\n * device sign it with its secure-element key, then verifies the returned\n * certificate chain up to a trusted Trezor root CA. On success returns a\n * `deviceId` that uniquely identifies the physical device, survives wipe, and\n * cannot be forged from a seed. Only trust the result when `verified` is true\n * (and `usedDebugKey` is false in production).\n *\n * Requires a secure-element model (Safe 3 = T2B1/T3B1, Safe 5 = T3T1, T3W1)\n * and an on-device confirmation. Older models (T1B1, T2T1) will fail.\n *\n * `dangerouslyAllowDebugKeys` accepts simulator / development root keys — NEVER\n * enable it in a production accounting flow, it lets an emulator mint any id.\n */\n async verifyDeviceAuthenticity(\n connectId: string,\n params: DeviceAuthenticityParams = {}\n ): Promise<Response<DeviceAuthenticityResult>> {\n if (params.challenge && params.dangerouslyAllowDebugKeys) {\n return failure(\n HardwareErrorCode.InvalidParams,\n 'Debug attestation roots cannot be used with a server challenge'\n );\n }\n if (params.challenge && !/^[0-9a-fA-F]{64}$/.test(params.challenge)) {\n return failure(\n HardwareErrorCode.InvalidParams,\n 'Device authenticity challenge must be exactly 32 bytes encoded as hex'\n );\n }\n\n const featuresRes = await this.getFeatures(connectId);\n if (!featuresRes.success) return featuresRes;\n\n const internalModel = (featuresRes.payload as { internal_model?: string }).internal_model;\n if (!internalModel) {\n return failure(HardwareErrorCode.UnknownError, 'Device internal_model unavailable');\n }\n\n const challengeHex =\n params.challenge?.toLowerCase() ?? Buffer.from(randomBytes(32)).toString('hex');\n const challenge = Buffer.from(challengeHex, 'hex');\n const proofRes = await this._callDeviceManagerMethod<Record<string, unknown>>(\n 'authenticateDevice',\n connectId,\n { challenge: challengeHex }\n );\n if (!proofRes.success) return proofRes;\n\n const proof = proofRes.payload as unknown as AuthenticityProof;\n const verification = authenticateDeviceFromProof({\n proof,\n challenge,\n deviceModel: internalModel,\n allowDebugKeys: params.dangerouslyAllowDebugKeys,\n });\n\n return success({\n ...verification,\n vendor: 'trezor' as const,\n trezorProof: {\n challenge: challengeHex,\n deviceModel: internalModel,\n proof,\n },\n });\n }\n\n deviceSettings(\n connectId: string,\n params: TrezorDeviceSettingsParams\n ): Promise<Response<Record<string, unknown>>> {\n return this._callDeviceManagerMethod<Record<string, unknown>>(\n 'deviceSettings',\n connectId,\n params\n );\n }\n\n setBrightness(\n connectId: string,\n params: TrezorBrightnessParams = {}\n ): Promise<Response<Record<string, unknown>>> {\n return this._callDeviceManagerMethod<Record<string, unknown>>(\n 'setBrightness',\n connectId,\n params\n );\n }\n\n changePin(\n connectId: string,\n params: TrezorChangePinParams = {}\n ): Promise<Response<Record<string, unknown>>> {\n return this._callDeviceManagerMethod<Record<string, unknown>>('changePin', connectId, params);\n }\n\n wipeDevice(connectId: string): Promise<Response<Record<string, unknown>>> {\n return this._callDeviceManagerMethod<Record<string, unknown>>('wipeDevice', connectId, {});\n }\n\n private async _getTrezorDeviceId(connectId: string): Promise<Response<string>> {\n const features = await this.getFeatures(connectId);\n if (!features.success) return features;\n const liveDeviceId = features.payload.device_id;\n if (typeof liveDeviceId !== 'string' || liveDeviceId.length === 0) {\n return failure(HardwareErrorCode.UnknownError, 'Trezor features did not include a device_id');\n }\n return success(liveDeviceId);\n }\n\n cancel(connectId?: string): void {\n const userAbortReason = Object.assign(new Error('User aborted operation'), {\n code: HardwareErrorCode.UserAborted,\n });\n // Release adapter-level UI waits (preemption) and connector-level ones\n // (THP pairing / PIN matrix) — a single CANCEL clears whichever is open.\n this._uiRegistry.cancel();\n this._connector.uiResponse({ type: UI_RESPONSE.CANCEL });\n if (connectId) {\n this._jobQueue.forceCancelActive(connectId, userAbortReason);\n void this._connector.cancel(this._sessions.get(connectId) ?? connectId);\n return;\n }\n for (const cid of this._sessions.keys()) {\n this._jobQueue.forceCancelActive(cid, userAbortReason);\n }\n for (const sessionId of this._sessions.values()) {\n void this._connector.cancel(sessionId);\n }\n }\n\n /**\n * Routes to BOTH the adapter's local UI registry (preemption decisions,\n * future SELECT_DEVICE / DEVICE_PERMISSION) AND the connector's registry\n * (THP pairing tag, PIN matrix). Each side ignores types it isn't waiting\n * for, so double-routing is harmless.\n */\n uiResponse(response: UiResponseEvent): void {\n this._uiRegistry.resolve(response.type, response.payload);\n this._connector.uiResponse(response);\n }\n\n /**\n * Returns the Trezor `device_id` from Features — a random 12-byte value the\n * device stores and regenerates on `wipe_device`. It identifies the PHYSICAL\n * DEVICE, not a seed/wallet: it is shared across passphrase wallets and\n * changes after wipe/restore, so it suits device-match checks but must NOT be\n * used as a wallet fingerprint (use `passphraseState` for wallet identity).\n * Unlike Ledger (which hashes a per-chain address), it is chain-global, so\n * `chain` is accepted for API parity but ignored.\n */\n async getChainFingerprint(\n connectId: string,\n _deviceId: string,\n _chain: ChainForFingerprint\n ): Promise<Response<string>> {\n return this._getTrezorDeviceId(connectId);\n }\n\n /**\n * Resolve the active passphrase wallet identity (`passphraseState`).\n * See {@link IHardwareWallet.getPassphraseState} for the discover/verify\n * semantics. Returns `null` for a standard wallet (no passphrase protection) —\n * there's nothing to pin. Serialized through the per-device job queue so it\n * can't interleave with a chain call on the same device (which would desync\n * the THP session selection).\n */\n getPassphraseState(\n connectId: string,\n passphraseState?: string\n ): Promise<Response<string | null>> {\n return this._jobQueue\n .enqueue(\n connectId,\n async signal => {\n const restorePassphraseRequestContext = this._setPassphraseRequestContext(connectId, {\n passphraseState,\n });\n try {\n return await this._resolvePassphraseState(connectId, passphraseState, signal);\n } finally {\n restorePassphraseRequestContext();\n }\n },\n {\n label: 'getPassphraseState',\n rejectIfBusy: true,\n busyError: TrezorAdapter._createDeviceBusyError('getPassphraseState'),\n }\n )\n .catch(error => this._errorToFailure(error));\n }\n\n evmGetAddress(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<EvmGetAddressParams>\n ) {\n return this._callWalletMethod<EvmAddress>('evmGetAddress', connectId, deviceId, params);\n }\n\n evmSignTransaction(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<EvmSignTxTrezorParams>\n ) {\n return this._callWalletMethod<EvmSignedTx>('evmSignTransaction', connectId, deviceId, params);\n }\n\n evmSignMessage(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<EvmSignMsgParams>\n ) {\n return this._callWalletMethod<EvmSignature>('evmSignMessage', connectId, deviceId, params);\n }\n\n evmSignTypedData(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<EvmSignTypedDataParams>\n ) {\n return this._callWalletMethod<EvmSignature>('evmSignTypedData', connectId, deviceId, params);\n }\n\n btcGetAddress(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<BtcGetAddressParams>\n ) {\n return this._callWalletMethod<BtcAddress>('btcGetAddress', connectId, deviceId, params);\n }\n\n btcGetPublicKey(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<BtcGetPublicKeyParams>\n ) {\n return this._callWalletMethod<BtcPublicKey>('btcGetPublicKey', connectId, deviceId, params);\n }\n\n btcSignTransaction(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<BtcSignTxParams>\n ) {\n return this._callWalletMethod<BtcSignedTx>('btcSignTransaction', connectId, deviceId, params);\n }\n\n btcSignPsbt(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<BtcSignPsbtParams>\n ) {\n return this._callWalletMethod<BtcSignedPsbt>('btcSignPsbt', connectId, deviceId, params);\n }\n\n btcSignMessage(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<BtcSignMsgParams>\n ) {\n return this._callWalletMethod<BtcSignature>('btcSignMessage', connectId, deviceId, params);\n }\n\n btcGetMasterFingerprint(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCommonParams\n ) {\n return this._callWalletMethod<{ masterFingerprint: string }>(\n 'btcGetMasterFingerprint',\n connectId,\n deviceId,\n params\n );\n }\n\n solGetAddress(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<SolGetAddressParams>\n ) {\n return this._callWalletMethod<SolAddress>('solGetAddress', connectId, deviceId, params);\n }\n\n solSignTransaction(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<SolSignTxParams>\n ) {\n return this._callWalletMethod<SolSignedTx>('solSignTransaction', connectId, deviceId, params);\n }\n\n solSignMessage(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<SolSignMsgParams>\n ) {\n return this._callWalletMethod<SolSignature>('solSignMessage', connectId, deviceId, params);\n }\n\n tronGetAddress(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<TronGetAddressParams>\n ) {\n return this._callWalletMethod<TronAddress>('tronGetAddress', connectId, deviceId, params);\n }\n\n tronSignTransaction(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<TronSignTxParams>\n ) {\n return this._callWalletMethod<TronSignedTx>('tronSignTransaction', connectId, deviceId, params);\n }\n\n tronSignMessage(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<TronSignMsgParams>\n ) {\n return this._callWalletMethod<TronSignature>('tronSignMessage', connectId, deviceId, params);\n }\n\n private static _unwrapConnectorResult(result: unknown): unknown {\n if (\n result &&\n typeof result === 'object' &&\n 'success' in result &&\n (((result as ConnectorCallResult).success === true && 'payload' in result) ||\n ((result as ConnectorCallResult).success === false && 'error' in result))\n ) {\n const connectorResult = result as ConnectorCallResult;\n if (connectorResult.success) return connectorResult.payload;\n throw rehydrateConnectorError(connectorResult.error);\n }\n return result;\n }\n\n private static async _callConnector(\n connector: IConnector,\n sessionId: string,\n method: string,\n params: unknown,\n signal?: AbortSignal\n ): Promise<unknown> {\n const promise = connector.call(sessionId, method, params);\n const result = signal ? await TrezorAdapter._abortable(signal, promise) : await promise;\n return TrezorAdapter._unwrapConnectorResult(result);\n }\n\n private static _normalizeAllNetworkItem(\n method: AllNetworkMethodName,\n item: AllNetworkAddressParams\n ): AllNetworkAddressParams {\n if (method !== 'btcGetAddress' && method !== 'btcGetPublicKey') {\n return item;\n }\n\n const itemWithCoin = item as AllNetworkAddressParams & { coin?: unknown };\n if (itemWithCoin.coin) {\n return item;\n }\n\n const network = item.network.toLowerCase();\n return {\n ...item,\n coin: TREZOR_BTC_NETWORK_COIN_MAP[network] ?? item.network,\n };\n }\n\n private static _getItemDeviceId(item: AllNetworkAddressParams): string | undefined {\n const { deviceId } = item as { deviceId?: unknown };\n return typeof deviceId === 'string' && deviceId.length > 0 ? deviceId : undefined;\n }\n\n private _callAllNetworkMethod(\n connectId: string,\n method: AllNetworkMethodName,\n item: AllNetworkAddressParams\n ): Promise<Response<unknown>> {\n switch (method) {\n case 'evmGetAddress':\n return this.evmGetAddress(connectId, '', item);\n case 'btcGetAddress':\n return this.btcGetAddress(connectId, '', item);\n case 'btcGetPublicKey':\n return this.btcGetPublicKey(connectId, '', item);\n case 'solGetAddress':\n return this.solGetAddress(connectId, '', item);\n case 'tronGetAddress':\n return this.tronGetAddress(connectId, '', item);\n default:\n return Promise.resolve(\n failure(\n HardwareErrorCode.InvalidParams,\n `Unsupported allNetwork method: ${String(method)}`\n )\n );\n }\n }\n\n on<K extends keyof HardwareEventMap>(\n event: K,\n listener: (event: HardwareEventMap[K]) => void\n ): void;\n\n on(event: string, listener: DeviceEventListener): void;\n\n on(event: string, listener: (event: HardwareEvent) => void): void {\n this._emitter.on(event, listener);\n }\n\n off<K extends keyof HardwareEventMap>(\n event: K,\n listener: (event: HardwareEventMap[K]) => void\n ): void;\n\n off(event: string, listener: DeviceEventListener): void;\n\n off(event: string, listener: (event: HardwareEvent) => void): void {\n this._emitter.off(event, listener);\n }\n\n private _callWalletMethod<T>(\n methodName: TrezorWalletMethodName,\n connectId: NullableCallArg<string>,\n deviceId: NullableCallArg<string>,\n params: NullableCallArg<unknown>\n ): Promise<Response<T>> {\n return this._callMethod<T>(methodName, connectId, deviceId, params, true);\n }\n\n private _callDeviceManagerMethod<T>(\n methodName: TrezorDeviceManagerMethodName,\n connectId: NullableCallArg<string>,\n params: NullableCallArg<unknown>\n ): Promise<Response<T>> {\n return this._callMethod<T>(methodName, connectId, undefined, params, false);\n }\n\n private async _callMethod<T>(\n methodName: TrezorMethodName,\n connectId: NullableCallArg<string>,\n deviceId: NullableCallArg<string>,\n params: NullableCallArg<unknown>,\n requiresWalletIntent: boolean\n ): Promise<Response<T>> {\n const call = TrezorAdapter._normalizeCallArgs(connectId, deviceId, params);\n debugLog('[TrezorAdapter][REQ]', {\n method: methodName,\n connectId: call.connectId || '(empty)',\n deviceId: deviceId || undefined,\n params: TrezorAdapter._sanitizeForLog(call.params, methodName),\n });\n try {\n const response = await this._jobQueue.enqueue(\n call.connectId,\n signal =>\n this._callWithRetry<T>(\n call.connectId,\n methodName,\n call.params,\n requiresWalletIntent,\n true,\n signal\n ),\n {\n label: methodName,\n rejectIfBusy: true,\n busyError: TrezorAdapter._createDeviceBusyError(methodName),\n }\n );\n debugLog('[TrezorAdapter][RES]', {\n method: methodName,\n success: response.success,\n payload: TrezorAdapter._sanitizeForLog(response.payload, methodName),\n });\n return response;\n } catch (error) {\n // The only way a Promise rejects out of `enqueue` is the busy-reject —\n // _callWithRetry never throws, it returns Response.failure. Convert to\n // the Response shape so chain method callers stay uniform.\n const response = this._errorToFailure(error);\n debugLog('[TrezorAdapter][RES]', {\n method: methodName,\n success: false,\n payload: TrezorAdapter._sanitizeForLog(response.payload, methodName),\n });\n return response;\n }\n }\n\n private async _callWithRetry<T>(\n connectId: string,\n methodName: TrezorMethodName,\n params: unknown,\n requiresWalletIntent: boolean,\n allowRetry: boolean,\n signal: AbortSignal\n ): Promise<Response<T>> {\n const { passphraseState, useEmptyPassphrase, rest } = TrezorAdapter._splitCommonParams(params);\n const restorePassphraseRequestContext = this._setPassphraseRequestContext(connectId, {\n passphraseState,\n useEmptyPassphrase,\n });\n try {\n const sessionId = await this._ensureSession(connectId, signal);\n if (requiresWalletIntent && !passphraseState && useEmptyPassphrase !== true) {\n return failure(\n HardwareErrorCode.InvalidParams,\n `Trezor ${methodName} requires an explicit wallet intent: passphraseState or useEmptyPassphrase`\n );\n }\n // Pin the call to the requested passphrase wallet. For THP this creates\n // a new application session; for v1 it re-initializes the device session.\n // Runs inside this queued job so the session alignment and the call can't\n // be interleaved by another request.\n if (passphraseState) {\n await TrezorAdapter._abortable(\n signal,\n this._alignAppSession(connectId, sessionId, passphraseState, signal)\n );\n } else if (useEmptyPassphrase === true) {\n await TrezorAdapter._abortable(signal, this._createFreshAppSession(sessionId, signal));\n }\n const result = await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, methodName, rest)\n );\n return success(result as T);\n } catch (error) {\n // If we were aborted, surface as-is — don't take the retry/recovery path.\n if (signal.aborted) {\n return this._errorToFailure(error);\n }\n const code = TrezorAdapter._errorCode(error);\n // On a transient disconnect (BLE drop, \"device not connected\" race after\n // the device-disconnect event hasn't propagated yet), drop the stale\n // session and reconnect once. Matches Ledger's _retryWithFreshConnection.\n if (allowRetry && code === HardwareErrorCode.DeviceDisconnected) {\n this._sessions.delete(connectId);\n this._verifiedPassphraseSessionsByConnectId.delete(connectId);\n return await this._callWithRetry<T>(\n connectId,\n methodName,\n params,\n requiresWalletIntent,\n false,\n signal\n );\n }\n // The device evicted the passphrase session created for this call. Retry\n // once and recreate the requested wallet context.\n if (\n allowRetry &&\n (passphraseState || useEmptyPassphrase === true) &&\n TrezorAdapter._isStaleSessionError(code)\n ) {\n if (passphraseState) {\n this._forgetVerifiedPassphraseSession(connectId, passphraseState);\n }\n return await this._callWithRetry<T>(\n connectId,\n methodName,\n params,\n requiresWalletIntent,\n false,\n signal\n );\n }\n debugLog('[TrezorAdapter][ERROR]', {\n method: methodName,\n connectId,\n error: TrezorAdapter._errorForLog(error),\n });\n return this._errorToFailure(error);\n } finally {\n restorePassphraseRequestContext();\n }\n }\n\n private async _ensureSession(connectId: string, signal?: AbortSignal): Promise<string> {\n const existing = this._sessions.get(connectId);\n if (existing) return existing;\n\n const pending = this._connectingPromises.get(connectId);\n if (pending) return pending;\n\n const promise = (async () => {\n try {\n if (signal?.aborted) {\n throw Object.assign(new Error('Trezor connection aborted'), {\n code: HardwareErrorCode.UserAborted,\n });\n }\n // Fail fast: a single connect attempt. On an \"unavailable device\" error we\n // drop the cached session/passphrase state and rethrow (no retry loop).\n try {\n return await this._connectSession(connectId, signal);\n } catch (error) {\n if (TrezorAdapter._isConnectionUnavailableError(error)) {\n this._sessions.delete(connectId);\n this._verifiedPassphraseSessionsByConnectId.delete(connectId);\n }\n throw error;\n }\n } finally {\n this._connectingPromises.delete(connectId);\n this._disconnectRequested.delete(connectId);\n }\n })();\n\n this._connectingPromises.set(connectId, promise);\n return promise;\n }\n\n /**\n * Pin the device's active wallet session to `passphraseState` (the target\n * wallet identity) before a wallet-bound op. THP has an app-session id; v1\n * does not, but it still binds passphrase to the device session, so the\n * connector re-initializes v1 before we derive and verify.\n *\n * SECURITY: a verified THP app session may be selected again, but the adapter\n * still derives and verifies the passphrase state before any chain method can\n * run. If the cache is stale, fall back to creating a fresh wallet context.\n */\n private async _alignAppSession(\n connectId: string,\n sessionId: string,\n passphraseState: string,\n signal: AbortSignal\n ): Promise<void> {\n const reused = await this._reuseVerifiedPassphraseSession(\n connectId,\n sessionId,\n passphraseState,\n signal\n );\n if (reused) return;\n\n const created = (await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, '__thpCreateSession', {\n passphraseMode: 'prompt',\n })\n )) as { protocol?: string; thpSessionId?: string | null };\n // v1 has no host-managed app-session id, but the connector has just\n // re-initialized a fresh device session; continue with reactive\n // passphrase-state verification.\n if (created?.protocol === 'thp' && !created.thpSessionId) return;\n if (created?.protocol !== 'thp' && created?.protocol !== 'v1') return;\n const state = await this._deriveState(sessionId, signal);\n if (state !== passphraseState) {\n throw Object.assign(\n new Error(\n `Passphrase produced wallet ${state}, expected ${passphraseState} (passphraseState mismatch)`\n ),\n { code: HardwareErrorCode.PassphraseStateMismatch }\n );\n }\n this._rememberVerifiedPassphraseSession(connectId, passphraseState, sessionId, created);\n }\n\n private _rememberVerifiedPassphraseSession(\n connectId: string,\n passphraseState: string,\n sessionId: string,\n created: { protocol?: string; thpSessionId?: string | null } | undefined\n ): void {\n let session: TrezorVerifiedPassphraseSession | undefined;\n if (created?.protocol === 'thp' && typeof created.thpSessionId === 'string') {\n session = {\n sessionId,\n protocol: 'thp',\n thpSessionId: created.thpSessionId,\n };\n } else if (created?.protocol === 'v1') {\n session = {\n sessionId,\n protocol: 'v1',\n };\n }\n if (!session) return;\n\n const sessions =\n this._verifiedPassphraseSessionsByConnectId.get(connectId) ??\n new Map<string, TrezorVerifiedPassphraseSession>();\n sessions.set(passphraseState, session);\n this._verifiedPassphraseSessionsByConnectId.set(connectId, sessions);\n }\n\n private _forgetVerifiedPassphraseSession(connectId: string, passphraseState: string): void {\n const sessions = this._verifiedPassphraseSessionsByConnectId.get(connectId);\n if (!sessions) return;\n sessions.delete(passphraseState);\n if (!sessions.size) {\n this._verifiedPassphraseSessionsByConnectId.delete(connectId);\n }\n }\n\n private async _reuseVerifiedPassphraseSession(\n connectId: string,\n sessionId: string,\n passphraseState: string,\n signal: AbortSignal\n ): Promise<boolean> {\n const cached = this._verifiedPassphraseSessionsByConnectId.get(connectId)?.get(passphraseState);\n if (!cached || cached.sessionId !== sessionId) {\n this._forgetVerifiedPassphraseSession(connectId, passphraseState);\n return false;\n }\n\n try {\n if (cached.protocol === 'thp') {\n if (!cached.thpSessionId) return false;\n await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, '__thpSelectSession', {\n thpSessionId: cached.thpSessionId,\n })\n );\n }\n const state = await this._deriveState(sessionId, signal);\n if (state !== passphraseState) {\n this._forgetVerifiedPassphraseSession(connectId, passphraseState);\n return false;\n }\n return true;\n } catch (error) {\n this._forgetVerifiedPassphraseSession(connectId, passphraseState);\n if (signal.aborted) throw error;\n return false;\n }\n }\n\n private async _createFreshAppSession(sessionId: string, signal: AbortSignal): Promise<void> {\n // Create a fresh standard-wallet context for this call. Empty passphrase is\n // enforced inside connector/core, not delegated to host UI handlers.\n await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, '__thpCreateSession', {\n passphraseMode: 'empty',\n })\n );\n }\n\n /**\n * Derive the active session's passphrase state — the wallet identity used to\n * verify the right wallet is active: the compressed PUBLIC KEY at a fixed\n * account path (m/44'/0'/0'). It comes from the SAME GetPublicKey that also\n * carries the 4-byte fingerprint, but we use the FULL pubkey (not the truncated\n * fingerprint) so it's collision-resistant — the address we return can never\n * come from the wrong wallet — while avoiding OneKey's extra Testnet GetAddress.\n * Non-interactive (also unlocks via PIN as a protected derivation).\n */\n private async _deriveState(sessionId: string, signal: AbortSignal): Promise<string> {\n const derived = (await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, 'btcGetPublicKey', {\n path: \"m/44'/0'/0'\",\n showOnDevice: false,\n })\n )) as { publicKey?: string };\n const state = derived?.publicKey;\n if (!state) {\n throw Object.assign(new Error('Failed to derive passphrase state (wallet public key)'), {\n code: HardwareErrorCode.UnknownError,\n });\n }\n return state;\n }\n\n private async _resolvePassphraseState(\n connectId: string,\n passphraseState: string | undefined,\n signal: AbortSignal\n ): Promise<Response<string | null>> {\n try {\n const sessionId = await this._ensureSession(connectId, signal);\n if (passphraseState) {\n // Verify mode: align (which always re-derives + confirms the state) so the\n // host can gate signing on the correct wallet being active.\n await this._alignAppSession(connectId, sessionId, passphraseState, signal);\n return success(passphraseState);\n }\n // Discover mode (OneKey-aligned). DON'T trust pre-read features — a LOCKED\n // device reports an unreliable `passphrase_protection`, and the connector's\n // plain getFeatures returns a CACHED blob, so checking `unlocked` up front\n // is meaningless. Instead derive first (which unlocks), then re-read fresh:\n // 1. Create a session + derive the state. This UNLOCKS the device (PIN) and,\n // for a passphrase wallet, prompts the passphrase (THP: proactively in\n // ThpCreateNewSession; v1: reactively).\n // 2. Re-read features FRESH from the device (now unlocked → trustworthy).\n // 3. passphrase_protection === true → return the current passphrase\n // state, even if the entered passphrase was empty. Otherwise →\n // standard wallet → return null (OneKey-aligned).\n const created = (await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, '__thpCreateSession', {\n passphraseMode: 'prompt',\n })\n )) as { protocol?: string; thpSessionId?: string | null };\n const state = await this._deriveState(sessionId, signal);\n const features = (await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, 'getFeatures', { refresh: true })\n )) as Record<string, unknown> | undefined;\n if (features?.passphrase_protection !== true) {\n // Standard wallet — discard the derived state, return null (OneKey convention).\n return success(null);\n }\n this._rememberVerifiedPassphraseSession(connectId, state, sessionId, created);\n return success(state);\n } catch (error) {\n return this._errorToFailure(error);\n }\n }\n\n private _onDeviceConnect = (data: { device: ConnectorDevice }): void => {\n const device = this._connectorDeviceToDeviceInfo(data.device);\n this._devices.set(device.connectId, device);\n this._emitter.emit(DEVICE.CONNECT, { type: DEVICE.CONNECT, payload: device });\n };\n\n private _onDeviceDisconnect = (data: { connectId: string }): void => {\n this._sessions.delete(data.connectId);\n this._verifiedPassphraseSessionsByConnectId.delete(data.connectId);\n this._devices.delete(data.connectId);\n this._emitter.emit(DEVICE.DISCONNECT, {\n type: DEVICE.DISCONNECT,\n payload: { connectId: data.connectId },\n });\n };\n\n private _onSupportFeatures = (data: {\n device: ConnectorDevice & { features: Record<string, unknown> };\n }): void => {\n const device = {\n ...this._connectorDeviceToDeviceInfo(data.device),\n features: data.device.features,\n };\n this._emitter.emit(DEVICE.FEATURES, {\n type: DEVICE.FEATURES,\n device,\n payload: { device },\n });\n };\n\n private _onUiRequest = (data: { type: string; payload?: unknown }): void => {\n if (data.type === UI_REQUEST.REQUEST_PASSPHRASE) {\n const payload =\n data.payload && typeof data.payload === 'object'\n ? (data.payload as Record<string, unknown>)\n : {};\n const connectId = typeof payload.connectId === 'string' ? payload.connectId : undefined;\n const context = connectId\n ? this._passphraseRequestContextByConnectId.get(connectId)\n : undefined;\n this._emitter.emit(\n data.type as keyof HardwareEventMap,\n {\n ...data,\n payload: {\n ...payload,\n ...(context?.passphraseState !== undefined\n ? { passphraseState: context.passphraseState }\n : {}),\n ...(context?.useEmptyPassphrase !== undefined\n ? { useEmptyPassphrase: context.useEmptyPassphrase }\n : {}),\n },\n } as never\n );\n return;\n }\n this._emitter.emit(data.type as keyof HardwareEventMap, data as never);\n };\n\n private _onUiEvent = (data: ConnectorUiEvent): void => {\n this._emitter.emit('ui-event', data);\n };\n\n private _onThpCredentialsChanged = (data: {\n connectId: string;\n deviceId?: string;\n credentials: Record<string, unknown>[];\n }): void => {\n this._emitter.emit(DEVICE.TREZOR_THP_CREDENTIALS_CHANGED, {\n type: DEVICE.TREZOR_THP_CREDENTIALS_CHANGED,\n payload: data,\n });\n };\n\n private _registerConnectorEvents(): void {\n this._connector.on('device-connect', this._onDeviceConnect);\n this._connector.on('device-disconnect', this._onDeviceDisconnect);\n this._connector.on(DEVICE.FEATURES, this._onSupportFeatures);\n this._connector.on('device-trezor-thp-credentials-changed', this._onThpCredentialsChanged);\n this._connector.on('ui-request', this._onUiRequest);\n this._connector.on('ui-event', this._onUiEvent);\n }\n\n private _unregisterConnectorEvents(): void {\n this._connector.off('device-connect', this._onDeviceConnect);\n this._connector.off('device-disconnect', this._onDeviceDisconnect);\n this._connector.off(DEVICE.FEATURES, this._onSupportFeatures);\n this._connector.off('device-trezor-thp-credentials-changed', this._onThpCredentialsChanged);\n this._connector.off('ui-request', this._onUiRequest);\n this._connector.off('ui-event', this._onUiEvent);\n }\n\n private _connectorDeviceToDeviceInfo(device: ConnectorDevice): DeviceInfo {\n return {\n vendor: 'trezor',\n model: device.model ?? 'unknown',\n firmwareVersion: 'unknown',\n deviceId: device.deviceId,\n connectId: device.connectId,\n label: device.name,\n // The per-device transport is authoritative (a combined USB+BLE\n // connector tags each discovered device); the connector's own\n // connectionType is only a nominal value, so fall back to it for\n // single-transport connectors that don't tag each device.\n connectionType: device.connectionType ?? this._connector.connectionType,\n capabilities: device.capabilities,\n };\n }\n\n private _errorToFailure(error: unknown) {\n const typed = error as { code?: unknown; message?: string };\n // Map hwk-trezor-core's string markers to standard HardwareErrorCodes.\n // The core tags pairing-handshake rejections (e.g. mistyped CodeEntry code)\n // with `code: 'ThpPairingFailed'`; surface it as a proper code, not Unknown.\n if (typed.code === 'ThpPairingFailed') {\n return failure(HardwareErrorCode.ThpPairingFailed, typed.message ?? 'THP pairing failed');\n }\n // Only trust a numeric `code` if it is an actual HardwareErrorCode. A raw\n // WebUSB AbortError carries a DOMException `code: 20`, which is NOT one of\n // ours — fall through to message-based mapping (transferOut/USBDevice abort\n // → TransportError) so it surfaces as a recognizable, retryable transport\n // error instead of Unknown.\n const trustedNumericCode =\n typeof typed.code === 'number' && KNOWN_HARDWARE_ERROR_CODES.has(typed.code)\n ? typed.code\n : undefined;\n const code =\n trustedNumericCode ??\n TrezorAdapter._mapTrezorFailureCode(error) ??\n HardwareErrorCode.UnknownError;\n return failure(code, typed.message ?? String(error));\n }\n\n /**\n * Emit `REQUEST_DEVICE_PERMISSION` and await the host's\n * `RECEIVE_DEVICE_PERMISSION` reply (60s budget covers OS prompt + user\n * tap). If the host doesn't wire a handler the wait times out and the\n * operation fails fast.\n *\n * Mirrors LedgerAdapter._ensureDevicePermission so hosts can register a\n * single handler that branches by `payload.transportType` (and optionally\n * the active brand) instead of teaching every adapter its own contract.\n *\n * - no `connectId` → environment-level check (scan / picker)\n * - with `connectId` → device-level check (per-call gating)\n */\n private async _ensureDevicePermission(connectId?: string, deviceId?: string): Promise<void> {\n // No handler? Skip silently so non-permission-aware hosts (and our own\n // unit tests) keep working — Ledger's adapter has a 60s timeout but\n // hardware-wallet flows on web/desktop don't always have a registered\n // listener, and they shouldn't stall for a minute waiting on one.\n if (!this._emitter.listenerCount(UI_REQUEST.REQUEST_DEVICE_PERMISSION)) {\n return;\n }\n const transportType: TransportType = this.activeTransport ?? 'usb';\n\n const waitPromise = this._uiRegistry.wait<{ granted: boolean; reason?: string }>(\n UI_REQUEST.REQUEST_DEVICE_PERMISSION,\n { timeoutMs: 60_000 }\n );\n\n this._emitter.emit(UI_REQUEST.REQUEST_DEVICE_PERMISSION, {\n type: UI_REQUEST.REQUEST_DEVICE_PERMISSION,\n payload: { transportType, connectId, deviceId },\n });\n\n const { granted } = await waitPromise;\n if (!granted) {\n throw Object.assign(new Error('Device permission denied'), {\n code: HardwareErrorCode.DevicePermissionDenied,\n });\n }\n }\n}\n","import { sha3_256 as sha3Hash } from '@noble/hashes/sha3';\n\nimport { deviceAuthenticityConfig } from './config';\nimport { prepareDeviceAuthenticityData, verifyAuthenticityProof } from './verifyAuthenticityProof';\n\nimport type { DeviceAuthenticityConfig } from './types';\n\nexport { deviceAuthenticityConfig } from './config';\nexport { prepareDeviceAuthenticityData, verifyAuthenticityProof } from './verifyAuthenticityProof';\nexport * from './types';\n\n/** Raw fields taken from a Trezor `AuthenticityProof` message. */\nexport type AuthenticityProof = {\n optiga_certificates: string[];\n optiga_signature: string;\n tropic_certificates?: string[];\n tropic_signature?: string;\n mcu_certificates?: string[];\n mcu_signature?: string;\n};\n\nexport type AuthenticateDeviceResult = {\n /**\n * True only when every layer implemented by this verifier and required by\n * the explicit model policy verified up to a trusted Trezor root CA AND the\n * device signed our challenge:\n * - Optiga (all secure-element models), and\n * - Tropic (T3W1 / Safe 7).\n * This is the currently implemented client policy, not full parity with the\n * latest capability-based Trezor Connect policy. Raw MCU fields remain in the\n * returned proof but are not yet a client-side pass/fail condition.\n * The reward backend must independently verify the same raw proof under its\n * own versioned policy; this client result is only a UX preview.\n */\n verified: boolean;\n /**\n * SHA3-256 of the verified Optiga device-attestation public key. It survives\n * wipe/recovery and has one representation across supported models. Do NOT\n * trust it when `verified` is false — it is then attacker-controlled.\n */\n deviceId?: string;\n /** Raw device attestation public key (hex) from the Optiga device certificate. */\n deviceCertPubKey?: string;\n /** X.509 subject serial number (hex), only present on T3W1 and above. */\n serialNumber?: string;\n rootPubKey?: string;\n caPubKey?: string;\n /**\n * True when the chain matched a DEBUG/staging root key (simulator / dev\n * device). A production accounting flow must reject verified results that\n * have `usedDebugKey === true`.\n */\n usedDebugKey?: boolean;\n /** Failure reason when `verified` is false. */\n error?: string;\n};\n\n// Revocation hook: CA public keys (hex) that must be rejected even if otherwise\n// valid. Empty today; kept so a leaked intermediate CA can be blocked by config\n// rather than a code change. Mirrors @trezor/device-authenticity's blacklist.\nexport const caPubKeyBlacklist: string[] = [];\n\nconst modelConfigOf = (config: DeviceAuthenticityConfig, deviceModel: string) => {\n const modelConfig = config[deviceModel];\n return modelConfig && typeof modelConfig !== 'number' ? modelConfig : undefined;\n};\n\nexport const getRequiredDeviceAuthenticityLayers = (\n deviceModel: string\n): readonly ('optiga' | 'tropic')[] => (deviceModel === 'T3W1' ? ['optiga', 'tropic'] : ['optiga']);\n\nconst isTropicExpected = (deviceModel: string): boolean =>\n getRequiredDeviceAuthenticityLayers(deviceModel).includes('tropic');\n\nconst matchedDebugKey = (\n config: DeviceAuthenticityConfig,\n deviceModel: string,\n rootPubKey?: string\n): boolean => {\n if (!rootPubKey) return false;\n const m = modelConfigOf(config, deviceModel);\n if (!m?.debug) return false;\n return [...(m.debug.rootPubKeysOptiga ?? []), ...(m.debug.rootPubKeysTropic ?? [])].includes(\n rootPubKey\n );\n};\n\n/**\n * Verify a Trezor `AuthenticityProof` against a challenge and derive a stable\n * per-device id. `challenge` must be the exact random bytes sent in the\n * `AuthenticateDevice` request. Never throws — malformed input from a malicious\n * device is folded into `{ verified: false }`.\n */\nexport const authenticateDeviceFromProof = ({\n proof,\n challenge,\n deviceModel,\n config = deviceAuthenticityConfig,\n allowDebugKeys,\n}: {\n proof: AuthenticityProof;\n challenge: Buffer;\n deviceModel: string;\n config?: DeviceAuthenticityConfig;\n allowDebugKeys?: boolean;\n}): AuthenticateDeviceResult => {\n try {\n const signedData = prepareDeviceAuthenticityData({ payload: challenge });\n const common = { signedData, deviceModel, config, allowDebugKeys, caPubKeyBlacklist };\n\n // 1) Optiga is required on every attestation-capable model.\n const { optiga_certificates: optigaCertificates, optiga_signature: optigaSignature } = proof;\n if (!optigaSignature || !optigaCertificates?.length) {\n return { verified: false, error: 'RESPONSE_PAYLOAD_MISSING' };\n }\n const optiga = verifyAuthenticityProof({\n ...common,\n proofType: 'optiga',\n certificates: optigaCertificates,\n signature: optigaSignature,\n });\n if (!optiga.valid) {\n return {\n verified: false,\n deviceCertPubKey: optiga.deviceCertPubKey,\n caPubKey: optiga.caPubKey,\n rootPubKey: optiga.rootPubKey,\n error: optiga.error,\n };\n }\n\n // 2) Trezor Connect requires the independent Tropic proof on T3W1.\n let usedDebugKey = matchedDebugKey(config, deviceModel, optiga.rootPubKey);\n if (isTropicExpected(deviceModel)) {\n const { tropic_certificates: tropicCertificates, tropic_signature: tropicSignature } = proof;\n if (!tropicSignature || !tropicCertificates?.length) {\n return { verified: false, error: 'RESPONSE_PAYLOAD_MISSING' };\n }\n const tropic = verifyAuthenticityProof({\n ...common,\n proofType: 'tropic',\n certificates: tropicCertificates,\n signature: tropicSignature,\n });\n if (!tropic.valid) {\n return { verified: false, error: tropic.error };\n }\n usedDebugKey = usedDebugKey || matchedDebugKey(config, deviceModel, tropic.rootPubKey);\n }\n\n const deviceId = Buffer.from(\n sha3Hash(Uint8Array.from(Buffer.from(optiga.deviceCertPubKey, 'hex')))\n ).toString('hex');\n\n return {\n verified: true,\n deviceId,\n deviceCertPubKey: optiga.deviceCertPubKey,\n serialNumber: optiga.serialNumber,\n rootPubKey: optiga.rootPubKey,\n caPubKey: optiga.caPubKey,\n usedDebugKey,\n };\n } catch (error) {\n // A malicious device can return a malformed certificate that throws deep in\n // the parser; never let that escape as a rejected promise.\n return {\n verified: false,\n error: `RESPONSE_MALFORMED: ${(error as Error)?.message ?? 'parse error'}`,\n };\n }\n};\n","import type { DeviceAuthenticityConfig } from './types';\n\n/**\n * Trezor manufacturing root CA public keys, per internal model.\n * Authoritative upstream source (firmware):\n * https://github.com/trezor/trezor-firmware/blob/main/core/embed/projects/prodtest/cmd/hsm_keys.h\n * Mirrored from @trezor/device-authenticity `deviceAuthenticityConfig`.\n *\n * Devices without a secure element (T1B1, T2T1) do not support AuthenticateDevice\n * and are therefore absent from this config.\n */\nexport const deviceAuthenticityConfig: DeviceAuthenticityConfig = {\n version: 2,\n T2B1: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_PROD_P256\n '04ca97480ac0d7b1e6efafe518cd433cec2bf8ab9822d76eafd34363b55d63e60380bff20acc75cde03cffcb50ab6f8ce70c878e37ebc58ff7cca0a83b16b15fa5',\n ],\n debug: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_DEBUG_P256 shared with T3B1\n '047f77368dea2d4d61e989f474a56723c3212dacf8a808d8795595ef38441427c4389bc454f02089d7f08b873005e4c28d432468997871c0bf286fd3861e21e96a',\n ],\n },\n },\n T3B1: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_PROD_P256\n '045b5c3fdd01f3602092834209b86df0ca86a9faf25cac35c73bf6237d66eb21eafcec3706f1ccd5eb4cc7f2fa1751213eccb1c78389afba89a5788ff31ee46a5d',\n ],\n debug: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_DEBUG_P256 shared with T2B1\n '047f77368dea2d4d61e989f474a56723c3212dacf8a808d8795595ef38441427c4389bc454f02089d7f08b873005e4c28d432468997871c0bf286fd3861e21e96a',\n ],\n },\n },\n T3T1: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_PROD_P256\n '041854b27fb1d9f65abb66828e78c9dc0ca301e66081ab0c6a4d104f9df1cd0ad5a7c75f77a8c092f55cf825d2abaf734f934c9394d5e75f75a5a06a5ee9be93ae',\n ],\n debug: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_DEBUG_P256\n '04e48b69cd7962068d3cca3bcc6b1747ef496c1e28b5529e34ad7295215ea161dbe8fb08ae0479568f9d2cb07630cb3e52f4af0692102da5873559e45e9fa72959',\n ],\n },\n },\n T3W1: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_PROD_P256\n '040dde0d3e0d4da593fac6fd02a461d0e7eef238aca55c7c50b4e9ec37f3873303b6429ef1c9b78b4411a7dcbbc5dde5225979c1c2da3b073e82b1ed3f5f9825bb',\n // upstream: DEV_AUTH_ROOT_PROD_BACKUP_P256\n '04c6a673af4ec44b10441b1d78676e15173ad0e36df9f7f2fa1cd819955f20fe32917b60da5fed3b3aa54a9ab8b3ed27d198b3768cad26eef5935cd87af0af065e',\n ],\n rootPubKeysTropic: [\n // upstream: DEV_AUTH_ROOT_PROD_ED25519\n '59237acd17134061d655b3f8d624573ca06ce8d862f38ba4e05140ce1d3d609d',\n // upstream: DEV_AUTH_ROOT_PROD_BACKUP_ED25519\n '5612606584ee7e0bc313b13f7ac94156bb4cb75bd77585ddbe579301306e85f1',\n ],\n debug: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_DEBUG_P256\n '04521192e173a9da4e3023f747d836563725372681eba3079c56ff11b2fc137ab189eb4155f371127651b5594f8c332fc1e9c0f3b80d4212822668b63189706578',\n // upstream: DEV_AUTH_ROOT_STAGING_P256\n '0465e88f9b2cea67e8364f0cfcfacd500af24e9040b357beee629ccc4fce1704d1a7ef7284f387708f92ef14600e2caad6894016fee819d623b95d66210c3e7519',\n ],\n rootPubKeysTropic: [\n // upstream: DEV_AUTH_ROOT_STAGING_ED25519\n 'cd318dc8405ae4f4144e3284dcb7b0cb0f0c2195c2ca14a0f6fccd9104e32a4b',\n ],\n },\n },\n};\n","import type { Oid, ParsedCertificate } from './x509certificate';\n\nexport const findSubjectContentByOid = (\n deviceCert: ParsedCertificate,\n oid: Oid\n): Uint8Array | null => {\n const subjectItems = deviceCert.tbsCertificate.subject;\n const matchedItem = subjectItems.find(({ algorithmOid }) => algorithmOid === oid);\n if (!matchedItem || !matchedItem.parameters) return null;\n\n return matchedItem.parameters.asn1.contents;\n};\n","import { type parseCertificate } from './x509certificate';\n\n// Check that this certificate is a valid Trezor CA.\n// KeyUsage must be present and allow certificate signing.\n// BasicConstraints must be present, have the cA flag and a pathLenConstraint.\n// Any unrecognized non-critical extension is allowed. Any unrecognized critical extension is disallowed.\nexport const validateCaCertExtensions = (\n cert: ReturnType<typeof parseCertificate>,\n pathLen: number\n) => {\n let hasKeyUsage = false;\n let hasBasicConstraints = false;\n cert.tbsCertificate.extensions.forEach(ext => {\n if (ext.key === 'keyUsage') {\n if (ext.keyCertSign !== '1') {\n throw new Error(`CA keyCertSign not set`);\n }\n hasKeyUsage = true;\n } else if (ext.key === 'basicConstraints') {\n if (!ext.cA) {\n throw new Error(`CA basicConstraints.cA not set`);\n }\n if (typeof ext.pathLenConstraint !== 'number') {\n throw new Error('CA basicConstraints.pathLenConstraint not set');\n }\n if (ext.pathLenConstraint < pathLen) {\n throw new Error('Issuer was not permitted to issue certificate');\n }\n hasBasicConstraints = true;\n } else if (ext.critical) {\n throw new Error(`Unknown critical extension ${ext.key} in CA certificate`);\n }\n });\n\n if (!hasKeyUsage || !hasBasicConstraints) {\n throw new Error(\n `CA missing extensions keyUsage: ${hasKeyUsage}, basicConstraints: ${hasBasicConstraints}`\n );\n }\n};\n","import { ed25519 } from '@noble/curves/ed25519';\nimport { p256 } from '@noble/curves/p256';\nimport { sha256 } from '@noble/hashes/sha256';\n\nimport { type AlgorithmName, fixSignature } from './x509certificate';\n\nexport type VerifySignature = (\n rawKey: Uint8Array,\n data: Uint8Array,\n signature: Uint8Array\n) => boolean;\n\n// P-256 (secp256r1) ECDSA over SHA-256. Hardware attestation signatures are NOT\n// guaranteed to be low-S normalized, so lowS must be disabled or valid signatures\n// would be rejected. `fixSignature` repairs the 1-in-256 malformed Optiga DER encoding.\nexport const verifySignatureP256: VerifySignature = (rawKey, data, signature) => {\n try {\n const sig = p256.Signature.fromDER(Buffer.from(fixSignature(signature)).toString('hex'));\n\n return p256.verify(sig.toBytes('compact'), sha256(data), rawKey, { lowS: false });\n } catch {\n // invalid inputs (e.g. an Ed25519 signature with a P-256 key) are an\n // unsuccessful verification, not a runtime error\n return false;\n }\n};\n\nexport const verifySignatureEd25519: VerifySignature = (rawKey, data, signature) => {\n try {\n return ed25519.verify(signature, data, rawKey);\n } catch {\n return false;\n }\n};\n\nexport const getVerifyFn = (algorithmName: AlgorithmName): VerifySignature => {\n if (algorithmName === 'P-256') return verifySignatureP256;\n if (algorithmName === 'Ed25519') return verifySignatureEd25519;\n throw new Error(`Unsupported signature algorithm: ${algorithmName}`);\n};\n","/**\n * Parse an x509 certificate returned from Trezor (AuthenticityProof) from DER format.\n * Ported from @trezor/device-authenticity (self-contained, no external deps).\n * inspired by https://blog.engelke.com/2014/10/21/web-crypto-and-x-509-certificates/\n */\n\ninterface Asn1 {\n cls: number; // ASN.1 class of the object.\n tag: number; // ASN.1 object type.\n structured: boolean; // Structured objects are encoded ASN.1 objects. Other objects (primitive) values depends on the tag and class\n byteLength: number; // value size\n contents: Uint8Array; // byte array containing the object value.\n raw: Uint8Array; // original byte array\n}\n\ntype OneOrTwoNumbers = `${number}` | `${number}.${number}`;\n// type that allows between 4 to 8 numbers to specify an OID\nexport type Oid = `${OneOrTwoNumbers}.${OneOrTwoNumbers}.${OneOrTwoNumbers}.${OneOrTwoNumbers}`;\n\n// algorithms supported to verify certificates and signatures\nexport type AlgorithmName = 'P-256' | 'Ed25519' | 'MLDSA44' | 'unknown';\n\ntype Extension =\n | {\n key: 'keyUsage';\n critical?: boolean;\n keyCertSign: '0' | '1';\n }\n | {\n key: 'basicConstraints';\n critical?: boolean;\n cA: boolean;\n pathLenConstraint?: number;\n }\n | (Asn1 & {\n key: Oid;\n critical?: boolean;\n });\n\nconst parseOidToAlgorithmName = (oid: Oid): AlgorithmName => {\n if (oid === '1.2.840.10045.4.3.2') return 'P-256'; // https://oid-base.com/get/1.2.840.10045.4.3.2\n if (oid === '1.3.101.112') return 'Ed25519'; // https://oid-base.com/get/1.3.101.112\n if (oid === '2.16.840.1.101.3.4.3.17') return 'MLDSA44'; // https://oid-base.com/get/2.16.840.1.101.3.4.3.17\n\n return 'unknown';\n};\n\nconst derToAsn1 = (byteArray: Uint8Array): Asn1 => {\n let position = 0;\n\n function getTag() {\n let tag = byteArray[0] & 0x1f;\n position += 1;\n if (tag === 0x1f) {\n tag = 0;\n while (byteArray[position] >= 0x80) {\n tag = tag * 128 + byteArray[position] - 0x80;\n position += 1;\n }\n tag = tag * 128 + byteArray[position] - 0x80;\n position += 1;\n }\n\n return tag;\n }\n\n function getLength() {\n let length = 0;\n\n if (byteArray[position] < 0x80) {\n length = byteArray[position];\n position += 1;\n } else {\n const numberOfDigits = byteArray[position] & 0x7f;\n position += 1;\n length = 0;\n for (let i = 0; i < numberOfDigits; i++) {\n length = length * 256 + byteArray[position];\n position += 1;\n }\n }\n\n return length;\n }\n\n const cls = (byteArray[0] & 0xc0) / 64;\n const structured = (byteArray[0] & 0x20) === 0x20;\n const tag = getTag();\n\n if (byteArray[position] === 0x80) {\n // DER forbids indefinite length encoding. You must use the definite length encoding (that is, with the length specified at the beginning).\n // https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/\n throw new Error('Unsupported length encoding');\n }\n\n const length = getLength(); // As encoded, which may be special value 0\n const byteLength = position + length;\n const contents = byteArray.subarray(position, byteLength);\n\n const raw = byteArray.subarray(0, byteLength); // May not be the whole input array\n\n return {\n cls,\n tag,\n structured,\n byteLength,\n contents,\n raw,\n };\n};\n\nconst derToAsn1List = (byteArray: Uint8Array) => {\n const result = [];\n let nextPosition = 0;\n while (nextPosition < byteArray.length) {\n const nextPiece = derToAsn1(byteArray.subarray(nextPosition));\n result.push(nextPiece);\n nextPosition += nextPiece.byteLength;\n }\n\n return result;\n};\n\nconst derBitStringValue = (byteArray: Uint8Array) => ({\n unusedBits: byteArray[0],\n bytes: byteArray.subarray(1),\n});\n\nconst parseSignatureValue = (asn1: Asn1) => {\n if (asn1.cls !== 0 || asn1.tag !== 3 || asn1.structured) {\n throw new Error('Bad signature value. Not a BIT STRING.');\n }\n\n return {\n asn1,\n bits: derBitStringValue(asn1.contents),\n };\n};\n\n// Optiga may produce a malformed signature with probability 1 in 256.\n// https://github.com/trezor/trezor-firmware/issues/3411\nexport const fixSignature = (byteArray: Uint8Array) => {\n const asn1 = derToAsn1(byteArray);\n if (asn1.cls !== 0 || asn1.tag !== 16 || !asn1.structured) {\n throw new Error('Bad signature. Not a SEQUENCE.');\n }\n\n const items = derToAsn1List(asn1.contents);\n let newLength = 0;\n const fixedItems = items.map(chunk => {\n // find first significant byte\n const index = chunk.contents.findIndex(value => value > 0x00);\n const data = chunk.contents.subarray(index);\n // According to the DER-encoding rules, the integers are supposed to be prefixed with a 0x00 byte\n // if **and only if** the most significant byte is >= 0x80\n const offset = data[0] >= 0x80 ? 1 : 0;\n // create replacement for chunk\n const chunkLength = data.length + offset;\n const newChunk = new Uint8Array(chunkLength + 2);\n // set first two bytes: original value and new length of the chunk\n newChunk.set([chunk.raw[0], chunkLength]);\n // optionally add 0\n if (offset > 0) {\n newChunk.set([0], 2);\n }\n // fill new chunk with data\n newChunk.set(data, 2 + offset);\n newLength += newChunk.length;\n\n return newChunk;\n });\n\n // create replacement for sequence object\n const signature = new Uint8Array(newLength + 2);\n // set two first bytes: original value and new length of all chunks\n signature.set([byteArray[0], newLength]);\n // fill new sequence with fixed items\n let signatureOffset = 2;\n fixedItems.forEach(item => {\n signature.set(item, signatureOffset);\n\n signatureOffset += item.length;\n });\n\n return signature;\n};\n\nconst derObjectIdentifierValue = (byteArray: Uint8Array) => {\n let oid = `${Math.floor(byteArray[0] / 40)}.${byteArray[0] % 40}`;\n let position = 1;\n while (position < byteArray.length) {\n let nextInteger = 0;\n while (byteArray[position] >= 0x80) {\n nextInteger = nextInteger * 0x80 + (byteArray[position] & 0x7f);\n position += 1;\n }\n nextInteger = nextInteger * 0x80 + byteArray[position];\n position += 1;\n oid += `.${nextInteger}`;\n }\n\n return oid as Oid;\n};\n\n/*\n * AlgorithmIdentifier ::= SEQUENCE {\n * algorithm OBJECT IDENTIFIER,\n * parameters ANY DEFINED BY algorithm OPTIONAL\n * }\n */\nconst parseAlgorithmIdentifier = (asn1: Asn1) => {\n if (asn1.cls !== 0 || asn1.tag !== 16 || !asn1.structured) {\n throw new Error('Bad algorithm identifier. Not a SEQUENCE.');\n }\n const pieces = derToAsn1List(asn1.contents);\n if (pieces.length > 2) {\n throw new Error('Bad algorithm identifier. Contains too many child objects.');\n }\n const encodedAlgorithm = pieces[0];\n if (encodedAlgorithm.cls !== 0 || encodedAlgorithm.tag !== 6 || encodedAlgorithm.structured) {\n throw new Error('Bad algorithm identifier. Does not begin with an OBJECT IDENTIFIER.');\n }\n const algorithmOid = derObjectIdentifierValue(encodedAlgorithm.contents);\n const algorithmName = parseOidToAlgorithmName(algorithmOid);\n\n return {\n asn1,\n algorithmOid,\n algorithmName,\n parameters: pieces.length === 2 ? { asn1: pieces[1] } : null,\n };\n};\n\n/*\n * Name ::= SEQUENCE {\n * algorithm OBJECT IDENTIFIER,\n * parameters ANY DEFINED BY algorithm OPTIONAL\n * }\n */\nexport const parseName = (asn1: Asn1) =>\n // SEQUENCE > SET > SEQUENCE\n derToAsn1List(asn1.contents).map(item => {\n const attrSet = derToAsn1(item.contents);\n\n return parseAlgorithmIdentifier(attrSet);\n });\n\n/*\n * SubjectPublicKeyInfo ::= SEQUENCE {\n * algorithm AlgorithmIdentifier,\n * subjectPublicKey BIT STRING\n * }\n */\nconst parseSubjectPublicKeyInfo = (asn1: Asn1) => {\n if (asn1.cls !== 0 || asn1.tag !== 16 || !asn1.structured) {\n throw new Error('Bad SPKI. Not a SEQUENCE.');\n }\n const pieces = derToAsn1List(asn1.contents);\n if (pieces.length !== 2) {\n throw new Error('Bad SubjectPublicKeyInfo. Wrong number of child objects.');\n }\n\n return {\n asn1,\n algorithm: parseAlgorithmIdentifier(pieces[0]),\n bits: derBitStringValue(pieces[1].contents),\n };\n};\n\n/*\n * Time ::= CHOICE {\n * utcTime UTCTime,\n * generalTime GeneralizedTime\n * }\n * NOTE: UTC and generalized times may both appear. (generalized > 2050)\n */\nconst parseUtcTime = (time: Asn1) => {\n // tag: 23 = UTCTime (YYMMDDhhmmZ);\n // tag: 24 = GeneralizedTime (YYYYMMDDhhmmZ)\n let offset = 4;\n let yearOffset = 0;\n if (time.tag === 23) {\n offset = 2;\n yearOffset = 2000;\n }\n const utc = Buffer.from(time.contents).toString();\n const year = yearOffset + Number(utc.substring(0, offset));\n const month = Number(utc.substring(offset, offset + 2)) - 1;\n const day = Number(utc.substring(offset + 2, offset + 4));\n const hour = Number(utc.substring(offset + 4, offset + 6));\n const minute = Number(utc.substring(offset + 6, offset + 8));\n\n const date = new Date();\n date.setUTCFullYear(year, month, day);\n date.setUTCHours(hour, minute, 0);\n\n return date;\n};\n\n/*\n * Validity ::= SEQUENCE {\n * from Time,\n * to Time\n * }\n */\nconst parseValidity = (asn1: Asn1) => {\n const [from, to] = derToAsn1List(asn1.contents);\n\n return {\n from: parseUtcTime(from),\n to: parseUtcTime(to),\n };\n};\n\n/*\n * https://www.alvestrand.no/objectid/2.5.29.html\n * Extensions ::= SEQUENCE {\n * extnID OBJECT IDENTIFIER,\n * critical BOOLEAN OPTIONAL,\n * extnValue OCTET STRING\n * }\n */\nconst parseExtensions = (data: Asn1) => {\n const asn1 = derToAsn1(data.contents);\n if (asn1.cls !== 0 || asn1.tag !== 16 || !asn1.structured) {\n throw new Error(\"This can't be a Extension. Wrong data type.\");\n }\n\n const readBoolean = (value?: Asn1) => {\n if (!value) return false;\n if (value.cls !== 0 || value.tag !== 1 || value.contents.length !== 1 || value.structured) {\n throw new Error(\"This can't be a boolean. Wrong data type.\");\n }\n if (![0x00, 0xff].includes(value.contents[0])) {\n throw new Error('Invalid boolean value.');\n }\n\n return value.contents[0] === 0xff;\n };\n\n const readBitString = (uint8Array: Uint8Array) => {\n const buffer = Buffer.from(uint8Array);\n const tag = buffer.readUInt8(0);\n if (tag !== 3) {\n throw new Error(\"This can't be a bit string. Wrong data type.\");\n }\n const length = buffer.readUInt8(1);\n const unusedBits = buffer.readUInt8(2);\n const bitStringBytes = buffer.subarray(3, 3 + length - 1); // -1 because the length includes the unusedBits byte\n const bitString = bitStringBytes.reduce(\n (str, byte) => str + byte.toString(2).padStart(8, '0'),\n ''\n );\n\n return bitString.slice(0, bitString.length - unusedBits) as '01';\n };\n\n const readInteger = (value?: Asn1) => {\n if (!value) return undefined;\n if (value.cls !== 0 || value.tag !== 2 || value.contents.length !== 1 || value.structured) {\n throw new Error(\"This can't be a integer. Wrong data type.\");\n }\n\n return Buffer.from(value.contents).readInt8();\n };\n\n const extensions: Extension[] = [];\n derToAsn1List(asn1.contents).forEach(item => {\n const [id, ...pieces] = derToAsn1List(item.contents);\n if (id.cls !== 0 || id.tag !== 6 || id.structured) {\n throw new Error('Bad extension. Does not begin with an OBJECT IDENTIFIER.');\n }\n\n const algorithm = derObjectIdentifierValue(id.contents);\n const critical = pieces.length > 1 ? readBoolean(pieces[0]) : false;\n const extnValue = pieces.length > 1 ? pieces[1] : pieces[0];\n if (extnValue.cls !== 0 || extnValue.tag !== 4 || extnValue.structured) {\n throw new Error(\"This can't be a octet string. Wrong data type.\");\n }\n\n if (algorithm === '2.5.29.15') {\n // https://www.alvestrand.no/objectid/2.5.29.15.html\n extensions.push({\n key: 'keyUsage',\n critical,\n keyCertSign: readBitString(extnValue.contents)[5] as '0' | '1',\n });\n } else if (algorithm === '2.5.29.19') {\n // https://www.alvestrand.no/objectid/2.5.29.19.html\n const fields = derToAsn1List(derToAsn1(extnValue.contents).contents);\n const ca = fields.length > 0 && fields[0].tag === 1 ? fields[0] : undefined;\n const len = fields.length > 0 && fields[0].tag === 2 ? fields[0] : fields[1];\n\n extensions.push({\n key: 'basicConstraints',\n critical,\n cA: readBoolean(ca),\n pathLenConstraint: readInteger(len),\n });\n } else {\n extensions.push({\n key: algorithm,\n critical,\n ...item,\n });\n }\n });\n\n return extensions;\n};\n\n/*\n * TBSCertificate ::= SEQUENCE {\n * version [0] EXPLICIT Version DEFAULT v1,\n * serialNumber CertificateSerialNumber,\n * signature AlgorithmIdentifier,\n * issuer Name[],\n * validity Validity,\n * subject Name[],\n * subjectPublicKeyInfo SubjectPublicKeyInfo,\n * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,\n * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,\n * extensions [3] EXPLICIT Extensions OPTIONAL\n * }\n */\nconst parseTBSCertificate = (asn1: Asn1) => {\n if (asn1.cls !== 0 || asn1.tag !== 16 || !asn1.structured) {\n throw new Error(\"This can't be a TBSCertificate. Wrong data type.\");\n }\n const pieces = derToAsn1List(asn1.contents);\n if (pieces.length < 7) {\n throw new Error('Bad TBS Certificate. There are fewer than the seven required children.');\n }\n\n return {\n asn1,\n version: pieces[0],\n serialNumber: pieces[1],\n signature: parseAlgorithmIdentifier(pieces[2]),\n issuer: pieces[3],\n validity: parseValidity(pieces[4]),\n subject: parseName(pieces[5]),\n subjectPublicKeyInfo: parseSubjectPublicKeyInfo(pieces[6]),\n extensions: parseExtensions(pieces[7]),\n };\n};\n\n/*\n * Certificate ::= SEQUENCE {\n * tbsCertificate TBSCertificate,\n * signatureAlgorithm AlgorithmIdentifier,\n * signatureValue BIT STRING\n * }\n */\nexport const parseCertificate = (byteArray: Uint8Array) => {\n const asn1 = derToAsn1(byteArray);\n if (asn1.cls !== 0 || asn1.tag !== 16 || !asn1.structured) {\n throw new Error(\"This can't be an X.509 certificate. Wrong data type.\");\n }\n const pieces = derToAsn1List(asn1.contents);\n if (pieces.length !== 3) {\n throw new Error('Certificate contains more than the three specified children.');\n }\n\n return {\n asn1,\n tbsCertificate: parseTBSCertificate(pieces[0]),\n signatureAlgorithm: parseAlgorithmIdentifier(pieces[1]),\n signatureValue: parseSignatureValue(pieces[2]),\n };\n};\n\nexport type ParsedCertificate = ReturnType<typeof parseCertificate>;\n","import { findSubjectContentByOid } from './findSubjectContentByOid';\nimport {\n type DeviceAuthenticityConfig,\n type VerifyAuthenticityProofParams,\n type VerifyAuthenticityProofResult,\n} from './types';\nimport { validateCaCertExtensions } from './validateCaCertExtensions';\nimport { getVerifyFn } from './verifySignatures';\nimport { type ParsedCertificate, parseCertificate } from './x509certificate';\n\nconst getChunkSize = (byteLength: number): Uint8Array => {\n if (byteLength < 0xfd) return Uint8Array.from([byteLength]);\n if (byteLength <= 0xffff) {\n const b = Buffer.alloc(3);\n b[0] = 0xfd;\n b.writeUInt16LE(byteLength, 1);\n\n return Uint8Array.from(b);\n }\n const b = Buffer.alloc(5);\n b[0] = 0xfe;\n b.writeUInt32LE(byteLength, 1);\n\n return Uint8Array.from(b);\n};\n\n// Compose the data against which the signatures are verified: each chunk is\n// prefixed with its compact-size length. Matches the firmware's signed payload.\nexport const prepareDeviceAuthenticityData = ({\n payload,\n prefix = 'AuthenticateDevice:',\n}: {\n payload: Buffer | Buffer[];\n prefix?: string;\n}): Uint8Array => {\n const chunks = [\n Uint8Array.from(Buffer.from(prefix)),\n ...(Array.isArray(payload) ? payload : [payload]).map(chunk => Uint8Array.from(chunk)),\n ];\n const framed = chunks.flatMap(chunk => [getChunkSize(chunk.byteLength), chunk]);\n const result = new Uint8Array(framed.reduce((total, chunk) => total + chunk.byteLength, 0));\n let offset = 0;\n for (const chunk of framed) {\n result.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return result;\n};\n\nconst getRootPubKeys = ({\n config,\n deviceModel,\n proofType,\n allowDebugKeys,\n}: {\n config: DeviceAuthenticityConfig;\n deviceModel: string;\n proofType: 'optiga' | 'tropic';\n allowDebugKeys?: boolean;\n}): string[] => {\n const modelConfig = config[deviceModel];\n if (!modelConfig || typeof modelConfig === 'number') {\n throw new Error(`Pubkeys for ${deviceModel} not found in config`);\n }\n const prod =\n proofType === 'optiga'\n ? modelConfig.rootPubKeysOptiga ?? []\n : modelConfig.rootPubKeysTropic ?? [];\n if (!allowDebugKeys) return prod;\n\n return [\n ...prod,\n ...(proofType === 'optiga'\n ? modelConfig.debug?.rootPubKeysOptiga ?? []\n : modelConfig.debug?.rootPubKeysTropic ?? []),\n ];\n};\n\nconst matchRootPubKeyToCertificate = ({\n cert,\n allRootPubKeys,\n}: {\n cert: ParsedCertificate;\n allRootPubKeys: string[];\n}): string | undefined => {\n const verifySignatureFn = getVerifyFn(cert.signatureAlgorithm.algorithmName);\n\n return allRootPubKeys.find(rootPubKey =>\n verifySignatureFn(\n Uint8Array.from(Buffer.from(rootPubKey, 'hex')),\n cert.tbsCertificate.asn1.raw,\n cert.signatureValue.bits.bytes\n )\n );\n};\n\n// Get internal model name from the device cert Subject (OID 2.5.4.3), e.g. 'T2B1'.\nconst parseModelFromDeviceCertSubject = (deviceCert: ParsedCertificate) => {\n const modelDescriptionBytes = findSubjectContentByOid(deviceCert, '2.5.4.3');\n if (modelDescriptionBytes === null) return null;\n\n // the whole string is e.g. 'T3W1 Trezor Safe 7', only the first 4 chars are the model\n return Buffer.from(modelDescriptionBytes.subarray(0, 4)).toString();\n};\n\n// Get serial number from the device cert Subject (OID 2.5.4.5). Only present on T3W1 and above.\nconst parseSerialNumberFromDeviceCert = (deviceCert: ParsedCertificate) => {\n const serialNumber = findSubjectContentByOid(deviceCert, '2.5.4.5');\n if (serialNumber === null) return undefined;\n\n return Buffer.from(serialNumber).toString('hex');\n};\n\n/**\n * Verifies the signing scheme: rootPubKey → CA pub key → device key → signature of prefixed challenge.\n * Returns the per-device attestation public key (`deviceCertPubKey`) on success.\n */\nexport const verifyAuthenticityProof = ({\n proofType,\n certificates,\n signature,\n signedData,\n deviceModel,\n config,\n allowDebugKeys,\n caPubKeyBlacklist = [],\n}: VerifyAuthenticityProofParams): VerifyAuthenticityProofResult => {\n const allRootPubKeys = getRootPubKeys({\n config,\n deviceModel,\n proofType,\n allowDebugKeys,\n });\n\n const parsedCertificates = certificates.map(c =>\n parseCertificate(new Uint8Array(Buffer.from(c, 'hex')))\n );\n\n const firstCertAlgName = parsedCertificates[0]?.signatureAlgorithm.algorithmName;\n // This mirrors Trezor Connect's production policy: Optiga uses P-256 and\n // Safe 7's additional Tropic proof uses Ed25519. MCU/ML-DSA is not a separate\n // client-side pass/fail condition.\n const expectedAlgorithmName = proofType === 'optiga' ? 'P-256' : 'Ed25519';\n if (firstCertAlgName !== expectedAlgorithmName) {\n return { valid: false, error: 'RESPONSE_MALFORMED' };\n }\n if (parsedCertificates.length !== 2) {\n return { valid: false, error: 'RESPONSE_MALFORMED' };\n }\n\n const [deviceCert, caCert] = parsedCertificates;\n const deviceCertPubKey = Buffer.from(\n deviceCert.tbsCertificate.subjectPublicKeyInfo.bits.bytes\n ).toString('hex');\n\n const deviceCertAlgName = deviceCert.signatureAlgorithm.algorithmName;\n const caCertAlgName = caCert.signatureAlgorithm.algorithmName;\n if (deviceCertAlgName !== caCertAlgName) {\n return { valid: false, deviceCertPubKey, error: 'RESPONSE_MALFORMED' };\n }\n\n validateCaCertExtensions(caCert, 0); // pathLenConstraint is always 0 (single CA cert, no chain)\n const verifySignatureFn = getVerifyFn(deviceCertAlgName);\n\n // 1) CA certificate must be signed by one of the trusted root pub keys.\n const caPubKeyBytes = caCert.tbsCertificate.subjectPublicKeyInfo.bits.bytes;\n const caPubKey = Buffer.from(caPubKeyBytes).toString('hex');\n const rootPubKeyMatch = matchRootPubKeyToCertificate({ allRootPubKeys, cert: caCert });\n if (rootPubKeyMatch === undefined) {\n return { valid: false, caPubKey, deviceCertPubKey, error: 'ROOT_PUBKEY_NOT_FOUND' };\n }\n // Revocation: reject a specific (e.g. leaked) intermediate CA even if it chains\n // to a trusted root.\n if (caPubKeyBlacklist.includes(caPubKey)) {\n return {\n valid: false,\n caPubKey,\n rootPubKey: rootPubKeyMatch,\n deviceCertPubKey,\n error: 'CA_PUBKEY_BLACKLISTED',\n };\n }\n // Both the CA and the device leaf must be currently valid: neither cert's\n // notBefore may be in the future, and neither may already be past notAfter.\n // An expired link in the chain must not still verify as trusted.\n const now = Date.now();\n const isWithinValidity = (validity: { from: Date; to: Date }) =>\n validity.from.getTime() <= now && now <= validity.to.getTime();\n if (\n !isWithinValidity(caCert.tbsCertificate.validity) ||\n !isWithinValidity(deviceCert.tbsCertificate.validity)\n ) {\n return {\n valid: false,\n caPubKey,\n rootPubKey: rootPubKeyMatch,\n deviceCertPubKey,\n error: 'CERTIFICATE_EXPIRED',\n };\n }\n\n // 2) Device model in the certificate must match the connected device.\n const modelFromSubject = parseModelFromDeviceCertSubject(deviceCert);\n if (modelFromSubject !== deviceModel) {\n return {\n valid: false,\n caPubKey,\n rootPubKey: rootPubKeyMatch,\n deviceCertPubKey,\n error: 'INVALID_DEVICE_MODEL',\n };\n }\n\n // 3) Device certificate must be signed by the CA pub key.\n const isDeviceCertValid = verifySignatureFn(\n Uint8Array.from(caPubKeyBytes),\n deviceCert.tbsCertificate.asn1.raw,\n deviceCert.signatureValue.bits.bytes\n );\n if (!isDeviceCertValid) {\n return {\n valid: false,\n caPubKey,\n rootPubKey: rootPubKeyMatch,\n deviceCertPubKey,\n error: 'INVALID_DEVICE_CERTIFICATE',\n };\n }\n\n // 4) The challenge signature must be produced by the device key.\n const isSignatureValid = verifySignatureFn(\n Uint8Array.from(deviceCert.tbsCertificate.subjectPublicKeyInfo.bits.bytes),\n signedData,\n Uint8Array.from(Buffer.from(signature, 'hex'))\n );\n if (!isSignatureValid) {\n return {\n valid: false,\n caPubKey,\n rootPubKey: rootPubKeyMatch,\n deviceCertPubKey,\n error: 'INVALID_DEVICE_SIGNATURE',\n };\n }\n\n return {\n valid: true,\n caPubKey,\n rootPubKey: rootPubKeyMatch,\n deviceCertPubKey,\n serialNumber: parseSerialNumberFromDeviceCert(deviceCert),\n };\n};\n","/**\n * SDK-global event bus. Per-runtime singleton; hosts forward events over their\n * own IPC when the adapter runs in an offscreen/background split.\n */\n\nexport type SdkLogEvent = {\n type: 'log';\n level: 'debug' | 'info' | 'warn' | 'error';\n /** Pre-stringified payload. */\n message: string;\n};\n\nexport type SdkEvent = SdkLogEvent;\n\nexport type SdkEventListener = (event: SdkEvent) => void;\n\nconst listeners = new Set<SdkEventListener>();\n\nexport function onSdkEvent(listener: SdkEventListener): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nexport function offSdkEvent(listener: SdkEventListener): void {\n listeners.delete(listener);\n}\n\nexport function emitSdkEvent(event: SdkEvent): void {\n if (listeners.size === 0) return;\n for (const listener of listeners) {\n try {\n listener(event);\n } catch {\n // Logging subscribers must not affect SDK execution.\n }\n }\n}\n\nexport function emitLog(level: SdkLogEvent['level'], ...args: unknown[]): void {\n if (listeners.size === 0) return;\n const message = args.map(a => (typeof a === 'string' ? a : safeStringify(a))).join(' ');\n emitSdkEvent({ type: 'log', level, message });\n}\n\nfunction safeStringify(value: unknown): string {\n if (value instanceof Error) {\n return value.stack ? `${value.message}\\n${value.stack}` : value.message;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n","import { emitLog } from './sdkEventBus';\n\nexport function debugLog(...args: unknown[]): void {\n emitLog('debug', ...args);\n}\n\nexport function debugError(...args: unknown[]): void {\n emitLog('error', ...args);\n}\n","import type { EvmEthereumDefinitions } from '@onekeyfe/hwk-adapter-core';\n\n/**\n * Opt-in helpers for fetching Trezor Ethereum \"definitions\" — the SatoshiLabs-\n * signed network/token metadata a Trezor device uses to render friendly names\n * for chains and tokens that are NOT built into firmware.\n *\n * IMPORTANT — the SDK signing methods (evmSignTransaction / evmGetAddress / …)\n * NEVER call these. A local hardware operation must not silently become a\n * third-party network request (see PR #824 review). The caller decides whether,\n * when, and where to fetch, then passes the result into the sign params as\n * `ethereumDefinitions`. When omitted, the device falls back to a generic\n * confirmation screen and signing still completes.\n *\n * Cross-process note: `fetchImpl` is a FUNCTION and cannot be serialized across\n * an IPC / postMessage boundary. Call these helpers in a network-capable\n * process and send only the resulting hex strings to the process that talks\n * to the device. Use `baseUrl` (a string) to route through your own\n * controlled proxy instead of hitting data.trezor.io directly.\n */\n\n/** Default upstream (Trezor). Override with `baseUrl` to use your own proxy. */\nexport const DEFAULT_ETHEREUM_DEFINITIONS_BASE_URL =\n 'https://data.trezor.io/firmware/eth-definitions';\n\n/** Timeout so a hung host can't stall the signing flow. */\nexport const DEFINITIONS_FETCH_TIMEOUT_MS = 20_000;\n\n/** Race a thunk against a deadline. Generic so it works with any FetchLike on\n * RN/Hermes; a timeout stops awaiting but does not cancel the socket. */\nexport async function runWithTimeout<T>(\n run: () => Promise<T>,\n timeoutMs: number = DEFINITIONS_FETCH_TIMEOUT_MS\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const deadline = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`definitions fetch timed out after ${timeoutMs}ms`)),\n timeoutMs\n );\n });\n try {\n return await Promise.race([run(), deadline]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nexport type FetchLike = (url: string) => Promise<{\n status: number;\n arrayBuffer(): Promise<ArrayBuffer>;\n}>;\n\nexport type FetchEthereumDefinitionsParams = {\n chainId?: number;\n slip44?: number;\n contractAddress?: string;\n baseUrl?: string;\n fetchImpl?: FetchLike;\n};\n\nexport type BuildEthereumDefinitionsForPathParams = {\n path: string;\n baseUrl?: string;\n fetchImpl?: FetchLike;\n};\n\nexport type BuildEthereumDefinitionsForSignTxParams = {\n path: string;\n chainId: number;\n to?: string;\n data?: string;\n baseUrl?: string;\n fetchImpl?: FetchLike;\n};\n\n/** Derive the SLIP-44 coin type (unhardened) from a BIP44-style path. */\nexport function getSlip44FromPath(path: string): number {\n const segments = path.trim().replace(/^m\\//i, '').split('/');\n const coinType = segments[1];\n const parsed = coinType === undefined ? NaN : Number(coinType.replace(/['hH]$/, ''));\n if (!Number.isFinite(parsed)) {\n throw new Error(`Cannot derive SLIP-44 coin type from path: ${path}`);\n }\n return parsed >= 0x80000000 ? parsed - 0x80000000 : parsed;\n}\n\nexport async function fetchEthereumDefinitions({\n chainId,\n slip44,\n contractAddress,\n baseUrl = DEFAULT_ETHEREUM_DEFINITIONS_BASE_URL,\n fetchImpl = globalThis.fetch,\n}: FetchEthereumDefinitionsParams): Promise<EvmEthereumDefinitions> {\n if (!chainId && !slip44) {\n throw new Error('fetchEthereumDefinitions requires chainId or slip44');\n }\n if (!fetchImpl) {\n return {};\n }\n\n const mode = chainId ? 'chain-id' : 'slip44';\n const id = chainId ?? slip44;\n const definitions: EvmEthereumDefinitions = {};\n\n const network = await fetchDefinitionHex(`${baseUrl}/${mode}/${id}/network.dat`, fetchImpl);\n if (network) {\n definitions.encodedNetwork = network;\n }\n\n if (contractAddress) {\n const token = await fetchDefinitionHex(\n `${baseUrl}/${mode}/${id}/token-${stripHexPrefix(contractAddress).toLowerCase()}.dat`,\n fetchImpl\n );\n if (token) {\n definitions.encodedToken = token;\n }\n }\n\n return definitions;\n}\n\nexport async function buildEthereumDefinitionsForPath({\n path,\n baseUrl,\n fetchImpl,\n}: BuildEthereumDefinitionsForPathParams): Promise<EvmEthereumDefinitions> {\n return fetchEthereumDefinitions({\n slip44: getSlip44FromPath(path),\n baseUrl,\n fetchImpl,\n });\n}\n\nexport async function buildEthereumDefinitionsForSignTx({\n path,\n chainId,\n to,\n data,\n baseUrl,\n fetchImpl,\n}: BuildEthereumDefinitionsForSignTxParams): Promise<EvmEthereumDefinitions | undefined> {\n const dataHex = stripHexPrefix(data ?? '');\n if (chainId === 1 && !dataHex) {\n return undefined;\n }\n return fetchEthereumDefinitions({\n chainId,\n slip44: getSlip44FromPath(path),\n contractAddress: dataHex && to ? to : undefined,\n baseUrl,\n fetchImpl,\n });\n}\n\nasync function fetchDefinitionHex(url: string, fetchImpl: FetchLike): Promise<string | undefined> {\n try {\n return await runWithTimeout(async () => {\n const response = await fetchImpl(url);\n if (response.status !== 200) {\n return undefined;\n }\n return arrayBufferToHex(await response.arrayBuffer());\n });\n } catch {\n return undefined;\n }\n}\n\nfunction arrayBufferToHex(value: ArrayBuffer): string {\n return Buffer.from(value).toString('hex');\n}\n\nfunction stripHexPrefix(value: string): string {\n return value.startsWith('0x') || value.startsWith('0X') ? value.slice(2) : value;\n}\n","import { runWithTimeout } from './ethereumDefinitions';\n\nimport type { FetchLike } from './ethereumDefinitions';\n\n/**\n * Opt-in helper for fetching a Trezor Solana token definition. Same contract as\n * the Ethereum helpers (see ./ethereumDefinitions): the SDK signing path NEVER\n * calls this — the caller fetches (through its own `baseUrl` proxy if desired)\n * and passes the bytes into `solSignTransaction` params as\n * `additionalInfo.encodedToken`. When omitted the device shows a generic token\n * confirmation screen and signing still completes.\n *\n * `fetchImpl` is a function and must not cross an IPC boundary — fetch in a\n * network-capable process and pass only the resulting hex string.\n */\n\n/** Default upstream (Trezor). Override with `baseUrl` to use your own proxy. */\nexport const DEFAULT_SOLANA_TOKEN_DEFINITION_BASE_URL =\n 'https://data.trezor.io/firmware/definitions/solana/token';\n\nexport type FetchSolanaTokenDefinitionParams = {\n tokenMint: string;\n baseUrl?: string;\n fetchImpl?: FetchLike;\n};\n\nexport async function fetchSolanaTokenDefinition({\n tokenMint,\n baseUrl = DEFAULT_SOLANA_TOKEN_DEFINITION_BASE_URL,\n fetchImpl = globalThis.fetch,\n}: FetchSolanaTokenDefinitionParams): Promise<string | undefined> {\n if (!fetchImpl) {\n return undefined;\n }\n try {\n return await runWithTimeout(async () => {\n const response = await fetchImpl(`${baseUrl}/${tokenMint}.dat`);\n if (response.status !== 200) {\n return undefined;\n }\n return Buffer.from(await response.arrayBuffer()).toString('hex');\n });\n } catch {\n return undefined;\n }\n}\n","export const TREZOR_SAFE_7_MODEL = 'T3W1';\n\nexport const TREZOR_BLE_SUPPORTED_MODELS = [TREZOR_SAFE_7_MODEL] as const;\n\nexport const TREZOR_BLE_PACKET_SIZE = 244;\n\nexport const TREZOR_BLE_UUIDS = {\n service: '8c000001-a59b-4d58-a9ad-073df69fa1b1',\n write: '8c000002-a59b-4d58-a9ad-073df69fa1b1',\n notify: '8c000003-a59b-4d58-a9ad-073df69fa1b1',\n push: '8c000004-a59b-4d58-a9ad-073df69fa1b1',\n batteryService: '0000180f-0000-1000-8000-00805f9b34fb',\n} as const;\n","import { TREZOR_BLE_SUPPORTED_MODELS, TREZOR_BLE_UUIDS } from '../constants';\n\nimport type { TrezorBleDescriptor } from '../types';\n\nexport function isTrezorSafe7BleName(name?: string): boolean {\n if (!name) return false;\n return /\\bTrezor\\b/i.test(name) && (/\\bSafe\\s*7\\b/i.test(name) || /\\bT3W1\\b/i.test(name));\n}\n\nexport function isTrezorBleSupportedModel(model?: string): boolean {\n if (!model) return false;\n const normalizedModel = model.trim().replace(/\\s+/g, ' ').toLowerCase();\n return (\n TREZOR_BLE_SUPPORTED_MODELS.some(\n supportedModel => supportedModel.toLowerCase() === normalizedModel\n ) ||\n normalizedModel === 'safe 7' ||\n normalizedModel === 'trezor safe 7'\n );\n}\n\nexport function isTrezorSafe7BleDescriptor(descriptor: TrezorBleDescriptor): boolean {\n return isTrezorBleDescriptor(descriptor);\n}\n\nexport function normalizeTrezorBleUuid(uuid: string): string {\n return uuid.replace(/-/g, '').toLowerCase();\n}\n\nexport function isTrezorBleServiceUuid(uuid: string): boolean {\n return normalizeTrezorBleUuid(uuid) === normalizeTrezorBleUuid(TREZOR_BLE_UUIDS.service);\n}\n\nexport function isTrezorBleDescriptor(descriptor: TrezorBleDescriptor): boolean {\n const serviceUuids = [\n ...(descriptor.serviceUUIDs ?? []),\n ...(descriptor.advertisedServiceUuids ?? []),\n ...(descriptor.serviceUuids ?? []),\n ];\n return serviceUuids.some(isTrezorBleServiceUuid);\n}\n\nexport function resolveTrezorBleConnectId(descriptor: TrezorBleDescriptor): string {\n return descriptor.id ?? descriptor.path ?? descriptor.deviceId ?? descriptor.name ?? '';\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/hardware-js-sdk/hardware-js-sdk/packages/hwk-trezor-adapter/dist/index.js","../src/adapter/TrezorAdapter.ts","../src/deviceAuthenticity/index.ts","../src/deviceAuthenticity/config.ts","../src/deviceAuthenticity/findSubjectContentByOid.ts","../src/deviceAuthenticity/validateCaCertExtensions.ts","../src/deviceAuthenticity/verifySignatures.ts","../src/deviceAuthenticity/x509certificate.ts","../src/deviceAuthenticity/verifyAuthenticityProof.ts","../src/utils/sdkEventBus.ts","../src/utils/debugLog.ts","../src/utils/ethereumDefinitions.ts","../src/utils/solanaTokenDefinition.ts","../src/constants.ts","../src/utils/bleIdentity.ts"],"names":[],"mappings":"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","file":"/home/runner/work/hardware-js-sdk/hardware-js-sdk/packages/hwk-trezor-adapter/dist/index.js","sourcesContent":[null,"import {\n DEVICE,\n DeviceJobQueue,\n HardwareErrorCode,\n TypedEventEmitter,\n UI_REQUEST,\n UI_RESPONSE,\n UiRequestRegistry,\n failure,\n rehydrateConnectorError,\n runAllNetworkGetAddress,\n success,\n} from '@onekeyfe/hwk-adapter-core';\nimport { randomBytes } from '@noble/hashes/utils';\n\nimport { authenticateDeviceFromProof } from '../deviceAuthenticity';\nimport { debugLog } from '../utils/debugLog';\n\nimport type { AuthenticityProof } from '../deviceAuthenticity';\nimport type {\n AllNetworkAddressParams,\n AllNetworkAddressResponse,\n AllNetworkGetAddressParams,\n AllNetworkMethodName,\n BtcAddress,\n BtcGetAddressParams,\n BtcGetPublicKeyParams,\n BtcPublicKey,\n BtcSignMsgParams,\n BtcSignPsbtParams,\n BtcSignTxParams,\n BtcSignature,\n BtcSignedPsbt,\n BtcSignedTx,\n ChainCapability,\n ChainForFingerprint,\n ConnectorCallResult,\n ConnectorDevice,\n ConnectorUiEvent,\n DeviceAuthenticityParams,\n DeviceAuthenticityResult,\n DeviceEventListener,\n DeviceInfo,\n EvmAddress,\n EvmGetAddressParams,\n EvmSignMsgParams,\n EvmSignTxTrezorParams,\n EvmSignTypedDataParams,\n EvmSignature,\n EvmSignedTx,\n HardwareEvent,\n HardwareEventMap,\n IBtcMethods,\n ICommonCallParams,\n IConnector,\n IDeviceManagerMethods,\n IEvmMethods,\n IHardwareWallet,\n ISolMethods,\n ITronMethods,\n Response,\n SolAddress,\n SolGetAddressParams,\n SolSignMsgParams,\n SolSignTxParams,\n SolSignature,\n SolSignedTx,\n TransportType,\n TrezorBrightnessParams,\n TrezorChangePinParams,\n TrezorDeviceSettingsParams,\n TronAddress,\n TronGetAddressParams,\n TronSignMsgParams,\n TronSignTxParams,\n TronSignature,\n TronSignedTx,\n UiResponseEvent,\n} from '@onekeyfe/hwk-adapter-core';\n\n// Valid HardwareErrorCode numbers. Used to reject foreign numeric `code`s (e.g.\n// a DOMException's `code: 20` on a WebUSB AbortError) that must NOT be trusted\n// as HardwareErrorCodes — otherwise they leak through as Unknown.\nconst KNOWN_HARDWARE_ERROR_CODES = new Set<number>(\n Object.values(HardwareErrorCode).filter((value): value is number => typeof value === 'number')\n);\n\ntype NullableCallArg<T> = T | null | undefined;\n\ntype TrezorPassphraseCallParams = {\n passphraseState?: string;\n useEmptyPassphrase?: boolean;\n};\n\ntype TrezorPassphraseRequestContext = {\n passphraseState?: string;\n useEmptyPassphrase?: boolean;\n};\n\ntype TrezorVerifiedPassphraseSession = {\n sessionId: string;\n protocol: 'thp' | 'v1';\n thpSessionId?: string;\n};\n\ntype TrezorCallParams<T> = (T & ICommonCallParams & TrezorPassphraseCallParams) | null | undefined;\n\ntype TrezorCommonParams = (ICommonCallParams & TrezorPassphraseCallParams) | null | undefined;\n\ntype AsyncMethodName<T> = Extract<\n {\n [K in keyof T]-?: NonNullable<T[K]> extends (\n ...args: infer _Args\n ) => Promise<Response<infer _Payload>>\n ? K\n : never;\n }[keyof T],\n string\n>;\n\ntype TrezorWalletMethods = IEvmMethods & IBtcMethods & ISolMethods & ITronMethods;\n\ntype TrezorWalletMethodName = AsyncMethodName<TrezorWalletMethods>;\n\ntype TrezorDeviceManagerMethodName = AsyncMethodName<IDeviceManagerMethods>;\n\ntype TrezorMethodName = TrezorWalletMethodName | TrezorDeviceManagerMethodName;\n\nconst TREZOR_BTC_NETWORK_COIN_MAP: Partial<Record<string, string>> = {\n btc: 'Bitcoin',\n bitcoin: 'Bitcoin',\n tbtc: 'Testnet',\n testnet: 'Testnet',\n sbtc: 'Signet',\n signet: 'Signet',\n ltc: 'Litecoin',\n litecoin: 'Litecoin',\n doge: 'Dogecoin',\n dogecoin: 'Dogecoin',\n bch: 'Bcash',\n bcash: 'Bcash',\n dash: 'Dash',\n};\n\nexport class TrezorAdapter implements IHardwareWallet {\n readonly vendor = 'trezor' as const;\n\n private readonly _connector: IConnector;\n\n private readonly _emitter = new TypedEventEmitter<HardwareEventMap>();\n\n private readonly _devices = new Map<string, DeviceInfo>();\n\n private readonly _sessions = new Map<string, string>();\n\n private readonly _connectingPromises = new Map<string, Promise<string>>();\n\n // connectIds disconnected mid-connect; the session they produce is torn down, not cached.\n private readonly _disconnectRequested = new Set<string>();\n\n private readonly _uiRegistry = new UiRequestRegistry();\n\n private readonly _passphraseRequestContextByConnectId = new Map<\n string,\n TrezorPassphraseRequestContext\n >();\n\n private readonly _verifiedPassphraseSessionsByConnectId = new Map<\n string,\n Map<string, TrezorVerifiedPassphraseSession>\n >();\n\n // Per-device queue with preemption + AbortSignal. Same-device chain calls\n // serialize (Trezor's protocol is single-request-response — interleaving\n // desyncs THP nonce). 'safe' read methods auto-cancel each other; 'confirm'\n // sign methods emit REQUEST_PREEMPTION so the consumer can decide.\n private readonly _jobQueue: DeviceJobQueue;\n\n constructor(connector: IConnector) {\n this._connector = connector;\n // Upstream DeviceJobQueue is now a global FIFO with no built-in\n // preemption — interruptibility lives at the application layer.\n this._jobQueue = new DeviceJobQueue();\n this._registerConnectorEvents();\n }\n\n private static _createDeviceBusyError(method: string): Error {\n // Our own queued request blocks this one → DeviceBusyInternal, not \"another app\".\n return Object.assign(new Error(`Trezor is busy with another request (${method})`), {\n code: HardwareErrorCode.DeviceBusyInternal,\n });\n }\n\n /**\n * Split a Trezor-only `passphraseState` routing hint off the chain params so\n * it never reaches the protobuf layer. The connector/firmware only sees the\n * real op fields; the adapter uses `passphraseState` to align the session.\n */\n private static _normalizeCallArgs(\n connectId: NullableCallArg<string>,\n deviceId: NullableCallArg<string>,\n params: NullableCallArg<unknown>\n ): {\n connectId: string;\n params: unknown;\n } {\n return {\n connectId: connectId || deviceId || '',\n params: params ?? {},\n };\n }\n\n private static _splitCommonParams(params: unknown): {\n passphraseState?: string;\n useEmptyPassphrase?: boolean;\n rest: unknown;\n } {\n if (params && typeof params === 'object') {\n const {\n passphraseState,\n autoInstallApp: _autoInstallApp,\n useEmptyPassphrase,\n ...rest\n } = params as Record<string, unknown>;\n return {\n passphraseState: typeof passphraseState === 'string' ? passphraseState : undefined,\n useEmptyPassphrase:\n typeof useEmptyPassphrase === 'boolean' ? useEmptyPassphrase : undefined,\n rest,\n };\n }\n return { rest: params ?? {} };\n }\n\n private static _withAllNetworkRequestCommonParams(\n item: AllNetworkAddressParams,\n commonParams: TrezorCommonParams\n ): AllNetworkAddressParams {\n const autoInstallApp = commonParams?.autoInstallApp;\n const passphraseState = commonParams?.passphraseState;\n const useEmptyPassphrase = commonParams?.useEmptyPassphrase;\n if (\n autoInstallApp === undefined &&\n passphraseState === undefined &&\n useEmptyPassphrase === undefined\n ) {\n return item;\n }\n return {\n ...item,\n ...(autoInstallApp !== undefined ? { autoInstallApp } : {}),\n ...(passphraseState !== undefined ? { passphraseState } : {}),\n ...(useEmptyPassphrase !== undefined ? { useEmptyPassphrase } : {}),\n };\n }\n\n // Keys are matched after normalizeLogKey, so snake_case/camelCase both hit.\n private static readonly _sensitiveLogKeys = new Set([\n 'credential',\n 'credentials',\n 'entropy',\n 'hoststatickey',\n 'mnemonic',\n 'mnemonics',\n 'passphrase',\n 'passphrasestate',\n 'password',\n 'pin',\n 'privatekey',\n 'seed',\n 'session',\n 'sessionid',\n 'trezorstaticpublickey',\n 'word',\n 'words',\n 'xprv',\n ]);\n\n private static _normalizeLogKey(key: string): string {\n return key.replace(/[_-]/g, '').toLowerCase();\n }\n\n // Mirrors hd-core's logBlockEvent: a signing method's body is the user's\n // transaction, so it is dropped wholesale; other methods redact by key.\n private static _sanitizeForLog(value: unknown, methodName?: string): unknown {\n if (methodName && /sign/i.test(methodName)) return '[redacted]';\n return TrezorAdapter._redactForLog(value, new WeakSet());\n }\n\n private static _redactForLog(value: unknown, seen: WeakSet<object>): unknown {\n if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {\n return `[BINARY:${value.byteLength}]`;\n }\n if (!value || typeof value !== 'object') return value;\n if (seen.has(value)) return '[CIRCULAR]';\n seen.add(value);\n const out = Array.isArray(value)\n ? value.map(item => TrezorAdapter._redactForLog(item, seen))\n : Object.fromEntries(\n Object.entries(value as Record<string, unknown>).map(([key, item]) => [\n key,\n TrezorAdapter._sensitiveLogKeys.has(TrezorAdapter._normalizeLogKey(key)) &&\n item !== null &&\n item !== undefined\n ? '[redacted]'\n : TrezorAdapter._redactForLog(item, seen),\n ])\n );\n seen.delete(value);\n return out;\n }\n\n private static _errorForLog(error: unknown): unknown {\n if (!error || typeof error !== 'object') return error;\n const typed = error as {\n code?: unknown;\n errorCode?: unknown;\n message?: unknown;\n name?: unknown;\n response?: unknown;\n statusCode?: unknown;\n };\n return TrezorAdapter._sanitizeForLog({\n name: typed.name,\n message: typed.message,\n code: typed.code,\n errorCode: typed.errorCode,\n statusCode: typed.statusCode,\n response: typed.response,\n });\n }\n\n private _setPassphraseRequestContext(\n connectId: string,\n context: TrezorPassphraseRequestContext\n ): () => void {\n const previous = this._passphraseRequestContextByConnectId.get(connectId);\n if (context.passphraseState !== undefined || context.useEmptyPassphrase !== undefined) {\n this._passphraseRequestContextByConnectId.set(connectId, context);\n } else {\n this._passphraseRequestContextByConnectId.delete(connectId);\n }\n return () => {\n if (previous) {\n this._passphraseRequestContextByConnectId.set(connectId, previous);\n } else {\n this._passphraseRequestContextByConnectId.delete(connectId);\n }\n };\n }\n\n private static _trezorFailureMessage(error: unknown): {\n code?: unknown;\n message?: unknown;\n } {\n const response = (error as { response?: unknown } | null | undefined)?.response;\n const message = (response as { message?: unknown } | null | undefined)?.message;\n if (!message || typeof message !== 'object') return {};\n const failureMessage = message as { code?: unknown; message?: unknown };\n return {\n code: failureMessage.code,\n message: failureMessage.message,\n };\n }\n\n private static _mapTrezorFailureCode(error: unknown): HardwareErrorCode | undefined {\n const directCode = (error as { code?: unknown } | null | undefined)?.code;\n if (directCode === 'Device_InitializeFailed') {\n return HardwareErrorCode.PinCancelled;\n }\n if (directCode === 'Failure_ActionCancelled') {\n return HardwareErrorCode.UserRejected;\n }\n if (directCode === 'ThpPairingRequired') {\n return HardwareErrorCode.ThpPairingRequired;\n }\n if (directCode === 'Failure_UnexpectedMessage') {\n return HardwareErrorCode.MethodNotSupported;\n }\n const message = (error as { message?: unknown } | null | undefined)?.message;\n if (\n typeof message === 'string' &&\n message.includes(\"Failed to execute 'transfer\") &&\n message.includes(\"on 'USBDevice'\")\n ) {\n return HardwareErrorCode.TransportError;\n }\n const failure = TrezorAdapter._trezorFailureMessage(error);\n if (failure.code === 'Failure_PinCancelled') {\n return HardwareErrorCode.PinCancelled;\n }\n if (failure.code === 'Failure_PinInvalid') {\n return HardwareErrorCode.PinInvalid;\n }\n if (failure.code === 'Failure_PinMismatch') {\n return HardwareErrorCode.PinMismatch;\n }\n if (failure.code === 'Failure_ActionCancelled') {\n return HardwareErrorCode.UserRejected;\n }\n if (failure.code === 'Failure_ProcessError' && failure.message === 'Unsupported script type') {\n return HardwareErrorCode.MethodNotSupported;\n }\n // Device rejected the derivation path (e.g. an index outside its allowed\n // range) — distinct path-specific surface, not a generic \"method\" error.\n if (failure.code === 'Failure_DataError' && failure.message === 'Forbidden key path') {\n return HardwareErrorCode.DevicePathForbidden;\n }\n // Match the stable setting name instead of the full firmware message.\n if (\n failure.code === 'Failure_DataError' &&\n typeof failure.message === 'string' &&\n failure.message.includes('PASSPHRASE_ALWAYS_ON_DEVICE')\n ) {\n return HardwareErrorCode.PassphraseAlwaysOnDevice;\n }\n // Firmware can't process this request (e.g. EIP-712 typed data the device\n // firmware doesn't support) → surface as \"method not supported\".\n if (failure.code === 'Failure_FirmwareError') {\n return HardwareErrorCode.MethodNotSupported;\n }\n // Device firmware doesn't recognize the message (e.g. an older device asked\n // for a coin/app it lacks) → \"method not supported\", not a generic unknown.\n if (failure.code === 'Failure_UnexpectedMessage') {\n return HardwareErrorCode.MethodNotSupported;\n }\n if (failure.code === 'Failure_NotInitialized') {\n return HardwareErrorCode.DeviceNotInitialized;\n }\n // Device busy with its own op, not another app → DeviceBusyInternal.\n if (failure.code === 'Failure_Busy' || failure.code === 'Failure_InProgress') {\n return HardwareErrorCode.DeviceBusyInternal;\n }\n return undefined;\n }\n\n /**\n * Whether a device-side error code means the THP application session we\n * selected is gone (the device evicted it from its limited slot pool). The\n * caller drops the stale cache entry and recreates from scratch.\n *\n * NOTE: the exact eviction code still needs real-device confirmation — match\n * conservatively so a genuine error isn't mistaken for an evicted session\n * (which would silently re-prompt the passphrase).\n */\n private static _isStaleSessionError(code: unknown): boolean {\n if (typeof code !== 'string') return false;\n return (\n code === 'ThpUnallocatedChannel' ||\n /invalid.?session|unallocated.?session|session.?not.?found/i.test(code)\n );\n }\n\n private static _errorCode(error: unknown): unknown {\n return (error as { code?: unknown }).code ?? TrezorAdapter._trezorFailureMessage(error).code;\n }\n\n private static _isConnectionUnavailableError(error: unknown): boolean {\n const { code } = error as { code?: unknown };\n return (\n code === HardwareErrorCode.DeviceNotFound || code === HardwareErrorCode.DeviceDisconnected\n );\n }\n\n private static _isRecoverableProtocolResidueError(error: unknown): boolean {\n const protocolError = error as {\n name?: unknown;\n code?: unknown;\n message?: unknown;\n };\n return (\n protocolError.name === 'TrezorProtocolError' &&\n protocolError.code === 'Malformed protocol format' &&\n protocolError.message === 'Malformed protocol format'\n );\n }\n\n /**\n * Race a promise against an abort signal. Copied from LedgerAdapter:\n * IConnector.call() can't actually be cancelled at the protocol level, but\n * the caller gets the abort immediately and the underlying work completes\n * uselessly in the background.\n */\n private static _abortable<T>(signal: AbortSignal, promise: Promise<T>): Promise<T> {\n if (signal.aborted) {\n return Promise.reject(\n (signal as AbortSignal & { reason?: unknown }).reason ?? new Error('Aborted')\n );\n }\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n reject((signal as AbortSignal & { reason?: unknown }).reason ?? new Error('Aborted'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n promise.then(\n value => {\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n err => {\n signal.removeEventListener('abort', onAbort);\n reject(err);\n }\n );\n });\n }\n\n private async _connectSession(connectId: string, signal?: AbortSignal): Promise<string> {\n const connectOnce = async () => {\n const connectPromise = this._connector.connect(connectId);\n if (!signal) return connectPromise;\n try {\n return await TrezorAdapter._abortable(signal, connectPromise);\n } catch (error) {\n // _abortable rejected (abort/failure) but the underlying connect keeps\n // running — tear down any session it still resolves so it isn't orphaned.\n void connectPromise.then(\n orphan => this._connector.disconnect(orphan.sessionId).catch(() => undefined),\n () => undefined\n );\n throw error;\n }\n };\n let session;\n try {\n session = await connectOnce();\n } catch (error) {\n if (signal?.aborted || !TrezorAdapter._isRecoverableProtocolResidueError(error)) {\n throw error;\n }\n // An interrupted THP handshake may leave exactly one v2 frame queued on\n // the shared USB pipe. The connector intentionally drains it as a loud\n // v1 malformed-frame failure; retry once now that the residue is gone.\n debugLog('[TrezorAdapter] retrying connect after draining a stale THP frame', {\n connectId,\n });\n session = await connectOnce();\n }\n // disconnectDevice() ran mid-connect: tear down the now-unwanted session\n // instead of caching it (else it leaks a limited THP slot).\n if (this._disconnectRequested.delete(connectId)) {\n await this._connector.disconnect(session.sessionId).catch(() => undefined);\n throw Object.assign(new Error('Trezor connection aborted'), {\n code: HardwareErrorCode.UserAborted,\n });\n }\n this._sessions.set(connectId, session.sessionId);\n this._verifiedPassphraseSessionsByConnectId.delete(connectId);\n this._devices.set(connectId, session.deviceInfo);\n return session.sessionId;\n }\n\n get activeTransport(): TransportType | null {\n return this._connector.connectionType;\n }\n\n async init(_config?: unknown): Promise<void> {}\n\n async dispose(): Promise<void> {\n this._unregisterConnectorEvents();\n this._uiRegistry.reset();\n this._jobQueue.clear();\n this._connector.reset();\n this._devices.clear();\n this._sessions.clear();\n this._verifiedPassphraseSessionsByConnectId.clear();\n this._connectingPromises.clear();\n this._emitter.removeAllListeners();\n }\n\n /**\n * Clear cached state without tearing down the adapter or unregistering\n * listeners. Mirrors LedgerAdapter.resetState() — useful for retry-after-\n * error flows. Aborts active jobs and releases UI waits.\n */\n resetState(): void {\n this._uiRegistry.reset();\n this._jobQueue.clear();\n this._devices.clear();\n this._sessions.clear();\n this._verifiedPassphraseSessionsByConnectId.clear();\n this._connectingPromises.clear();\n }\n\n getAvailableTransports(): TransportType[] {\n return this.activeTransport ? [this.activeTransport] : [];\n }\n\n async switchTransport(_type: TransportType): Promise<void> {}\n\n async searchDevices(options?: { waitForAllTransports?: boolean }): Promise<DeviceInfo[]> {\n await this._ensureDevicePermission();\n const scanned = await this._connector.searchDevices(\n options?.waitForAllTransports ? { waitForAll: true } : undefined\n );\n const scannedIds = new Set(scanned.map(d => d.connectId));\n // A rescan that misses a currently-connected device shouldn't evict it —\n // otherwise getDeviceInfo would return DeviceNotFound for an active session.\n for (const connectId of [...this._devices.keys()]) {\n if (!scannedIds.has(connectId) && !this._sessions.has(connectId)) {\n this._devices.delete(connectId);\n }\n }\n for (const device of scanned) {\n const info = this._connectorDeviceToDeviceInfo(device);\n this._devices.set(info.connectId, info);\n }\n return Array.from(this._devices.values());\n }\n\n async connectDevice(connectId: string): Promise<Response<string>> {\n try {\n await this._ensureDevicePermission(connectId);\n await this._ensureSession(connectId);\n return success(connectId);\n } catch (error) {\n return this._errorToFailure(error);\n }\n }\n\n async disconnectDevice(connectId: string): Promise<void> {\n const connecting = this._connectingPromises.has(connectId);\n this._connectingPromises.delete(connectId);\n const sessionId = this._sessions.get(connectId);\n if (!sessionId) {\n // Connect may be in flight (session not recorded yet) — mark it so\n // _connectSession tears down the session it produces instead of caching it.\n if (connecting) this._disconnectRequested.add(connectId);\n return;\n }\n this._sessions.delete(connectId);\n this._verifiedPassphraseSessionsByConnectId.delete(connectId);\n await this._connector.disconnect(sessionId);\n }\n\n async getDeviceInfo(connectId: string, deviceId: string): Promise<Response<DeviceInfo>> {\n const device =\n this._devices.get(connectId) ??\n Array.from(this._devices.values()).find(item => item.deviceId === deviceId);\n if (!device) return failure(HardwareErrorCode.DeviceNotFound, 'Trezor device not found');\n return success(device);\n }\n\n getSupportedChains(): ChainCapability[] {\n // Keep in sync with TrezorConnectorBase.call()'s switch.\n // Chain inclusion means \"at least one method wired\" — per-method gaps:\n // sol: signMessage is explicitly NotSupported (firmware doesn't ship it).\n // tron: signMessage same. signTransaction uses the multi-step\n // TronContractRequest flow.\n return ['btc', 'evm', 'sol', 'tron'];\n }\n\n async allNetworkGetAddress(\n connectId: string,\n deviceId: string,\n params: AllNetworkGetAddressParams\n ): Promise<Response<AllNetworkAddressResponse[]>> {\n let liveDeviceId = '';\n const topLevelFailureIndexes = new Set<number>();\n const isSingleNetworkBundle = params.bundle.every(\n item => item.network === params.bundle[0]?.network\n );\n const getLiveDeviceId = async (): Promise<Response<string>> => {\n if (liveDeviceId) return success(liveDeviceId);\n const deviceIdResponse = await this._getTrezorDeviceId(connectId);\n if (deviceIdResponse.success) {\n liveDeviceId = deviceIdResponse.payload;\n }\n return deviceIdResponse;\n };\n\n return runAllNetworkGetAddress({\n connectId,\n deviceId,\n params,\n normalizeItem: TrezorAdapter._normalizeAllNetworkItem,\n callItem: async ({ method, item, index }) => {\n const expectedDeviceId = TrezorAdapter._getItemDeviceId(item) ?? deviceId;\n if (expectedDeviceId) {\n const fingerprint = await getLiveDeviceId();\n if (!fingerprint.success) {\n topLevelFailureIndexes.add(index);\n return fingerprint;\n }\n if (expectedDeviceId !== fingerprint.payload) {\n topLevelFailureIndexes.add(index);\n return failure(\n HardwareErrorCode.DeviceMismatch,\n `Wrong device: expected ${expectedDeviceId}, got ${fingerprint.payload}`,\n { expected: expectedDeviceId, actual: fingerprint.payload }\n );\n }\n }\n const callItem = TrezorAdapter._withAllNetworkRequestCommonParams(item, params);\n return this._callAllNetworkMethod(connectId, method, callItem);\n },\n attachIdentity: async ({ item, chain, payload }) => {\n if (!liveDeviceId) {\n const fingerprint = await getLiveDeviceId();\n if (!fingerprint.success) {\n return { ...item, success: false, payload: fingerprint.payload };\n }\n }\n return {\n ...item,\n success: true,\n payload: {\n ...payload,\n deviceIdentity: {\n vendor: 'trezor',\n type: 'deviceId',\n value: liveDeviceId,\n },\n chainFingerprint: liveDeviceId,\n chainFingerprintChain: chain,\n },\n };\n },\n // One named boolean per abort reason.\n shouldAbortBundle: (response, { index }) => {\n const isSessionLevelFailure = topLevelFailureIndexes.has(index);\n // Mixed bundles must not abort — other chains can still derive.\n const isWholeChainForbidden =\n isSingleNetworkBundle && response.payload?.code === HardwareErrorCode.DevicePathForbidden;\n const isPassphrasePolicyFailure =\n response.payload?.code === HardwareErrorCode.PassphraseAlwaysOnDevice;\n return isSessionLevelFailure || isWholeChainForbidden || isPassphrasePolicyFailure;\n },\n });\n }\n\n getFeatures(connectId: string): Promise<Response<Record<string, unknown>>> {\n return this._callDeviceManagerMethod<Record<string, unknown>>('getFeatures', connectId, {});\n }\n\n /**\n * Device authenticity attestation. Sends a host-generated challenge, has the\n * device sign it with its secure-element key, then verifies the returned\n * certificate chain up to a trusted Trezor root CA. On success returns a\n * `deviceId` that uniquely identifies the physical device, survives wipe, and\n * cannot be forged from a seed. Only trust the result when `verified` is true\n * (and `usedDebugKey` is false in production).\n *\n * Requires a secure-element model (Safe 3 = T2B1/T3B1, Safe 5 = T3T1, T3W1)\n * and an on-device confirmation. Older models (T1B1, T2T1) will fail.\n *\n * `dangerouslyAllowDebugKeys` accepts simulator / development root keys — NEVER\n * enable it in a production accounting flow, it lets an emulator mint any id.\n */\n async verifyDeviceAuthenticity(\n connectId: string,\n params: DeviceAuthenticityParams = {}\n ): Promise<Response<DeviceAuthenticityResult>> {\n if (params.challenge && params.dangerouslyAllowDebugKeys) {\n return failure(\n HardwareErrorCode.InvalidParams,\n 'Debug attestation roots cannot be used with a server challenge'\n );\n }\n if (params.challenge && !/^[0-9a-fA-F]{64}$/.test(params.challenge)) {\n return failure(\n HardwareErrorCode.InvalidParams,\n 'Device authenticity challenge must be exactly 32 bytes encoded as hex'\n );\n }\n\n const featuresRes = await this.getFeatures(connectId);\n if (!featuresRes.success) return featuresRes;\n\n const internalModel = (featuresRes.payload as { internal_model?: string }).internal_model;\n if (!internalModel) {\n return failure(HardwareErrorCode.UnknownError, 'Device internal_model unavailable');\n }\n\n const challengeHex =\n params.challenge?.toLowerCase() ?? Buffer.from(randomBytes(32)).toString('hex');\n const challenge = Buffer.from(challengeHex, 'hex');\n const proofRes = await this._callDeviceManagerMethod<Record<string, unknown>>(\n 'authenticateDevice',\n connectId,\n { challenge: challengeHex }\n );\n if (!proofRes.success) return proofRes;\n\n const proof = proofRes.payload as unknown as AuthenticityProof;\n const verification = authenticateDeviceFromProof({\n proof,\n challenge,\n deviceModel: internalModel,\n allowDebugKeys: params.dangerouslyAllowDebugKeys,\n });\n\n return success({\n ...verification,\n vendor: 'trezor' as const,\n trezorProof: {\n challenge: challengeHex,\n deviceModel: internalModel,\n proof,\n },\n });\n }\n\n deviceSettings(\n connectId: string,\n params: TrezorDeviceSettingsParams\n ): Promise<Response<Record<string, unknown>>> {\n return this._callDeviceManagerMethod<Record<string, unknown>>(\n 'deviceSettings',\n connectId,\n params\n );\n }\n\n setBrightness(\n connectId: string,\n params: TrezorBrightnessParams = {}\n ): Promise<Response<Record<string, unknown>>> {\n return this._callDeviceManagerMethod<Record<string, unknown>>(\n 'setBrightness',\n connectId,\n params\n );\n }\n\n changePin(\n connectId: string,\n params: TrezorChangePinParams = {}\n ): Promise<Response<Record<string, unknown>>> {\n return this._callDeviceManagerMethod<Record<string, unknown>>('changePin', connectId, params);\n }\n\n wipeDevice(connectId: string): Promise<Response<Record<string, unknown>>> {\n return this._callDeviceManagerMethod<Record<string, unknown>>('wipeDevice', connectId, {});\n }\n\n private async _getTrezorDeviceId(connectId: string): Promise<Response<string>> {\n const features = await this.getFeatures(connectId);\n if (!features.success) return features;\n const liveDeviceId = features.payload.device_id;\n if (typeof liveDeviceId !== 'string' || liveDeviceId.length === 0) {\n return failure(HardwareErrorCode.UnknownError, 'Trezor features did not include a device_id');\n }\n return success(liveDeviceId);\n }\n\n cancel(connectId?: string): void {\n const userAbortReason = Object.assign(new Error('User aborted operation'), {\n code: HardwareErrorCode.UserAborted,\n });\n // Release adapter-level UI waits (preemption) and connector-level ones\n // (THP pairing / PIN matrix) — a single CANCEL clears whichever is open.\n this._uiRegistry.cancel();\n this._connector.uiResponse({ type: UI_RESPONSE.CANCEL });\n if (connectId) {\n this._jobQueue.forceCancelActive(connectId, userAbortReason);\n void this._connector.cancel(this._sessions.get(connectId) ?? connectId);\n return;\n }\n for (const cid of this._sessions.keys()) {\n this._jobQueue.forceCancelActive(cid, userAbortReason);\n }\n for (const sessionId of this._sessions.values()) {\n void this._connector.cancel(sessionId);\n }\n }\n\n /**\n * Routes to BOTH the adapter's local UI registry (preemption decisions,\n * future SELECT_DEVICE / DEVICE_PERMISSION) AND the connector's registry\n * (THP pairing tag, PIN matrix). Each side ignores types it isn't waiting\n * for, so double-routing is harmless.\n */\n uiResponse(response: UiResponseEvent): void {\n this._uiRegistry.resolve(response.type, response.payload);\n this._connector.uiResponse(response);\n }\n\n /**\n * Returns the Trezor `device_id` from Features — a random 12-byte value the\n * device stores and regenerates on `wipe_device`. It identifies the PHYSICAL\n * DEVICE, not a seed/wallet: it is shared across passphrase wallets and\n * changes after wipe/restore, so it suits device-match checks but must NOT be\n * used as a wallet fingerprint (use `passphraseState` for wallet identity).\n * Unlike Ledger (which hashes a per-chain address), it is chain-global, so\n * `chain` is accepted for API parity but ignored.\n */\n async getChainFingerprint(\n connectId: string,\n _deviceId: string,\n _chain: ChainForFingerprint\n ): Promise<Response<string>> {\n return this._getTrezorDeviceId(connectId);\n }\n\n /**\n * Resolve the active passphrase wallet identity (`passphraseState`).\n * See {@link IHardwareWallet.getPassphraseState} for the discover/verify\n * semantics. Returns `null` for a standard wallet (no passphrase protection) —\n * there's nothing to pin. Serialized through the per-device job queue so it\n * can't interleave with a chain call on the same device (which would desync\n * the THP session selection).\n */\n getPassphraseState(\n connectId: string,\n passphraseState?: string\n ): Promise<Response<string | null>> {\n return this._jobQueue\n .enqueue(\n connectId,\n async signal => {\n const restorePassphraseRequestContext = this._setPassphraseRequestContext(connectId, {\n passphraseState,\n });\n try {\n return await this._resolvePassphraseState(connectId, passphraseState, signal);\n } finally {\n restorePassphraseRequestContext();\n }\n },\n {\n label: 'getPassphraseState',\n rejectIfBusy: true,\n busyError: TrezorAdapter._createDeviceBusyError('getPassphraseState'),\n }\n )\n .catch(error => this._errorToFailure(error));\n }\n\n evmGetAddress(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<EvmGetAddressParams>\n ) {\n return this._callWalletMethod<EvmAddress>('evmGetAddress', connectId, deviceId, params);\n }\n\n evmSignTransaction(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<EvmSignTxTrezorParams>\n ) {\n return this._callWalletMethod<EvmSignedTx>('evmSignTransaction', connectId, deviceId, params);\n }\n\n evmSignMessage(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<EvmSignMsgParams>\n ) {\n return this._callWalletMethod<EvmSignature>('evmSignMessage', connectId, deviceId, params);\n }\n\n evmSignTypedData(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<EvmSignTypedDataParams>\n ) {\n return this._callWalletMethod<EvmSignature>('evmSignTypedData', connectId, deviceId, params);\n }\n\n btcGetAddress(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<BtcGetAddressParams>\n ) {\n return this._callWalletMethod<BtcAddress>('btcGetAddress', connectId, deviceId, params);\n }\n\n btcGetPublicKey(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<BtcGetPublicKeyParams>\n ) {\n return this._callWalletMethod<BtcPublicKey>('btcGetPublicKey', connectId, deviceId, params);\n }\n\n btcSignTransaction(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<BtcSignTxParams>\n ) {\n return this._callWalletMethod<BtcSignedTx>('btcSignTransaction', connectId, deviceId, params);\n }\n\n btcSignPsbt(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<BtcSignPsbtParams>\n ) {\n return this._callWalletMethod<BtcSignedPsbt>('btcSignPsbt', connectId, deviceId, params);\n }\n\n btcSignMessage(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<BtcSignMsgParams>\n ) {\n return this._callWalletMethod<BtcSignature>('btcSignMessage', connectId, deviceId, params);\n }\n\n btcGetMasterFingerprint(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCommonParams\n ) {\n return this._callWalletMethod<{ masterFingerprint: string }>(\n 'btcGetMasterFingerprint',\n connectId,\n deviceId,\n params\n );\n }\n\n solGetAddress(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<SolGetAddressParams>\n ) {\n return this._callWalletMethod<SolAddress>('solGetAddress', connectId, deviceId, params);\n }\n\n solSignTransaction(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<SolSignTxParams>\n ) {\n return this._callWalletMethod<SolSignedTx>('solSignTransaction', connectId, deviceId, params);\n }\n\n solSignMessage(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<SolSignMsgParams>\n ) {\n return this._callWalletMethod<SolSignature>('solSignMessage', connectId, deviceId, params);\n }\n\n tronGetAddress(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<TronGetAddressParams>\n ) {\n return this._callWalletMethod<TronAddress>('tronGetAddress', connectId, deviceId, params);\n }\n\n tronSignTransaction(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<TronSignTxParams>\n ) {\n return this._callWalletMethod<TronSignedTx>('tronSignTransaction', connectId, deviceId, params);\n }\n\n tronSignMessage(\n connectId?: string | null,\n deviceId?: string | null,\n params?: TrezorCallParams<TronSignMsgParams>\n ) {\n return this._callWalletMethod<TronSignature>('tronSignMessage', connectId, deviceId, params);\n }\n\n private static _unwrapConnectorResult(result: unknown): unknown {\n if (\n result &&\n typeof result === 'object' &&\n 'success' in result &&\n (((result as ConnectorCallResult).success === true && 'payload' in result) ||\n ((result as ConnectorCallResult).success === false && 'error' in result))\n ) {\n const connectorResult = result as ConnectorCallResult;\n if (connectorResult.success) return connectorResult.payload;\n throw rehydrateConnectorError(connectorResult.error);\n }\n return result;\n }\n\n private static async _callConnector(\n connector: IConnector,\n sessionId: string,\n method: string,\n params: unknown,\n signal?: AbortSignal\n ): Promise<unknown> {\n const promise = connector.call(sessionId, method, params);\n const result = signal ? await TrezorAdapter._abortable(signal, promise) : await promise;\n return TrezorAdapter._unwrapConnectorResult(result);\n }\n\n private static _normalizeAllNetworkItem(\n method: AllNetworkMethodName,\n item: AllNetworkAddressParams\n ): AllNetworkAddressParams {\n if (method !== 'btcGetAddress' && method !== 'btcGetPublicKey') {\n return item;\n }\n\n const itemWithCoin = item as AllNetworkAddressParams & { coin?: unknown };\n if (itemWithCoin.coin) {\n return item;\n }\n\n const network = item.network.toLowerCase();\n return {\n ...item,\n coin: TREZOR_BTC_NETWORK_COIN_MAP[network] ?? item.network,\n };\n }\n\n private static _getItemDeviceId(item: AllNetworkAddressParams): string | undefined {\n const { deviceId } = item as { deviceId?: unknown };\n return typeof deviceId === 'string' && deviceId.length > 0 ? deviceId : undefined;\n }\n\n private _callAllNetworkMethod(\n connectId: string,\n method: AllNetworkMethodName,\n item: AllNetworkAddressParams\n ): Promise<Response<unknown>> {\n switch (method) {\n case 'evmGetAddress':\n return this.evmGetAddress(connectId, '', item);\n case 'btcGetAddress':\n return this.btcGetAddress(connectId, '', item);\n case 'btcGetPublicKey':\n return this.btcGetPublicKey(connectId, '', item);\n case 'solGetAddress':\n return this.solGetAddress(connectId, '', item);\n case 'tronGetAddress':\n return this.tronGetAddress(connectId, '', item);\n default:\n return Promise.resolve(\n failure(\n HardwareErrorCode.InvalidParams,\n `Unsupported allNetwork method: ${String(method)}`\n )\n );\n }\n }\n\n on<K extends keyof HardwareEventMap>(\n event: K,\n listener: (event: HardwareEventMap[K]) => void\n ): void;\n\n on(event: string, listener: DeviceEventListener): void;\n\n on(event: string, listener: (event: HardwareEvent) => void): void {\n this._emitter.on(event, listener);\n }\n\n off<K extends keyof HardwareEventMap>(\n event: K,\n listener: (event: HardwareEventMap[K]) => void\n ): void;\n\n off(event: string, listener: DeviceEventListener): void;\n\n off(event: string, listener: (event: HardwareEvent) => void): void {\n this._emitter.off(event, listener);\n }\n\n private _callWalletMethod<T>(\n methodName: TrezorWalletMethodName,\n connectId: NullableCallArg<string>,\n deviceId: NullableCallArg<string>,\n params: NullableCallArg<unknown>\n ): Promise<Response<T>> {\n return this._callMethod<T>(methodName, connectId, deviceId, params, true);\n }\n\n private _callDeviceManagerMethod<T>(\n methodName: TrezorDeviceManagerMethodName,\n connectId: NullableCallArg<string>,\n params: NullableCallArg<unknown>\n ): Promise<Response<T>> {\n return this._callMethod<T>(methodName, connectId, undefined, params, false);\n }\n\n private async _callMethod<T>(\n methodName: TrezorMethodName,\n connectId: NullableCallArg<string>,\n deviceId: NullableCallArg<string>,\n params: NullableCallArg<unknown>,\n requiresWalletIntent: boolean\n ): Promise<Response<T>> {\n const call = TrezorAdapter._normalizeCallArgs(connectId, deviceId, params);\n debugLog('[TrezorAdapter][REQ]', {\n method: methodName,\n connectId: call.connectId || '(empty)',\n deviceId: deviceId || undefined,\n params: TrezorAdapter._sanitizeForLog(call.params, methodName),\n });\n try {\n const response = await this._jobQueue.enqueue(\n call.connectId,\n signal =>\n this._callWithRetry<T>(\n call.connectId,\n methodName,\n call.params,\n requiresWalletIntent,\n true,\n signal\n ),\n {\n label: methodName,\n rejectIfBusy: true,\n busyError: TrezorAdapter._createDeviceBusyError(methodName),\n }\n );\n debugLog('[TrezorAdapter][RES]', {\n method: methodName,\n success: response.success,\n payload: TrezorAdapter._sanitizeForLog(response.payload, methodName),\n });\n return response;\n } catch (error) {\n // The only way a Promise rejects out of `enqueue` is the busy-reject —\n // _callWithRetry never throws, it returns Response.failure. Convert to\n // the Response shape so chain method callers stay uniform.\n const response = this._errorToFailure(error);\n debugLog('[TrezorAdapter][RES]', {\n method: methodName,\n success: false,\n payload: TrezorAdapter._sanitizeForLog(response.payload, methodName),\n });\n return response;\n }\n }\n\n private async _callWithRetry<T>(\n connectId: string,\n methodName: TrezorMethodName,\n params: unknown,\n requiresWalletIntent: boolean,\n allowRetry: boolean,\n signal: AbortSignal\n ): Promise<Response<T>> {\n const { passphraseState, useEmptyPassphrase, rest } = TrezorAdapter._splitCommonParams(params);\n const restorePassphraseRequestContext = this._setPassphraseRequestContext(connectId, {\n passphraseState,\n useEmptyPassphrase,\n });\n try {\n const sessionId = await this._ensureSession(connectId, signal);\n if (requiresWalletIntent && !passphraseState && useEmptyPassphrase !== true) {\n return failure(\n HardwareErrorCode.InvalidParams,\n `Trezor ${methodName} requires an explicit wallet intent: passphraseState or useEmptyPassphrase`\n );\n }\n // Pin the call to the requested passphrase wallet. For THP this creates\n // a new application session; for v1 it re-initializes the device session.\n // Runs inside this queued job so the session alignment and the call can't\n // be interleaved by another request.\n if (passphraseState) {\n await TrezorAdapter._abortable(\n signal,\n this._alignAppSession(connectId, sessionId, passphraseState, signal)\n );\n } else if (useEmptyPassphrase === true) {\n await TrezorAdapter._abortable(signal, this._createFreshAppSession(sessionId, signal));\n }\n const result = await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, methodName, rest)\n );\n return success(result as T);\n } catch (error) {\n // If we were aborted, surface as-is — don't take the retry/recovery path.\n if (signal.aborted) {\n return this._errorToFailure(error);\n }\n const code = TrezorAdapter._errorCode(error);\n // On a transient disconnect (BLE drop, \"device not connected\" race after\n // the device-disconnect event hasn't propagated yet), drop the stale\n // session and reconnect once. Matches Ledger's _retryWithFreshConnection.\n if (allowRetry && code === HardwareErrorCode.DeviceDisconnected) {\n this._sessions.delete(connectId);\n this._verifiedPassphraseSessionsByConnectId.delete(connectId);\n return await this._callWithRetry<T>(\n connectId,\n methodName,\n params,\n requiresWalletIntent,\n false,\n signal\n );\n }\n // The device evicted the passphrase session created for this call. Retry\n // once and recreate the requested wallet context.\n if (\n allowRetry &&\n (passphraseState || useEmptyPassphrase === true) &&\n TrezorAdapter._isStaleSessionError(code)\n ) {\n if (passphraseState) {\n this._forgetVerifiedPassphraseSession(connectId, passphraseState);\n }\n return await this._callWithRetry<T>(\n connectId,\n methodName,\n params,\n requiresWalletIntent,\n false,\n signal\n );\n }\n debugLog('[TrezorAdapter][ERROR]', {\n method: methodName,\n connectId,\n error: TrezorAdapter._errorForLog(error),\n });\n return this._errorToFailure(error);\n } finally {\n restorePassphraseRequestContext();\n }\n }\n\n private async _ensureSession(connectId: string, signal?: AbortSignal): Promise<string> {\n const existing = this._sessions.get(connectId);\n if (existing) return existing;\n\n const pending = this._connectingPromises.get(connectId);\n if (pending) return pending;\n\n const promise = (async () => {\n try {\n if (signal?.aborted) {\n throw Object.assign(new Error('Trezor connection aborted'), {\n code: HardwareErrorCode.UserAborted,\n });\n }\n // Fail fast: a single connect attempt. On an \"unavailable device\" error we\n // drop the cached session/passphrase state and rethrow (no retry loop).\n try {\n return await this._connectSession(connectId, signal);\n } catch (error) {\n if (TrezorAdapter._isConnectionUnavailableError(error)) {\n this._sessions.delete(connectId);\n this._verifiedPassphraseSessionsByConnectId.delete(connectId);\n }\n throw error;\n }\n } finally {\n this._connectingPromises.delete(connectId);\n this._disconnectRequested.delete(connectId);\n }\n })();\n\n this._connectingPromises.set(connectId, promise);\n return promise;\n }\n\n /**\n * Pin the device's active wallet session to `passphraseState` (the target\n * wallet identity) before a wallet-bound op. THP has an app-session id; v1\n * does not, but it still binds passphrase to the device session, so the\n * connector re-initializes v1 before we derive and verify.\n *\n * SECURITY: a verified THP app session may be selected again, but the adapter\n * still derives and verifies the passphrase state before any chain method can\n * run. If the cache is stale, fall back to creating a fresh wallet context.\n */\n private async _alignAppSession(\n connectId: string,\n sessionId: string,\n passphraseState: string,\n signal: AbortSignal\n ): Promise<void> {\n const reused = await this._reuseVerifiedPassphraseSession(\n connectId,\n sessionId,\n passphraseState,\n signal\n );\n if (reused) return;\n\n const created = (await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, '__thpCreateSession', {\n passphraseMode: 'prompt',\n })\n )) as { protocol?: string; thpSessionId?: string | null };\n // v1 has no host-managed app-session id, but the connector has just\n // re-initialized a fresh device session; continue with reactive\n // passphrase-state verification.\n if (created?.protocol === 'thp' && !created.thpSessionId) return;\n if (created?.protocol !== 'thp' && created?.protocol !== 'v1') return;\n const state = await this._deriveState(sessionId, signal);\n if (state !== passphraseState) {\n throw Object.assign(\n new Error(\n `Passphrase produced wallet ${state}, expected ${passphraseState} (passphraseState mismatch)`\n ),\n { code: HardwareErrorCode.PassphraseStateMismatch }\n );\n }\n this._rememberVerifiedPassphraseSession(connectId, passphraseState, sessionId, created);\n }\n\n private _rememberVerifiedPassphraseSession(\n connectId: string,\n passphraseState: string,\n sessionId: string,\n created: { protocol?: string; thpSessionId?: string | null } | undefined\n ): void {\n let session: TrezorVerifiedPassphraseSession | undefined;\n if (created?.protocol === 'thp' && typeof created.thpSessionId === 'string') {\n session = {\n sessionId,\n protocol: 'thp',\n thpSessionId: created.thpSessionId,\n };\n } else if (created?.protocol === 'v1') {\n session = {\n sessionId,\n protocol: 'v1',\n };\n }\n if (!session) return;\n\n const sessions =\n this._verifiedPassphraseSessionsByConnectId.get(connectId) ??\n new Map<string, TrezorVerifiedPassphraseSession>();\n sessions.set(passphraseState, session);\n this._verifiedPassphraseSessionsByConnectId.set(connectId, sessions);\n }\n\n private _forgetVerifiedPassphraseSession(connectId: string, passphraseState: string): void {\n const sessions = this._verifiedPassphraseSessionsByConnectId.get(connectId);\n if (!sessions) return;\n sessions.delete(passphraseState);\n if (!sessions.size) {\n this._verifiedPassphraseSessionsByConnectId.delete(connectId);\n }\n }\n\n private async _reuseVerifiedPassphraseSession(\n connectId: string,\n sessionId: string,\n passphraseState: string,\n signal: AbortSignal\n ): Promise<boolean> {\n const cached = this._verifiedPassphraseSessionsByConnectId.get(connectId)?.get(passphraseState);\n if (!cached || cached.sessionId !== sessionId) {\n this._forgetVerifiedPassphraseSession(connectId, passphraseState);\n return false;\n }\n\n try {\n if (cached.protocol === 'thp') {\n if (!cached.thpSessionId) return false;\n await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, '__thpSelectSession', {\n thpSessionId: cached.thpSessionId,\n })\n );\n }\n const state = await this._deriveState(sessionId, signal);\n if (state !== passphraseState) {\n this._forgetVerifiedPassphraseSession(connectId, passphraseState);\n return false;\n }\n return true;\n } catch (error) {\n this._forgetVerifiedPassphraseSession(connectId, passphraseState);\n if (signal.aborted) throw error;\n return false;\n }\n }\n\n private async _createFreshAppSession(sessionId: string, signal: AbortSignal): Promise<void> {\n // Create a fresh standard-wallet context for this call. Empty passphrase is\n // enforced inside connector/core, not delegated to host UI handlers.\n await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, '__thpCreateSession', {\n passphraseMode: 'empty',\n })\n );\n }\n\n /**\n * Derive the active session's passphrase state — the wallet identity used to\n * verify the right wallet is active: the compressed PUBLIC KEY at a fixed\n * account path (m/44'/0'/0'). It comes from the SAME GetPublicKey that also\n * carries the 4-byte fingerprint, but we use the FULL pubkey (not the truncated\n * fingerprint) so it's collision-resistant — the address we return can never\n * come from the wrong wallet — while avoiding OneKey's extra Testnet GetAddress.\n * Non-interactive (also unlocks via PIN as a protected derivation).\n */\n private async _deriveState(sessionId: string, signal: AbortSignal): Promise<string> {\n const derived = (await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, 'btcGetPublicKey', {\n path: \"m/44'/0'/0'\",\n showOnDevice: false,\n })\n )) as { publicKey?: string };\n const state = derived?.publicKey;\n if (!state) {\n throw Object.assign(new Error('Failed to derive passphrase state (wallet public key)'), {\n code: HardwareErrorCode.UnknownError,\n });\n }\n return state;\n }\n\n private async _resolvePassphraseState(\n connectId: string,\n passphraseState: string | undefined,\n signal: AbortSignal\n ): Promise<Response<string | null>> {\n try {\n const sessionId = await this._ensureSession(connectId, signal);\n if (passphraseState) {\n // Verify mode: align (which always re-derives + confirms the state) so the\n // host can gate signing on the correct wallet being active.\n await this._alignAppSession(connectId, sessionId, passphraseState, signal);\n return success(passphraseState);\n }\n // Discover mode (OneKey-aligned). DON'T trust pre-read features — a LOCKED\n // device reports an unreliable `passphrase_protection`, and the connector's\n // plain getFeatures returns a CACHED blob, so checking `unlocked` up front\n // is meaningless. Instead derive first (which unlocks), then re-read fresh:\n // 1. Create a session + derive the state. This UNLOCKS the device (PIN) and,\n // for a passphrase wallet, prompts the passphrase (THP: proactively in\n // ThpCreateNewSession; v1: reactively).\n // 2. Re-read features FRESH from the device (now unlocked → trustworthy).\n // 3. passphrase_protection === true → return the current passphrase\n // state, even if the entered passphrase was empty. Otherwise →\n // standard wallet → return null (OneKey-aligned).\n const created = (await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, '__thpCreateSession', {\n passphraseMode: 'prompt',\n })\n )) as { protocol?: string; thpSessionId?: string | null };\n const state = await this._deriveState(sessionId, signal);\n const features = (await TrezorAdapter._abortable(\n signal,\n TrezorAdapter._callConnector(this._connector, sessionId, 'getFeatures', { refresh: true })\n )) as Record<string, unknown> | undefined;\n if (features?.passphrase_protection !== true) {\n // Standard wallet — discard the derived state, return null (OneKey convention).\n return success(null);\n }\n this._rememberVerifiedPassphraseSession(connectId, state, sessionId, created);\n return success(state);\n } catch (error) {\n return this._errorToFailure(error);\n }\n }\n\n private _onDeviceConnect = (data: { device: ConnectorDevice }): void => {\n const device = this._connectorDeviceToDeviceInfo(data.device);\n this._devices.set(device.connectId, device);\n this._emitter.emit(DEVICE.CONNECT, { type: DEVICE.CONNECT, payload: device });\n };\n\n private _onDeviceDisconnect = (data: { connectId: string }): void => {\n this._sessions.delete(data.connectId);\n this._verifiedPassphraseSessionsByConnectId.delete(data.connectId);\n this._devices.delete(data.connectId);\n this._emitter.emit(DEVICE.DISCONNECT, {\n type: DEVICE.DISCONNECT,\n payload: { connectId: data.connectId },\n });\n };\n\n private _onSupportFeatures = (data: {\n device: ConnectorDevice & { features: Record<string, unknown> };\n }): void => {\n const device = {\n ...this._connectorDeviceToDeviceInfo(data.device),\n features: data.device.features,\n };\n this._emitter.emit(DEVICE.FEATURES, {\n type: DEVICE.FEATURES,\n device,\n payload: { device },\n });\n };\n\n private _onUiRequest = (data: { type: string; payload?: unknown }): void => {\n if (data.type === UI_REQUEST.REQUEST_PASSPHRASE) {\n const payload =\n data.payload && typeof data.payload === 'object'\n ? (data.payload as Record<string, unknown>)\n : {};\n const connectId = typeof payload.connectId === 'string' ? payload.connectId : undefined;\n const context = connectId\n ? this._passphraseRequestContextByConnectId.get(connectId)\n : undefined;\n this._emitter.emit(\n data.type as keyof HardwareEventMap,\n {\n ...data,\n payload: {\n ...payload,\n ...(context?.passphraseState !== undefined\n ? { passphraseState: context.passphraseState }\n : {}),\n ...(context?.useEmptyPassphrase !== undefined\n ? { useEmptyPassphrase: context.useEmptyPassphrase }\n : {}),\n },\n } as never\n );\n return;\n }\n this._emitter.emit(data.type as keyof HardwareEventMap, data as never);\n };\n\n private _onUiEvent = (data: ConnectorUiEvent): void => {\n this._emitter.emit('ui-event', data);\n };\n\n private _onThpCredentialsChanged = (data: {\n connectId: string;\n deviceId?: string;\n credentials: Record<string, unknown>[];\n }): void => {\n this._emitter.emit(DEVICE.TREZOR_THP_CREDENTIALS_CHANGED, {\n type: DEVICE.TREZOR_THP_CREDENTIALS_CHANGED,\n payload: data,\n });\n };\n\n private _registerConnectorEvents(): void {\n this._connector.on('device-connect', this._onDeviceConnect);\n this._connector.on('device-disconnect', this._onDeviceDisconnect);\n this._connector.on(DEVICE.FEATURES, this._onSupportFeatures);\n this._connector.on('device-trezor-thp-credentials-changed', this._onThpCredentialsChanged);\n this._connector.on('ui-request', this._onUiRequest);\n this._connector.on('ui-event', this._onUiEvent);\n }\n\n private _unregisterConnectorEvents(): void {\n this._connector.off('device-connect', this._onDeviceConnect);\n this._connector.off('device-disconnect', this._onDeviceDisconnect);\n this._connector.off(DEVICE.FEATURES, this._onSupportFeatures);\n this._connector.off('device-trezor-thp-credentials-changed', this._onThpCredentialsChanged);\n this._connector.off('ui-request', this._onUiRequest);\n this._connector.off('ui-event', this._onUiEvent);\n }\n\n private _connectorDeviceToDeviceInfo(device: ConnectorDevice): DeviceInfo {\n return {\n vendor: 'trezor',\n model: device.model ?? 'unknown',\n firmwareVersion: 'unknown',\n deviceId: device.deviceId,\n connectId: device.connectId,\n label: device.name,\n // The per-device transport is authoritative (a combined USB+BLE\n // connector tags each discovered device); the connector's own\n // connectionType is only a nominal value, so fall back to it for\n // single-transport connectors that don't tag each device.\n connectionType: device.connectionType ?? this._connector.connectionType,\n capabilities: device.capabilities,\n };\n }\n\n private _errorToFailure(error: unknown) {\n const typed = error as { code?: unknown; message?: string };\n // Map hwk-trezor-core's string markers to standard HardwareErrorCodes.\n // The core tags pairing-handshake rejections (e.g. mistyped CodeEntry code)\n // with `code: 'ThpPairingFailed'`; surface it as a proper code, not Unknown.\n if (typed.code === 'ThpPairingFailed') {\n return failure(HardwareErrorCode.ThpPairingFailed, typed.message ?? 'THP pairing failed');\n }\n // Only trust a numeric `code` if it is an actual HardwareErrorCode. A raw\n // WebUSB AbortError carries a DOMException `code: 20`, which is NOT one of\n // ours — fall through to message-based mapping (transferOut/USBDevice abort\n // → TransportError) so it surfaces as a recognizable, retryable transport\n // error instead of Unknown.\n const trustedNumericCode =\n typeof typed.code === 'number' && KNOWN_HARDWARE_ERROR_CODES.has(typed.code)\n ? typed.code\n : undefined;\n const code =\n trustedNumericCode ??\n TrezorAdapter._mapTrezorFailureCode(error) ??\n HardwareErrorCode.UnknownError;\n return failure(code, typed.message ?? String(error));\n }\n\n /**\n * Emit `REQUEST_DEVICE_PERMISSION` and await the host's\n * `RECEIVE_DEVICE_PERMISSION` reply (60s budget covers OS prompt + user\n * tap). If the host doesn't wire a handler the wait times out and the\n * operation fails fast.\n *\n * Mirrors LedgerAdapter._ensureDevicePermission so hosts can register a\n * single handler that branches by `payload.transportType` (and optionally\n * the active brand) instead of teaching every adapter its own contract.\n *\n * - no `connectId` → environment-level check (scan / picker)\n * - with `connectId` → device-level check (per-call gating)\n */\n private async _ensureDevicePermission(connectId?: string, deviceId?: string): Promise<void> {\n // No handler? Skip silently so non-permission-aware hosts (and our own\n // unit tests) keep working — Ledger's adapter has a 60s timeout but\n // hardware-wallet flows on web/desktop don't always have a registered\n // listener, and they shouldn't stall for a minute waiting on one.\n if (!this._emitter.listenerCount(UI_REQUEST.REQUEST_DEVICE_PERMISSION)) {\n return;\n }\n const transportType: TransportType = this.activeTransport ?? 'usb';\n\n const waitPromise = this._uiRegistry.wait<{ granted: boolean; reason?: string }>(\n UI_REQUEST.REQUEST_DEVICE_PERMISSION,\n { timeoutMs: 60_000 }\n );\n\n this._emitter.emit(UI_REQUEST.REQUEST_DEVICE_PERMISSION, {\n type: UI_REQUEST.REQUEST_DEVICE_PERMISSION,\n payload: { transportType, connectId, deviceId },\n });\n\n const { granted } = await waitPromise;\n if (!granted) {\n throw Object.assign(new Error('Device permission denied'), {\n code: HardwareErrorCode.DevicePermissionDenied,\n });\n }\n }\n}\n","import { sha3_256 as sha3Hash } from '@noble/hashes/sha3';\n\nimport { deviceAuthenticityConfig } from './config';\nimport { prepareDeviceAuthenticityData, verifyAuthenticityProof } from './verifyAuthenticityProof';\n\nimport type { DeviceAuthenticityConfig } from './types';\n\nexport { deviceAuthenticityConfig } from './config';\nexport { prepareDeviceAuthenticityData, verifyAuthenticityProof } from './verifyAuthenticityProof';\nexport * from './types';\n\n/** Raw fields taken from a Trezor `AuthenticityProof` message. */\nexport type AuthenticityProof = {\n optiga_certificates: string[];\n optiga_signature: string;\n tropic_certificates?: string[];\n tropic_signature?: string;\n mcu_certificates?: string[];\n mcu_signature?: string;\n};\n\nexport type AuthenticateDeviceResult = {\n /**\n * True only when every layer implemented by this verifier and required by\n * the explicit model policy verified up to a trusted Trezor root CA AND the\n * device signed our challenge:\n * - Optiga (all secure-element models), and\n * - Tropic (T3W1 / Safe 7).\n * This is the currently implemented client policy, not full parity with the\n * latest capability-based Trezor Connect policy. Raw MCU fields remain in the\n * returned proof but are not yet a client-side pass/fail condition.\n * The reward backend must independently verify the same raw proof under its\n * own versioned policy; this client result is only a UX preview.\n */\n verified: boolean;\n /**\n * SHA3-256 of the verified Optiga device-attestation public key. It survives\n * wipe/recovery and has one representation across supported models. Do NOT\n * trust it when `verified` is false — it is then attacker-controlled.\n */\n deviceId?: string;\n /** Raw device attestation public key (hex) from the Optiga device certificate. */\n deviceCertPubKey?: string;\n /** X.509 subject serial number (hex), only present on T3W1 and above. */\n serialNumber?: string;\n rootPubKey?: string;\n caPubKey?: string;\n /**\n * True when the chain matched a DEBUG/staging root key (simulator / dev\n * device). A production accounting flow must reject verified results that\n * have `usedDebugKey === true`.\n */\n usedDebugKey?: boolean;\n /** Failure reason when `verified` is false. */\n error?: string;\n};\n\n// Revocation hook: CA public keys (hex) that must be rejected even if otherwise\n// valid. Empty today; kept so a leaked intermediate CA can be blocked by config\n// rather than a code change. Mirrors @trezor/device-authenticity's blacklist.\nexport const caPubKeyBlacklist: string[] = [];\n\nconst modelConfigOf = (config: DeviceAuthenticityConfig, deviceModel: string) => {\n const modelConfig = config[deviceModel];\n return modelConfig && typeof modelConfig !== 'number' ? modelConfig : undefined;\n};\n\nexport const getRequiredDeviceAuthenticityLayers = (\n deviceModel: string\n): readonly ('optiga' | 'tropic')[] => (deviceModel === 'T3W1' ? ['optiga', 'tropic'] : ['optiga']);\n\nconst isTropicExpected = (deviceModel: string): boolean =>\n getRequiredDeviceAuthenticityLayers(deviceModel).includes('tropic');\n\nconst matchedDebugKey = (\n config: DeviceAuthenticityConfig,\n deviceModel: string,\n rootPubKey?: string\n): boolean => {\n if (!rootPubKey) return false;\n const m = modelConfigOf(config, deviceModel);\n if (!m?.debug) return false;\n return [...(m.debug.rootPubKeysOptiga ?? []), ...(m.debug.rootPubKeysTropic ?? [])].includes(\n rootPubKey\n );\n};\n\n/**\n * Verify a Trezor `AuthenticityProof` against a challenge and derive a stable\n * per-device id. `challenge` must be the exact random bytes sent in the\n * `AuthenticateDevice` request. Never throws — malformed input from a malicious\n * device is folded into `{ verified: false }`.\n */\nexport const authenticateDeviceFromProof = ({\n proof,\n challenge,\n deviceModel,\n config = deviceAuthenticityConfig,\n allowDebugKeys,\n}: {\n proof: AuthenticityProof;\n challenge: Buffer;\n deviceModel: string;\n config?: DeviceAuthenticityConfig;\n allowDebugKeys?: boolean;\n}): AuthenticateDeviceResult => {\n try {\n const signedData = prepareDeviceAuthenticityData({ payload: challenge });\n const common = { signedData, deviceModel, config, allowDebugKeys, caPubKeyBlacklist };\n\n // 1) Optiga is required on every attestation-capable model.\n const { optiga_certificates: optigaCertificates, optiga_signature: optigaSignature } = proof;\n if (!optigaSignature || !optigaCertificates?.length) {\n return { verified: false, error: 'RESPONSE_PAYLOAD_MISSING' };\n }\n const optiga = verifyAuthenticityProof({\n ...common,\n proofType: 'optiga',\n certificates: optigaCertificates,\n signature: optigaSignature,\n });\n if (!optiga.valid) {\n return {\n verified: false,\n deviceCertPubKey: optiga.deviceCertPubKey,\n caPubKey: optiga.caPubKey,\n rootPubKey: optiga.rootPubKey,\n error: optiga.error,\n };\n }\n\n // 2) Trezor Connect requires the independent Tropic proof on T3W1.\n let usedDebugKey = matchedDebugKey(config, deviceModel, optiga.rootPubKey);\n if (isTropicExpected(deviceModel)) {\n const { tropic_certificates: tropicCertificates, tropic_signature: tropicSignature } = proof;\n if (!tropicSignature || !tropicCertificates?.length) {\n return { verified: false, error: 'RESPONSE_PAYLOAD_MISSING' };\n }\n const tropic = verifyAuthenticityProof({\n ...common,\n proofType: 'tropic',\n certificates: tropicCertificates,\n signature: tropicSignature,\n });\n if (!tropic.valid) {\n return { verified: false, error: tropic.error };\n }\n usedDebugKey = usedDebugKey || matchedDebugKey(config, deviceModel, tropic.rootPubKey);\n }\n\n const deviceId = Buffer.from(\n sha3Hash(Uint8Array.from(Buffer.from(optiga.deviceCertPubKey, 'hex')))\n ).toString('hex');\n\n return {\n verified: true,\n deviceId,\n deviceCertPubKey: optiga.deviceCertPubKey,\n serialNumber: optiga.serialNumber,\n rootPubKey: optiga.rootPubKey,\n caPubKey: optiga.caPubKey,\n usedDebugKey,\n };\n } catch (error) {\n // A malicious device can return a malformed certificate that throws deep in\n // the parser; never let that escape as a rejected promise.\n return {\n verified: false,\n error: `RESPONSE_MALFORMED: ${(error as Error)?.message ?? 'parse error'}`,\n };\n }\n};\n","import type { DeviceAuthenticityConfig } from './types';\n\n/**\n * Trezor manufacturing root CA public keys, per internal model.\n * Authoritative upstream source (firmware):\n * https://github.com/trezor/trezor-firmware/blob/main/core/embed/projects/prodtest/cmd/hsm_keys.h\n * Mirrored from @trezor/device-authenticity `deviceAuthenticityConfig`.\n *\n * Devices without a secure element (T1B1, T2T1) do not support AuthenticateDevice\n * and are therefore absent from this config.\n */\nexport const deviceAuthenticityConfig: DeviceAuthenticityConfig = {\n version: 2,\n T2B1: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_PROD_P256\n '04ca97480ac0d7b1e6efafe518cd433cec2bf8ab9822d76eafd34363b55d63e60380bff20acc75cde03cffcb50ab6f8ce70c878e37ebc58ff7cca0a83b16b15fa5',\n ],\n debug: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_DEBUG_P256 shared with T3B1\n '047f77368dea2d4d61e989f474a56723c3212dacf8a808d8795595ef38441427c4389bc454f02089d7f08b873005e4c28d432468997871c0bf286fd3861e21e96a',\n ],\n },\n },\n T3B1: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_PROD_P256\n '045b5c3fdd01f3602092834209b86df0ca86a9faf25cac35c73bf6237d66eb21eafcec3706f1ccd5eb4cc7f2fa1751213eccb1c78389afba89a5788ff31ee46a5d',\n ],\n debug: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_DEBUG_P256 shared with T2B1\n '047f77368dea2d4d61e989f474a56723c3212dacf8a808d8795595ef38441427c4389bc454f02089d7f08b873005e4c28d432468997871c0bf286fd3861e21e96a',\n ],\n },\n },\n T3T1: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_PROD_P256\n '041854b27fb1d9f65abb66828e78c9dc0ca301e66081ab0c6a4d104f9df1cd0ad5a7c75f77a8c092f55cf825d2abaf734f934c9394d5e75f75a5a06a5ee9be93ae',\n ],\n debug: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_DEBUG_P256\n '04e48b69cd7962068d3cca3bcc6b1747ef496c1e28b5529e34ad7295215ea161dbe8fb08ae0479568f9d2cb07630cb3e52f4af0692102da5873559e45e9fa72959',\n ],\n },\n },\n T3W1: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_PROD_P256\n '040dde0d3e0d4da593fac6fd02a461d0e7eef238aca55c7c50b4e9ec37f3873303b6429ef1c9b78b4411a7dcbbc5dde5225979c1c2da3b073e82b1ed3f5f9825bb',\n // upstream: DEV_AUTH_ROOT_PROD_BACKUP_P256\n '04c6a673af4ec44b10441b1d78676e15173ad0e36df9f7f2fa1cd819955f20fe32917b60da5fed3b3aa54a9ab8b3ed27d198b3768cad26eef5935cd87af0af065e',\n ],\n rootPubKeysTropic: [\n // upstream: DEV_AUTH_ROOT_PROD_ED25519\n '59237acd17134061d655b3f8d624573ca06ce8d862f38ba4e05140ce1d3d609d',\n // upstream: DEV_AUTH_ROOT_PROD_BACKUP_ED25519\n '5612606584ee7e0bc313b13f7ac94156bb4cb75bd77585ddbe579301306e85f1',\n ],\n debug: {\n rootPubKeysOptiga: [\n // upstream: DEV_AUTH_ROOT_DEBUG_P256\n '04521192e173a9da4e3023f747d836563725372681eba3079c56ff11b2fc137ab189eb4155f371127651b5594f8c332fc1e9c0f3b80d4212822668b63189706578',\n // upstream: DEV_AUTH_ROOT_STAGING_P256\n '0465e88f9b2cea67e8364f0cfcfacd500af24e9040b357beee629ccc4fce1704d1a7ef7284f387708f92ef14600e2caad6894016fee819d623b95d66210c3e7519',\n ],\n rootPubKeysTropic: [\n // upstream: DEV_AUTH_ROOT_STAGING_ED25519\n 'cd318dc8405ae4f4144e3284dcb7b0cb0f0c2195c2ca14a0f6fccd9104e32a4b',\n ],\n },\n },\n};\n","import type { Oid, ParsedCertificate } from './x509certificate';\n\nexport const findSubjectContentByOid = (\n deviceCert: ParsedCertificate,\n oid: Oid\n): Uint8Array | null => {\n const subjectItems = deviceCert.tbsCertificate.subject;\n const matchedItem = subjectItems.find(({ algorithmOid }) => algorithmOid === oid);\n if (!matchedItem || !matchedItem.parameters) return null;\n\n return matchedItem.parameters.asn1.contents;\n};\n","import { type parseCertificate } from './x509certificate';\n\n// Check that this certificate is a valid Trezor CA.\n// KeyUsage must be present and allow certificate signing.\n// BasicConstraints must be present, have the cA flag and a pathLenConstraint.\n// Any unrecognized non-critical extension is allowed. Any unrecognized critical extension is disallowed.\nexport const validateCaCertExtensions = (\n cert: ReturnType<typeof parseCertificate>,\n pathLen: number\n) => {\n let hasKeyUsage = false;\n let hasBasicConstraints = false;\n cert.tbsCertificate.extensions.forEach(ext => {\n if (ext.key === 'keyUsage') {\n if (ext.keyCertSign !== '1') {\n throw new Error(`CA keyCertSign not set`);\n }\n hasKeyUsage = true;\n } else if (ext.key === 'basicConstraints') {\n if (!ext.cA) {\n throw new Error(`CA basicConstraints.cA not set`);\n }\n if (typeof ext.pathLenConstraint !== 'number') {\n throw new Error('CA basicConstraints.pathLenConstraint not set');\n }\n if (ext.pathLenConstraint < pathLen) {\n throw new Error('Issuer was not permitted to issue certificate');\n }\n hasBasicConstraints = true;\n } else if (ext.critical) {\n throw new Error(`Unknown critical extension ${ext.key} in CA certificate`);\n }\n });\n\n if (!hasKeyUsage || !hasBasicConstraints) {\n throw new Error(\n `CA missing extensions keyUsage: ${hasKeyUsage}, basicConstraints: ${hasBasicConstraints}`\n );\n }\n};\n","import { ed25519 } from '@noble/curves/ed25519';\nimport { p256 } from '@noble/curves/p256';\nimport { sha256 } from '@noble/hashes/sha256';\n\nimport { type AlgorithmName, fixSignature } from './x509certificate';\n\nexport type VerifySignature = (\n rawKey: Uint8Array,\n data: Uint8Array,\n signature: Uint8Array\n) => boolean;\n\n// P-256 (secp256r1) ECDSA over SHA-256. Hardware attestation signatures are NOT\n// guaranteed to be low-S normalized, so lowS must be disabled or valid signatures\n// would be rejected. `fixSignature` repairs the 1-in-256 malformed Optiga DER encoding.\nexport const verifySignatureP256: VerifySignature = (rawKey, data, signature) => {\n try {\n const sig = p256.Signature.fromDER(Buffer.from(fixSignature(signature)).toString('hex'));\n\n return p256.verify(sig.toBytes('compact'), sha256(data), rawKey, { lowS: false });\n } catch {\n // invalid inputs (e.g. an Ed25519 signature with a P-256 key) are an\n // unsuccessful verification, not a runtime error\n return false;\n }\n};\n\nexport const verifySignatureEd25519: VerifySignature = (rawKey, data, signature) => {\n try {\n return ed25519.verify(signature, data, rawKey);\n } catch {\n return false;\n }\n};\n\nexport const getVerifyFn = (algorithmName: AlgorithmName): VerifySignature => {\n if (algorithmName === 'P-256') return verifySignatureP256;\n if (algorithmName === 'Ed25519') return verifySignatureEd25519;\n throw new Error(`Unsupported signature algorithm: ${algorithmName}`);\n};\n","/**\n * Parse an x509 certificate returned from Trezor (AuthenticityProof) from DER format.\n * Ported from @trezor/device-authenticity (self-contained, no external deps).\n * inspired by https://blog.engelke.com/2014/10/21/web-crypto-and-x-509-certificates/\n */\n\ninterface Asn1 {\n cls: number; // ASN.1 class of the object.\n tag: number; // ASN.1 object type.\n structured: boolean; // Structured objects are encoded ASN.1 objects. Other objects (primitive) values depends on the tag and class\n byteLength: number; // value size\n contents: Uint8Array; // byte array containing the object value.\n raw: Uint8Array; // original byte array\n}\n\ntype OneOrTwoNumbers = `${number}` | `${number}.${number}`;\n// type that allows between 4 to 8 numbers to specify an OID\nexport type Oid = `${OneOrTwoNumbers}.${OneOrTwoNumbers}.${OneOrTwoNumbers}.${OneOrTwoNumbers}`;\n\n// algorithms supported to verify certificates and signatures\nexport type AlgorithmName = 'P-256' | 'Ed25519' | 'MLDSA44' | 'unknown';\n\ntype Extension =\n | {\n key: 'keyUsage';\n critical?: boolean;\n keyCertSign: '0' | '1';\n }\n | {\n key: 'basicConstraints';\n critical?: boolean;\n cA: boolean;\n pathLenConstraint?: number;\n }\n | (Asn1 & {\n key: Oid;\n critical?: boolean;\n });\n\nconst parseOidToAlgorithmName = (oid: Oid): AlgorithmName => {\n if (oid === '1.2.840.10045.4.3.2') return 'P-256'; // https://oid-base.com/get/1.2.840.10045.4.3.2\n if (oid === '1.3.101.112') return 'Ed25519'; // https://oid-base.com/get/1.3.101.112\n if (oid === '2.16.840.1.101.3.4.3.17') return 'MLDSA44'; // https://oid-base.com/get/2.16.840.1.101.3.4.3.17\n\n return 'unknown';\n};\n\nconst derToAsn1 = (byteArray: Uint8Array): Asn1 => {\n let position = 0;\n\n function getTag() {\n let tag = byteArray[0] & 0x1f;\n position += 1;\n if (tag === 0x1f) {\n tag = 0;\n while (byteArray[position] >= 0x80) {\n tag = tag * 128 + byteArray[position] - 0x80;\n position += 1;\n }\n tag = tag * 128 + byteArray[position] - 0x80;\n position += 1;\n }\n\n return tag;\n }\n\n function getLength() {\n let length = 0;\n\n if (byteArray[position] < 0x80) {\n length = byteArray[position];\n position += 1;\n } else {\n const numberOfDigits = byteArray[position] & 0x7f;\n position += 1;\n length = 0;\n for (let i = 0; i < numberOfDigits; i++) {\n length = length * 256 + byteArray[position];\n position += 1;\n }\n }\n\n return length;\n }\n\n const cls = (byteArray[0] & 0xc0) / 64;\n const structured = (byteArray[0] & 0x20) === 0x20;\n const tag = getTag();\n\n if (byteArray[position] === 0x80) {\n // DER forbids indefinite length encoding. You must use the definite length encoding (that is, with the length specified at the beginning).\n // https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/\n throw new Error('Unsupported length encoding');\n }\n\n const length = getLength(); // As encoded, which may be special value 0\n const byteLength = position + length;\n const contents = byteArray.subarray(position, byteLength);\n\n const raw = byteArray.subarray(0, byteLength); // May not be the whole input array\n\n return {\n cls,\n tag,\n structured,\n byteLength,\n contents,\n raw,\n };\n};\n\nconst derToAsn1List = (byteArray: Uint8Array) => {\n const result = [];\n let nextPosition = 0;\n while (nextPosition < byteArray.length) {\n const nextPiece = derToAsn1(byteArray.subarray(nextPosition));\n result.push(nextPiece);\n nextPosition += nextPiece.byteLength;\n }\n\n return result;\n};\n\nconst derBitStringValue = (byteArray: Uint8Array) => ({\n unusedBits: byteArray[0],\n bytes: byteArray.subarray(1),\n});\n\nconst parseSignatureValue = (asn1: Asn1) => {\n if (asn1.cls !== 0 || asn1.tag !== 3 || asn1.structured) {\n throw new Error('Bad signature value. Not a BIT STRING.');\n }\n\n return {\n asn1,\n bits: derBitStringValue(asn1.contents),\n };\n};\n\n// Optiga may produce a malformed signature with probability 1 in 256.\n// https://github.com/trezor/trezor-firmware/issues/3411\nexport const fixSignature = (byteArray: Uint8Array) => {\n const asn1 = derToAsn1(byteArray);\n if (asn1.cls !== 0 || asn1.tag !== 16 || !asn1.structured) {\n throw new Error('Bad signature. Not a SEQUENCE.');\n }\n\n const items = derToAsn1List(asn1.contents);\n let newLength = 0;\n const fixedItems = items.map(chunk => {\n // find first significant byte\n const index = chunk.contents.findIndex(value => value > 0x00);\n const data = chunk.contents.subarray(index);\n // According to the DER-encoding rules, the integers are supposed to be prefixed with a 0x00 byte\n // if **and only if** the most significant byte is >= 0x80\n const offset = data[0] >= 0x80 ? 1 : 0;\n // create replacement for chunk\n const chunkLength = data.length + offset;\n const newChunk = new Uint8Array(chunkLength + 2);\n // set first two bytes: original value and new length of the chunk\n newChunk.set([chunk.raw[0], chunkLength]);\n // optionally add 0\n if (offset > 0) {\n newChunk.set([0], 2);\n }\n // fill new chunk with data\n newChunk.set(data, 2 + offset);\n newLength += newChunk.length;\n\n return newChunk;\n });\n\n // create replacement for sequence object\n const signature = new Uint8Array(newLength + 2);\n // set two first bytes: original value and new length of all chunks\n signature.set([byteArray[0], newLength]);\n // fill new sequence with fixed items\n let signatureOffset = 2;\n fixedItems.forEach(item => {\n signature.set(item, signatureOffset);\n\n signatureOffset += item.length;\n });\n\n return signature;\n};\n\nconst derObjectIdentifierValue = (byteArray: Uint8Array) => {\n let oid = `${Math.floor(byteArray[0] / 40)}.${byteArray[0] % 40}`;\n let position = 1;\n while (position < byteArray.length) {\n let nextInteger = 0;\n while (byteArray[position] >= 0x80) {\n nextInteger = nextInteger * 0x80 + (byteArray[position] & 0x7f);\n position += 1;\n }\n nextInteger = nextInteger * 0x80 + byteArray[position];\n position += 1;\n oid += `.${nextInteger}`;\n }\n\n return oid as Oid;\n};\n\n/*\n * AlgorithmIdentifier ::= SEQUENCE {\n * algorithm OBJECT IDENTIFIER,\n * parameters ANY DEFINED BY algorithm OPTIONAL\n * }\n */\nconst parseAlgorithmIdentifier = (asn1: Asn1) => {\n if (asn1.cls !== 0 || asn1.tag !== 16 || !asn1.structured) {\n throw new Error('Bad algorithm identifier. Not a SEQUENCE.');\n }\n const pieces = derToAsn1List(asn1.contents);\n if (pieces.length > 2) {\n throw new Error('Bad algorithm identifier. Contains too many child objects.');\n }\n const encodedAlgorithm = pieces[0];\n if (encodedAlgorithm.cls !== 0 || encodedAlgorithm.tag !== 6 || encodedAlgorithm.structured) {\n throw new Error('Bad algorithm identifier. Does not begin with an OBJECT IDENTIFIER.');\n }\n const algorithmOid = derObjectIdentifierValue(encodedAlgorithm.contents);\n const algorithmName = parseOidToAlgorithmName(algorithmOid);\n\n return {\n asn1,\n algorithmOid,\n algorithmName,\n parameters: pieces.length === 2 ? { asn1: pieces[1] } : null,\n };\n};\n\n/*\n * Name ::= SEQUENCE {\n * algorithm OBJECT IDENTIFIER,\n * parameters ANY DEFINED BY algorithm OPTIONAL\n * }\n */\nexport const parseName = (asn1: Asn1) =>\n // SEQUENCE > SET > SEQUENCE\n derToAsn1List(asn1.contents).map(item => {\n const attrSet = derToAsn1(item.contents);\n\n return parseAlgorithmIdentifier(attrSet);\n });\n\n/*\n * SubjectPublicKeyInfo ::= SEQUENCE {\n * algorithm AlgorithmIdentifier,\n * subjectPublicKey BIT STRING\n * }\n */\nconst parseSubjectPublicKeyInfo = (asn1: Asn1) => {\n if (asn1.cls !== 0 || asn1.tag !== 16 || !asn1.structured) {\n throw new Error('Bad SPKI. Not a SEQUENCE.');\n }\n const pieces = derToAsn1List(asn1.contents);\n if (pieces.length !== 2) {\n throw new Error('Bad SubjectPublicKeyInfo. Wrong number of child objects.');\n }\n\n return {\n asn1,\n algorithm: parseAlgorithmIdentifier(pieces[0]),\n bits: derBitStringValue(pieces[1].contents),\n };\n};\n\n/*\n * Time ::= CHOICE {\n * utcTime UTCTime,\n * generalTime GeneralizedTime\n * }\n * NOTE: UTC and generalized times may both appear. (generalized > 2050)\n */\nconst parseUtcTime = (time: Asn1) => {\n // tag: 23 = UTCTime (YYMMDDhhmmZ);\n // tag: 24 = GeneralizedTime (YYYYMMDDhhmmZ)\n let offset = 4;\n let yearOffset = 0;\n if (time.tag === 23) {\n offset = 2;\n yearOffset = 2000;\n }\n const utc = Buffer.from(time.contents).toString();\n const year = yearOffset + Number(utc.substring(0, offset));\n const month = Number(utc.substring(offset, offset + 2)) - 1;\n const day = Number(utc.substring(offset + 2, offset + 4));\n const hour = Number(utc.substring(offset + 4, offset + 6));\n const minute = Number(utc.substring(offset + 6, offset + 8));\n\n const date = new Date();\n date.setUTCFullYear(year, month, day);\n date.setUTCHours(hour, minute, 0);\n\n return date;\n};\n\n/*\n * Validity ::= SEQUENCE {\n * from Time,\n * to Time\n * }\n */\nconst parseValidity = (asn1: Asn1) => {\n const [from, to] = derToAsn1List(asn1.contents);\n\n return {\n from: parseUtcTime(from),\n to: parseUtcTime(to),\n };\n};\n\n/*\n * https://www.alvestrand.no/objectid/2.5.29.html\n * Extensions ::= SEQUENCE {\n * extnID OBJECT IDENTIFIER,\n * critical BOOLEAN OPTIONAL,\n * extnValue OCTET STRING\n * }\n */\nconst parseExtensions = (data: Asn1) => {\n const asn1 = derToAsn1(data.contents);\n if (asn1.cls !== 0 || asn1.tag !== 16 || !asn1.structured) {\n throw new Error(\"This can't be a Extension. Wrong data type.\");\n }\n\n const readBoolean = (value?: Asn1) => {\n if (!value) return false;\n if (value.cls !== 0 || value.tag !== 1 || value.contents.length !== 1 || value.structured) {\n throw new Error(\"This can't be a boolean. Wrong data type.\");\n }\n if (![0x00, 0xff].includes(value.contents[0])) {\n throw new Error('Invalid boolean value.');\n }\n\n return value.contents[0] === 0xff;\n };\n\n const readBitString = (uint8Array: Uint8Array) => {\n const buffer = Buffer.from(uint8Array);\n const tag = buffer.readUInt8(0);\n if (tag !== 3) {\n throw new Error(\"This can't be a bit string. Wrong data type.\");\n }\n const length = buffer.readUInt8(1);\n const unusedBits = buffer.readUInt8(2);\n const bitStringBytes = buffer.subarray(3, 3 + length - 1); // -1 because the length includes the unusedBits byte\n const bitString = bitStringBytes.reduce(\n (str, byte) => str + byte.toString(2).padStart(8, '0'),\n ''\n );\n\n return bitString.slice(0, bitString.length - unusedBits) as '01';\n };\n\n const readInteger = (value?: Asn1) => {\n if (!value) return undefined;\n if (value.cls !== 0 || value.tag !== 2 || value.contents.length !== 1 || value.structured) {\n throw new Error(\"This can't be a integer. Wrong data type.\");\n }\n\n return Buffer.from(value.contents).readInt8();\n };\n\n const extensions: Extension[] = [];\n derToAsn1List(asn1.contents).forEach(item => {\n const [id, ...pieces] = derToAsn1List(item.contents);\n if (id.cls !== 0 || id.tag !== 6 || id.structured) {\n throw new Error('Bad extension. Does not begin with an OBJECT IDENTIFIER.');\n }\n\n const algorithm = derObjectIdentifierValue(id.contents);\n const critical = pieces.length > 1 ? readBoolean(pieces[0]) : false;\n const extnValue = pieces.length > 1 ? pieces[1] : pieces[0];\n if (extnValue.cls !== 0 || extnValue.tag !== 4 || extnValue.structured) {\n throw new Error(\"This can't be a octet string. Wrong data type.\");\n }\n\n if (algorithm === '2.5.29.15') {\n // https://www.alvestrand.no/objectid/2.5.29.15.html\n extensions.push({\n key: 'keyUsage',\n critical,\n keyCertSign: readBitString(extnValue.contents)[5] as '0' | '1',\n });\n } else if (algorithm === '2.5.29.19') {\n // https://www.alvestrand.no/objectid/2.5.29.19.html\n const fields = derToAsn1List(derToAsn1(extnValue.contents).contents);\n const ca = fields.length > 0 && fields[0].tag === 1 ? fields[0] : undefined;\n const len = fields.length > 0 && fields[0].tag === 2 ? fields[0] : fields[1];\n\n extensions.push({\n key: 'basicConstraints',\n critical,\n cA: readBoolean(ca),\n pathLenConstraint: readInteger(len),\n });\n } else {\n extensions.push({\n key: algorithm,\n critical,\n ...item,\n });\n }\n });\n\n return extensions;\n};\n\n/*\n * TBSCertificate ::= SEQUENCE {\n * version [0] EXPLICIT Version DEFAULT v1,\n * serialNumber CertificateSerialNumber,\n * signature AlgorithmIdentifier,\n * issuer Name[],\n * validity Validity,\n * subject Name[],\n * subjectPublicKeyInfo SubjectPublicKeyInfo,\n * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,\n * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,\n * extensions [3] EXPLICIT Extensions OPTIONAL\n * }\n */\nconst parseTBSCertificate = (asn1: Asn1) => {\n if (asn1.cls !== 0 || asn1.tag !== 16 || !asn1.structured) {\n throw new Error(\"This can't be a TBSCertificate. Wrong data type.\");\n }\n const pieces = derToAsn1List(asn1.contents);\n if (pieces.length < 7) {\n throw new Error('Bad TBS Certificate. There are fewer than the seven required children.');\n }\n\n return {\n asn1,\n version: pieces[0],\n serialNumber: pieces[1],\n signature: parseAlgorithmIdentifier(pieces[2]),\n issuer: pieces[3],\n validity: parseValidity(pieces[4]),\n subject: parseName(pieces[5]),\n subjectPublicKeyInfo: parseSubjectPublicKeyInfo(pieces[6]),\n extensions: parseExtensions(pieces[7]),\n };\n};\n\n/*\n * Certificate ::= SEQUENCE {\n * tbsCertificate TBSCertificate,\n * signatureAlgorithm AlgorithmIdentifier,\n * signatureValue BIT STRING\n * }\n */\nexport const parseCertificate = (byteArray: Uint8Array) => {\n const asn1 = derToAsn1(byteArray);\n if (asn1.cls !== 0 || asn1.tag !== 16 || !asn1.structured) {\n throw new Error(\"This can't be an X.509 certificate. Wrong data type.\");\n }\n const pieces = derToAsn1List(asn1.contents);\n if (pieces.length !== 3) {\n throw new Error('Certificate contains more than the three specified children.');\n }\n\n return {\n asn1,\n tbsCertificate: parseTBSCertificate(pieces[0]),\n signatureAlgorithm: parseAlgorithmIdentifier(pieces[1]),\n signatureValue: parseSignatureValue(pieces[2]),\n };\n};\n\nexport type ParsedCertificate = ReturnType<typeof parseCertificate>;\n","import { findSubjectContentByOid } from './findSubjectContentByOid';\nimport {\n type DeviceAuthenticityConfig,\n type VerifyAuthenticityProofParams,\n type VerifyAuthenticityProofResult,\n} from './types';\nimport { validateCaCertExtensions } from './validateCaCertExtensions';\nimport { getVerifyFn } from './verifySignatures';\nimport { type ParsedCertificate, parseCertificate } from './x509certificate';\n\nconst getChunkSize = (byteLength: number): Uint8Array => {\n if (byteLength < 0xfd) return Uint8Array.from([byteLength]);\n if (byteLength <= 0xffff) {\n const b = Buffer.alloc(3);\n b[0] = 0xfd;\n b.writeUInt16LE(byteLength, 1);\n\n return Uint8Array.from(b);\n }\n const b = Buffer.alloc(5);\n b[0] = 0xfe;\n b.writeUInt32LE(byteLength, 1);\n\n return Uint8Array.from(b);\n};\n\n// Compose the data against which the signatures are verified: each chunk is\n// prefixed with its compact-size length. Matches the firmware's signed payload.\nexport const prepareDeviceAuthenticityData = ({\n payload,\n prefix = 'AuthenticateDevice:',\n}: {\n payload: Buffer | Buffer[];\n prefix?: string;\n}): Uint8Array => {\n const chunks = [\n Uint8Array.from(Buffer.from(prefix)),\n ...(Array.isArray(payload) ? payload : [payload]).map(chunk => Uint8Array.from(chunk)),\n ];\n const framed = chunks.flatMap(chunk => [getChunkSize(chunk.byteLength), chunk]);\n const result = new Uint8Array(framed.reduce((total, chunk) => total + chunk.byteLength, 0));\n let offset = 0;\n for (const chunk of framed) {\n result.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return result;\n};\n\nconst getRootPubKeys = ({\n config,\n deviceModel,\n proofType,\n allowDebugKeys,\n}: {\n config: DeviceAuthenticityConfig;\n deviceModel: string;\n proofType: 'optiga' | 'tropic';\n allowDebugKeys?: boolean;\n}): string[] => {\n const modelConfig = config[deviceModel];\n if (!modelConfig || typeof modelConfig === 'number') {\n throw new Error(`Pubkeys for ${deviceModel} not found in config`);\n }\n const prod =\n proofType === 'optiga'\n ? modelConfig.rootPubKeysOptiga ?? []\n : modelConfig.rootPubKeysTropic ?? [];\n if (!allowDebugKeys) return prod;\n\n return [\n ...prod,\n ...(proofType === 'optiga'\n ? modelConfig.debug?.rootPubKeysOptiga ?? []\n : modelConfig.debug?.rootPubKeysTropic ?? []),\n ];\n};\n\nconst matchRootPubKeyToCertificate = ({\n cert,\n allRootPubKeys,\n}: {\n cert: ParsedCertificate;\n allRootPubKeys: string[];\n}): string | undefined => {\n const verifySignatureFn = getVerifyFn(cert.signatureAlgorithm.algorithmName);\n\n return allRootPubKeys.find(rootPubKey =>\n verifySignatureFn(\n Uint8Array.from(Buffer.from(rootPubKey, 'hex')),\n cert.tbsCertificate.asn1.raw,\n cert.signatureValue.bits.bytes\n )\n );\n};\n\n// Get internal model name from the device cert Subject (OID 2.5.4.3), e.g. 'T2B1'.\nconst parseModelFromDeviceCertSubject = (deviceCert: ParsedCertificate) => {\n const modelDescriptionBytes = findSubjectContentByOid(deviceCert, '2.5.4.3');\n if (modelDescriptionBytes === null) return null;\n\n // the whole string is e.g. 'T3W1 Trezor Safe 7', only the first 4 chars are the model\n return Buffer.from(modelDescriptionBytes.subarray(0, 4)).toString();\n};\n\n// Get serial number from the device cert Subject (OID 2.5.4.5). Only present on T3W1 and above.\nconst parseSerialNumberFromDeviceCert = (deviceCert: ParsedCertificate) => {\n const serialNumber = findSubjectContentByOid(deviceCert, '2.5.4.5');\n if (serialNumber === null) return undefined;\n\n return Buffer.from(serialNumber).toString('hex');\n};\n\n/**\n * Verifies the signing scheme: rootPubKey → CA pub key → device key → signature of prefixed challenge.\n * Returns the per-device attestation public key (`deviceCertPubKey`) on success.\n */\nexport const verifyAuthenticityProof = ({\n proofType,\n certificates,\n signature,\n signedData,\n deviceModel,\n config,\n allowDebugKeys,\n caPubKeyBlacklist = [],\n}: VerifyAuthenticityProofParams): VerifyAuthenticityProofResult => {\n const allRootPubKeys = getRootPubKeys({\n config,\n deviceModel,\n proofType,\n allowDebugKeys,\n });\n\n const parsedCertificates = certificates.map(c =>\n parseCertificate(new Uint8Array(Buffer.from(c, 'hex')))\n );\n\n const firstCertAlgName = parsedCertificates[0]?.signatureAlgorithm.algorithmName;\n // This mirrors Trezor Connect's production policy: Optiga uses P-256 and\n // Safe 7's additional Tropic proof uses Ed25519. MCU/ML-DSA is not a separate\n // client-side pass/fail condition.\n const expectedAlgorithmName = proofType === 'optiga' ? 'P-256' : 'Ed25519';\n if (firstCertAlgName !== expectedAlgorithmName) {\n return { valid: false, error: 'RESPONSE_MALFORMED' };\n }\n if (parsedCertificates.length !== 2) {\n return { valid: false, error: 'RESPONSE_MALFORMED' };\n }\n\n const [deviceCert, caCert] = parsedCertificates;\n const deviceCertPubKey = Buffer.from(\n deviceCert.tbsCertificate.subjectPublicKeyInfo.bits.bytes\n ).toString('hex');\n\n const deviceCertAlgName = deviceCert.signatureAlgorithm.algorithmName;\n const caCertAlgName = caCert.signatureAlgorithm.algorithmName;\n if (deviceCertAlgName !== caCertAlgName) {\n return { valid: false, deviceCertPubKey, error: 'RESPONSE_MALFORMED' };\n }\n\n validateCaCertExtensions(caCert, 0); // pathLenConstraint is always 0 (single CA cert, no chain)\n const verifySignatureFn = getVerifyFn(deviceCertAlgName);\n\n // 1) CA certificate must be signed by one of the trusted root pub keys.\n const caPubKeyBytes = caCert.tbsCertificate.subjectPublicKeyInfo.bits.bytes;\n const caPubKey = Buffer.from(caPubKeyBytes).toString('hex');\n const rootPubKeyMatch = matchRootPubKeyToCertificate({ allRootPubKeys, cert: caCert });\n if (rootPubKeyMatch === undefined) {\n return { valid: false, caPubKey, deviceCertPubKey, error: 'ROOT_PUBKEY_NOT_FOUND' };\n }\n // Revocation: reject a specific (e.g. leaked) intermediate CA even if it chains\n // to a trusted root.\n if (caPubKeyBlacklist.includes(caPubKey)) {\n return {\n valid: false,\n caPubKey,\n rootPubKey: rootPubKeyMatch,\n deviceCertPubKey,\n error: 'CA_PUBKEY_BLACKLISTED',\n };\n }\n // Both the CA and the device leaf must be currently valid: neither cert's\n // notBefore may be in the future, and neither may already be past notAfter.\n // An expired link in the chain must not still verify as trusted.\n const now = Date.now();\n const isWithinValidity = (validity: { from: Date; to: Date }) =>\n validity.from.getTime() <= now && now <= validity.to.getTime();\n if (\n !isWithinValidity(caCert.tbsCertificate.validity) ||\n !isWithinValidity(deviceCert.tbsCertificate.validity)\n ) {\n return {\n valid: false,\n caPubKey,\n rootPubKey: rootPubKeyMatch,\n deviceCertPubKey,\n error: 'CERTIFICATE_EXPIRED',\n };\n }\n\n // 2) Device model in the certificate must match the connected device.\n const modelFromSubject = parseModelFromDeviceCertSubject(deviceCert);\n if (modelFromSubject !== deviceModel) {\n return {\n valid: false,\n caPubKey,\n rootPubKey: rootPubKeyMatch,\n deviceCertPubKey,\n error: 'INVALID_DEVICE_MODEL',\n };\n }\n\n // 3) Device certificate must be signed by the CA pub key.\n const isDeviceCertValid = verifySignatureFn(\n Uint8Array.from(caPubKeyBytes),\n deviceCert.tbsCertificate.asn1.raw,\n deviceCert.signatureValue.bits.bytes\n );\n if (!isDeviceCertValid) {\n return {\n valid: false,\n caPubKey,\n rootPubKey: rootPubKeyMatch,\n deviceCertPubKey,\n error: 'INVALID_DEVICE_CERTIFICATE',\n };\n }\n\n // 4) The challenge signature must be produced by the device key.\n const isSignatureValid = verifySignatureFn(\n Uint8Array.from(deviceCert.tbsCertificate.subjectPublicKeyInfo.bits.bytes),\n signedData,\n Uint8Array.from(Buffer.from(signature, 'hex'))\n );\n if (!isSignatureValid) {\n return {\n valid: false,\n caPubKey,\n rootPubKey: rootPubKeyMatch,\n deviceCertPubKey,\n error: 'INVALID_DEVICE_SIGNATURE',\n };\n }\n\n return {\n valid: true,\n caPubKey,\n rootPubKey: rootPubKeyMatch,\n deviceCertPubKey,\n serialNumber: parseSerialNumberFromDeviceCert(deviceCert),\n };\n};\n","/**\n * SDK-global event bus. Per-runtime singleton; hosts forward events over their\n * own IPC when the adapter runs in an offscreen/background split.\n */\n\nexport type SdkLogEvent = {\n type: 'log';\n level: 'debug' | 'info' | 'warn' | 'error';\n /** Pre-stringified payload. */\n message: string;\n};\n\nexport type SdkEvent = SdkLogEvent;\n\nexport type SdkEventListener = (event: SdkEvent) => void;\n\nconst listeners = new Set<SdkEventListener>();\n\nexport function onSdkEvent(listener: SdkEventListener): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nexport function offSdkEvent(listener: SdkEventListener): void {\n listeners.delete(listener);\n}\n\nexport function emitSdkEvent(event: SdkEvent): void {\n if (listeners.size === 0) return;\n for (const listener of listeners) {\n try {\n listener(event);\n } catch {\n // Logging subscribers must not affect SDK execution.\n }\n }\n}\n\nexport function emitLog(level: SdkLogEvent['level'], ...args: unknown[]): void {\n if (listeners.size === 0) return;\n const message = args.map(a => (typeof a === 'string' ? a : safeStringify(a))).join(' ');\n emitSdkEvent({ type: 'log', level, message });\n}\n\nfunction safeStringify(value: unknown): string {\n if (value instanceof Error) {\n return value.stack ? `${value.message}\\n${value.stack}` : value.message;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n","import { emitLog } from './sdkEventBus';\n\nexport function debugLog(...args: unknown[]): void {\n emitLog('debug', ...args);\n}\n\nexport function debugError(...args: unknown[]): void {\n emitLog('error', ...args);\n}\n","import type { EvmEthereumDefinitions } from '@onekeyfe/hwk-adapter-core';\n\n/**\n * Opt-in helpers for fetching Trezor Ethereum \"definitions\" — the SatoshiLabs-\n * signed network/token metadata a Trezor device uses to render friendly names\n * for chains and tokens that are NOT built into firmware.\n *\n * IMPORTANT — the SDK signing methods (evmSignTransaction / evmGetAddress / …)\n * NEVER call these. A local hardware operation must not silently become a\n * third-party network request (see PR #824 review). The caller decides whether,\n * when, and where to fetch, then passes the result into the sign params as\n * `ethereumDefinitions`. When omitted, the device falls back to a generic\n * confirmation screen and signing still completes.\n *\n * Cross-process note: `fetchImpl` is a FUNCTION and cannot be serialized across\n * an IPC / postMessage boundary. Call these helpers in a network-capable\n * process and send only the resulting hex strings to the process that talks\n * to the device. Use `baseUrl` (a string) to route through your own\n * controlled proxy instead of hitting data.trezor.io directly.\n */\n\n/** Default upstream (Trezor). Override with `baseUrl` to use your own proxy. */\nexport const DEFAULT_ETHEREUM_DEFINITIONS_BASE_URL =\n 'https://data.trezor.io/firmware/eth-definitions';\n\n/** Timeout so a hung host can't stall the signing flow. */\nexport const DEFINITIONS_FETCH_TIMEOUT_MS = 20_000;\n\n/** Race a thunk against a deadline. Generic so it works with any FetchLike on\n * RN/Hermes; a timeout stops awaiting but does not cancel the socket. */\nexport async function runWithTimeout<T>(\n run: () => Promise<T>,\n timeoutMs: number = DEFINITIONS_FETCH_TIMEOUT_MS\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const deadline = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`definitions fetch timed out after ${timeoutMs}ms`)),\n timeoutMs\n );\n });\n try {\n return await Promise.race([run(), deadline]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nexport type FetchLike = (url: string) => Promise<{\n status: number;\n arrayBuffer(): Promise<ArrayBuffer>;\n}>;\n\nexport type FetchEthereumDefinitionsParams = {\n chainId?: number;\n slip44?: number;\n contractAddress?: string;\n baseUrl?: string;\n fetchImpl?: FetchLike;\n};\n\nexport type BuildEthereumDefinitionsForPathParams = {\n path: string;\n baseUrl?: string;\n fetchImpl?: FetchLike;\n};\n\nexport type BuildEthereumDefinitionsForSignTxParams = {\n path: string;\n chainId: number;\n to?: string;\n data?: string;\n baseUrl?: string;\n fetchImpl?: FetchLike;\n};\n\n/** Derive the SLIP-44 coin type (unhardened) from a BIP44-style path. */\nexport function getSlip44FromPath(path: string): number {\n const segments = path.trim().replace(/^m\\//i, '').split('/');\n const coinType = segments[1];\n const parsed = coinType === undefined ? NaN : Number(coinType.replace(/['hH]$/, ''));\n if (!Number.isFinite(parsed)) {\n throw new Error(`Cannot derive SLIP-44 coin type from path: ${path}`);\n }\n return parsed >= 0x80000000 ? parsed - 0x80000000 : parsed;\n}\n\nexport async function fetchEthereumDefinitions({\n chainId,\n slip44,\n contractAddress,\n baseUrl = DEFAULT_ETHEREUM_DEFINITIONS_BASE_URL,\n fetchImpl = globalThis.fetch,\n}: FetchEthereumDefinitionsParams): Promise<EvmEthereumDefinitions> {\n if (!chainId && !slip44) {\n throw new Error('fetchEthereumDefinitions requires chainId or slip44');\n }\n if (!fetchImpl) {\n return {};\n }\n\n const mode = chainId ? 'chain-id' : 'slip44';\n const id = chainId ?? slip44;\n const definitions: EvmEthereumDefinitions = {};\n\n const network = await fetchDefinitionHex(`${baseUrl}/${mode}/${id}/network.dat`, fetchImpl);\n if (network) {\n definitions.encodedNetwork = network;\n }\n\n if (contractAddress) {\n const token = await fetchDefinitionHex(\n `${baseUrl}/${mode}/${id}/token-${stripHexPrefix(contractAddress).toLowerCase()}.dat`,\n fetchImpl\n );\n if (token) {\n definitions.encodedToken = token;\n }\n }\n\n return definitions;\n}\n\nexport async function buildEthereumDefinitionsForPath({\n path,\n baseUrl,\n fetchImpl,\n}: BuildEthereumDefinitionsForPathParams): Promise<EvmEthereumDefinitions> {\n return fetchEthereumDefinitions({\n slip44: getSlip44FromPath(path),\n baseUrl,\n fetchImpl,\n });\n}\n\nexport async function buildEthereumDefinitionsForSignTx({\n path,\n chainId,\n to,\n data,\n baseUrl,\n fetchImpl,\n}: BuildEthereumDefinitionsForSignTxParams): Promise<EvmEthereumDefinitions | undefined> {\n const dataHex = stripHexPrefix(data ?? '');\n if (chainId === 1 && !dataHex) {\n return undefined;\n }\n return fetchEthereumDefinitions({\n chainId,\n slip44: getSlip44FromPath(path),\n contractAddress: dataHex && to ? to : undefined,\n baseUrl,\n fetchImpl,\n });\n}\n\nasync function fetchDefinitionHex(url: string, fetchImpl: FetchLike): Promise<string | undefined> {\n try {\n return await runWithTimeout(async () => {\n const response = await fetchImpl(url);\n if (response.status !== 200) {\n return undefined;\n }\n return arrayBufferToHex(await response.arrayBuffer());\n });\n } catch {\n return undefined;\n }\n}\n\nfunction arrayBufferToHex(value: ArrayBuffer): string {\n return Buffer.from(value).toString('hex');\n}\n\nfunction stripHexPrefix(value: string): string {\n return value.startsWith('0x') || value.startsWith('0X') ? value.slice(2) : value;\n}\n","import { runWithTimeout } from './ethereumDefinitions';\n\nimport type { FetchLike } from './ethereumDefinitions';\n\n/**\n * Opt-in helper for fetching a Trezor Solana token definition. Same contract as\n * the Ethereum helpers (see ./ethereumDefinitions): the SDK signing path NEVER\n * calls this — the caller fetches (through its own `baseUrl` proxy if desired)\n * and passes the bytes into `solSignTransaction` params as\n * `additionalInfo.encodedToken`. When omitted the device shows a generic token\n * confirmation screen and signing still completes.\n *\n * `fetchImpl` is a function and must not cross an IPC boundary — fetch in a\n * network-capable process and pass only the resulting hex string.\n */\n\n/** Default upstream (Trezor). Override with `baseUrl` to use your own proxy. */\nexport const DEFAULT_SOLANA_TOKEN_DEFINITION_BASE_URL =\n 'https://data.trezor.io/firmware/definitions/solana/token';\n\nexport type FetchSolanaTokenDefinitionParams = {\n tokenMint: string;\n baseUrl?: string;\n fetchImpl?: FetchLike;\n};\n\nexport async function fetchSolanaTokenDefinition({\n tokenMint,\n baseUrl = DEFAULT_SOLANA_TOKEN_DEFINITION_BASE_URL,\n fetchImpl = globalThis.fetch,\n}: FetchSolanaTokenDefinitionParams): Promise<string | undefined> {\n if (!fetchImpl) {\n return undefined;\n }\n try {\n return await runWithTimeout(async () => {\n const response = await fetchImpl(`${baseUrl}/${tokenMint}.dat`);\n if (response.status !== 200) {\n return undefined;\n }\n return Buffer.from(await response.arrayBuffer()).toString('hex');\n });\n } catch {\n return undefined;\n }\n}\n","export const TREZOR_SAFE_7_MODEL = 'T3W1';\n\nexport const TREZOR_BLE_SUPPORTED_MODELS = [TREZOR_SAFE_7_MODEL] as const;\n\nexport const TREZOR_BLE_PACKET_SIZE = 244;\n\nexport const TREZOR_BLE_UUIDS = {\n service: '8c000001-a59b-4d58-a9ad-073df69fa1b1',\n write: '8c000002-a59b-4d58-a9ad-073df69fa1b1',\n notify: '8c000003-a59b-4d58-a9ad-073df69fa1b1',\n push: '8c000004-a59b-4d58-a9ad-073df69fa1b1',\n batteryService: '0000180f-0000-1000-8000-00805f9b34fb',\n} as const;\n","import { TREZOR_BLE_SUPPORTED_MODELS, TREZOR_BLE_UUIDS } from '../constants';\n\nimport type { TrezorBleDescriptor } from '../types';\n\nexport function isTrezorSafe7BleName(name?: string): boolean {\n if (!name) return false;\n return /\\bTrezor\\b/i.test(name) && (/\\bSafe\\s*7\\b/i.test(name) || /\\bT3W1\\b/i.test(name));\n}\n\nexport function isTrezorBleSupportedModel(model?: string): boolean {\n if (!model) return false;\n const normalizedModel = model.trim().replace(/\\s+/g, ' ').toLowerCase();\n return (\n TREZOR_BLE_SUPPORTED_MODELS.some(\n supportedModel => supportedModel.toLowerCase() === normalizedModel\n ) ||\n normalizedModel === 'safe 7' ||\n normalizedModel === 'trezor safe 7'\n );\n}\n\nexport function isTrezorSafe7BleDescriptor(descriptor: TrezorBleDescriptor): boolean {\n return isTrezorBleDescriptor(descriptor);\n}\n\nexport function normalizeTrezorBleUuid(uuid: string): string {\n return uuid.replace(/-/g, '').toLowerCase();\n}\n\nexport function isTrezorBleServiceUuid(uuid: string): boolean {\n return normalizeTrezorBleUuid(uuid) === normalizeTrezorBleUuid(TREZOR_BLE_UUIDS.service);\n}\n\nexport function isTrezorBleDescriptor(descriptor: TrezorBleDescriptor): boolean {\n const serviceUuids = [\n ...(descriptor.serviceUUIDs ?? []),\n ...(descriptor.advertisedServiceUuids ?? []),\n ...(descriptor.serviceUuids ?? []),\n ];\n return serviceUuids.some(isTrezorBleServiceUuid);\n}\n\nexport function resolveTrezorBleConnectId(descriptor: TrezorBleDescriptor): string {\n return descriptor.id ?? descriptor.path ?? descriptor.deviceId ?? descriptor.name ?? '';\n}\n"]}
|