@ledgerhq/revault-sdk 0.18.0 → 0.18.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/lib/{chunk-BGVGHXMO.js → chunk-7F2SX4FL.js} +3 -2
- package/lib/chunk-7F2SX4FL.js.map +1 -0
- package/lib/{chunk-R2LW7L74.cjs → chunk-UCQR7XCK.cjs} +3 -2
- package/lib/chunk-UCQR7XCK.cjs.map +1 -0
- package/lib/device.cjs +2 -2
- package/lib/device.js +1 -1
- package/lib/engine.cjs +21 -21
- package/lib/engine.js +1 -1
- package/package.json +4 -3
- package/lib/chunk-BGVGHXMO.js.map +0 -1
- package/lib/chunk-R2LW7L74.cjs.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/device/api-device.ts
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
import axiosRetry from "axios-retry";
|
|
4
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
5
|
|
|
5
6
|
// src/device/constants.ts
|
|
6
7
|
var PATHS = {
|
|
@@ -31,7 +32,7 @@ var STREAMING_NEXT_ACTION = 1;
|
|
|
31
32
|
// src/device/api-device.ts
|
|
32
33
|
function createApiDevice(options, logger) {
|
|
33
34
|
const { genSeed } = options;
|
|
34
|
-
const sessionId =
|
|
35
|
+
const sessionId = uuidv4();
|
|
35
36
|
const deviceApi = axios.create({ baseURL: options.deviceApiUrl });
|
|
36
37
|
axiosRetry(deviceApi, {
|
|
37
38
|
retryDelay: axiosRetry.linearDelay(1e3),
|
|
@@ -494,4 +495,4 @@ export {
|
|
|
494
495
|
registerTransportModule,
|
|
495
496
|
withDevicePolling
|
|
496
497
|
};
|
|
497
|
-
//# sourceMappingURL=chunk-
|
|
498
|
+
//# sourceMappingURL=chunk-7F2SX4FL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/device/api-device.ts","../src/device/constants.ts","../src/device/utils.ts","../src/device/device-access.ts"],"sourcesContent":["import type {\n HsmCertificate,\n PsdAuthChallengeResponse,\n PsdPairingData,\n PubKey,\n} from \"@ledgerhq/revault-api\";\nimport axios from \"axios\";\nimport axiosRetry from \"axios-retry\";\nimport { v4 as uuidv4 } from \"uuid\";\n\nimport type { Logger } from \"@/types\";\n\nimport { APPID_VAULT_ADMINISTRATOR, PATHS, SCRIPT_IDS, STR_ROLES } from \"./constants\";\nimport type { Device } from \"./types\";\n\ntype Options = {\n /**\n * device-api URL\n */\n deviceApiUrl: string;\n\n /**\n * salt used by device-api\n */\n salt: string;\n\n /**\n * seed generation function used for device-api device spawn\n */\n genSeed: (p: { salt: string; index: number }) => string;\n};\n\nexport type ApiDevice = {\n /**\n * bind a device to the given device index\n */\n with: (index: number) => Device;\n};\n\nexport function createApiDevice(options: Options, logger: Logger): ApiDevice {\n const { genSeed } = options;\n const sessionId = uuidv4();\n const deviceApi = axios.create({ baseURL: options.deviceApiUrl });\n\n axiosRetry(deviceApi, {\n retryDelay: axiosRetry.linearDelay(1000),\n retries: 3,\n /* v8 ignore start */\n retryCondition: () => true,\n onRetry: (retryCount, err) => {\n const formattedErr = `${err.code ?? `UNKNOWN`} on ${err.config?.method?.toUpperCase() ?? \"UNKNOWN_METHOD\"} ${err.config?.url ?? \"UNKNOWN_URL\"}`;\n logger.warning(\n `Retrying request to device-api (attempt ${retryCount}) - Cause: ${formattedErr}`,\n );\n },\n /* v8 ignore stop */\n });\n\n deviceApi.interceptors.request.use(\n (config) => {\n /* v8 ignore start */\n logger.debug(\n `[device-api] ${config.method?.toUpperCase()} ${config.url}${config.data ? ` ${JSON.stringify(config.data)}` : \"\"}`,\n );\n if ([\"post\", \"put\"].includes(config.method!)) {\n // when interceptor is called in combination with axios-retry, config.data can be a string or an object\n // and we should return either a string or an object compatible with the original type\n const isStringMode = typeof config.data === \"string\";\n const data = (isStringMode ? JSON.parse(config.data) : config.data) as Record<\n string,\n unknown\n >;\n const enhancedData = {\n sessionID: sessionId, // Used to ensure dedicated session if someones else uses same seed\n psd_model: \"02\", // Stax\n version: 3, // Used to properly enable noise channel\n ...data,\n };\n config.data = isStringMode ? JSON.stringify(enhancedData) : enhancedData;\n }\n return config;\n },\n /* v8 ignore stop */\n (error: Error) => Promise.reject(error),\n );\n\n return {\n with: (index: number): Device => {\n const seed = genSeed({ salt: options.salt, index });\n\n const authenticate = async ({\n challenge,\n workspaceName,\n }: {\n challenge: PsdAuthChallengeResponse;\n workspaceName: string;\n }): Promise<string> => {\n const role = challenge.role;\n if (role === \"READ_ONLY_API_KEY\")\n throw new Error(\"Invalid role for authenticate: READ_ONLY_API_KEY\");\n const { data } = await deviceApi.post<string>(\"/authenticate\", {\n seed,\n challenge: Buffer.from(challenge.challenge, \"base64\").toString(\"hex\"),\n application: APPID_VAULT_ADMINISTRATOR,\n key_handle: challenge.keyHandle,\n name: challenge.name,\n role: STR_ROLES[role],\n type: \"02\", // Stax\n workspaceName,\n });\n return data;\n };\n\n const finalizePairingHandshake = async (params: {\n handshake: string;\n partitionId: string;\n }): Promise<string> => {\n const { handshake, partitionId } = params;\n const payload = { seed, handshake, ciphertext: partitionId };\n const res = await deviceApi.post<string>(\"/finalize-pairing-handshake\", payload);\n return res.data;\n };\n\n const generateSeedFragment = async (): Promise<string> => {\n const payload = {\n seed,\n path: PATHS.KEY_MATERIAL,\n goal: 0, // seed fragment\n };\n const { data } = await deviceApi.post<string>(\"/generate-key-component\", payload);\n return data;\n };\n\n const generateWrappingKeyFragment = async (): Promise<string> => {\n const goal = 1; // means Wrapping Key\n const payload = { seed, goal, path: PATHS.KEY_MATERIAL };\n const res = await deviceApi.post<string>(\"/generate-key-fragments\", payload);\n return Buffer.from(res.data, \"hex\").toString(\"base64\");\n };\n\n const getAttestation = async (): Promise<{\n attestation: {\n codeHash: string;\n attestationPub: string;\n certificate: string;\n };\n buffer: Buffer;\n }> => {\n const { data: attestationStr } = await deviceApi.post<string>(\"/get-attestation\", { seed });\n const attestationBuffer = Buffer.from(attestationStr, \"hex\");\n const certLen = attestationBuffer.readInt8(32 + 65 + 1);\n return {\n attestation: {\n codeHash: attestationBuffer.slice(0, 32).toString(\"base64\"),\n attestationPub: attestationBuffer.slice(32, 32 + 65).toString(\"base64\"),\n certificate: attestationBuffer.slice(32 + 65, 32 + 65 + 2 + certLen).toString(\"base64\"),\n },\n buffer: attestationBuffer,\n };\n };\n\n const getConfidentialityKey = async (): Promise<{\n confidentialityKeyCurve25519: string;\n confidentialityKeySignature: string;\n }> => {\n const res = await deviceApi.post<{\n confidentiality_key_curve25519: string;\n confidentiality_key_signature: string;\n }>(\"/get-confidentiality-key\", { seed });\n return {\n confidentialityKeyCurve25519: res.data.confidentiality_key_curve25519,\n confidentialityKeySignature: res.data.confidentiality_key_signature,\n };\n };\n\n const getConfidentialityPubKey = async (): Promise<{ pubKey: string; signature: string }> => {\n return getPublicKey({ path: PATHS.CONFIDENTIALITY });\n };\n\n const getDeviceUserId = async (): Promise<string> => {\n const res = await deviceApi.post<{ blue_device_id: string }>(\"/blue-device-id\", { seed });\n return res.data.blue_device_id.toUpperCase();\n };\n\n const getU2FPubKey = async (): Promise<{ pubKey: string; signature: string }> => {\n return getPublicKey({ path: PATHS.U2F });\n };\n\n const getValidationPubKey = async (): Promise<{ pubKey: string; signature: string }> => {\n return getPublicKey({ path: PATHS.VALIDATION });\n };\n\n const hasPairing = async (): Promise<boolean> => {\n const res = await deviceApi.post<string[]>(\"/get-partition-ids\", { seed });\n return res.data.length > 0;\n };\n\n const initPairingHandshake = async (): Promise<{ pubKey: string; signature: string }> => {\n const { data } = await deviceApi.post<{ attestation: string; pubKey: string }>(\n \"/initiate-pairing-handshake\",\n { seed },\n );\n return {\n pubKey: data.pubKey,\n signature: Buffer.from(data.attestation, \"hex\").toString(\"base64\"),\n };\n };\n\n const openSession = async (params: {\n secureChannel: { pubKey: PubKey; certificate: HsmCertificate };\n }): Promise<void> => {\n const { secureChannel } = params;\n const signature = Buffer.from(secureChannel.certificate.signature, \"base64\");\n const attestationPubKey = Buffer.from(secureChannel.certificate.attestationPub, \"base64\");\n const attestationCertificate = Buffer.from(secureChannel.certificate.certificate, \"base64\");\n const attestation = Buffer.concat([\n Buffer.from([signature.length]),\n signature,\n attestationPubKey,\n Buffer.from([attestationCertificate.length]),\n attestationCertificate,\n ]);\n const payload = {\n seed,\n path: PATHS.CONFIDENTIALITY,\n pubKey: secureChannel.pubKey,\n attestation: attestation.toString(\"hex\"),\n scriptId: SCRIPT_IDS.INIT_SESSION,\n };\n const { data: deviceResponse } = await deviceApi.post<string>(\"/open-session\", payload);\n if (deviceResponse !== \"9000\") {\n throw new Error(`device-api device failed with code: ${deviceResponse}`);\n }\n };\n\n const register = async (params: {\n challenge: string;\n u2fRegisterData: string;\n name: string;\n role: \"ADMIN\" | \"SHARED_OWNER\" | \"OPERATOR\";\n }): Promise<{\n u2fRegister: Buffer;\n keyHandle: Buffer;\n rfu: number;\n pubKey: string;\n }> => {\n const { challenge, u2fRegisterData, name, role } = params;\n const payload = {\n seed,\n challenge,\n name,\n role: STR_ROLES[role],\n hsm_data: u2fRegisterData,\n type: \"02\", // Stax\n application: APPID_VAULT_ADMINISTRATOR,\n };\n const { data } = await deviceApi.post<string>(\"/register\", payload);\n const response = Buffer.from(data, \"hex\");\n let i = 0;\n const rfu = response.slice(i, (i += 1))[0]!;\n const pubKey = response.slice(i, (i += 65)).toString(\"hex\");\n const keyHandleLength = response.slice(i, ++i)[0]!;\n const keyHandle = response.slice(i, (i += keyHandleLength));\n return {\n u2fRegister: response.slice(0, response.length - 2),\n keyHandle,\n rfu,\n pubKey,\n };\n };\n\n const registerData = async (params: { challenge: string }): Promise<string> => {\n const { challenge } = params;\n const payload = { seed, challenge };\n const res = await deviceApi.post<string>(\"/u2f-register-data\", payload);\n return res.data;\n };\n\n const setPairing = async ({ pairing }: { pairing: PsdPairingData }): Promise<void> => {\n const payload = {\n seed,\n blob: pairing.blob,\n pairing: {\n bootstrap_enabled: pairing.pairing.bootstrapEnabled,\n partition_key: pairing.pairing.partitionKey,\n workspace_name: pairing.pairing.workspaceName,\n },\n partition_key_attestation: {\n code_hash: pairing.partitionKeyAttestation.codeHash,\n attestation_pub: pairing.partitionKeyAttestation.attestationPub,\n certificate: pairing.partitionKeyAttestation.certificate,\n signature: pairing.partitionKeyAttestation.signature,\n },\n };\n await deviceApi.post(\"/set-partition-key\", payload);\n };\n\n const startKKPatternAsResponder = async (params: {\n handshake: string;\n handshakeAttestation: HsmCertificate;\n partitionId: string;\n }): Promise<{ handshake: string; signature: string }> => {\n const payload = {\n seed,\n script_id: SCRIPT_IDS.ACCOUNT_MANAGER_SESSION,\n handshake: params.handshake,\n // for device-api it's normal to have empty string as handshake_attestation\n handshake_attestation: \"\",\n partition_id: params.partitionId,\n };\n const { data } = await deviceApi.post<string>(\"/start-kk-pattern-as-responder\", payload);\n const buffer = Buffer.from(data, \"hex\");\n const dataLength = buffer.readInt8(0);\n return {\n handshake: buffer.slice(1, dataLength + 1).toString(\"base64\"),\n signature: buffer.slice(dataLength + 1).toString(\"base64\"),\n };\n };\n\n const startKPatternAsResponder = async (params: {\n handshake: string;\n handshakeAttestation: HsmCertificate;\n partitionId: string;\n }): Promise<void> => {\n const { handshake, partitionId } = params;\n const payload = {\n seed,\n script_id: SCRIPT_IDS.ACCOUNT_MANAGER_SESSION,\n handshake,\n // for device-api it's normal to have empty string as handshake_attestation\n handshake_attestation: \"\",\n partition_id: partitionId,\n };\n const { data: deviceResponse } = await deviceApi.post<string>(\n \"/start-k-pattern-as-responder\",\n payload,\n );\n if (deviceResponse !== \"9000\") {\n throw new Error(`device-api device failed with code: ${deviceResponse}`);\n }\n };\n\n const getPublicKey = async (params: {\n path: string;\n }): Promise<{ pubKey: string; signature: string }> => {\n const { path } = params;\n type DeviceApiGetPubKeyResponse = { attestation: string; pubKey: string };\n const payload = { seed, secp256k1: true, path };\n const res = await deviceApi.post<DeviceApiGetPubKeyResponse>(\"/get-public-key\", payload);\n return {\n pubKey: res.data.pubKey,\n signature: Buffer.from(res.data.attestation, \"hex\").toString(\"base64\"),\n };\n };\n\n const validateVaultOperation = async (params: { actions: string[] }): Promise<string> => {\n const { actions } = params;\n const payload = {\n seed,\n path: PATHS.VALIDATION,\n actions,\n };\n const { data: signature } = await deviceApi.post<string>(\"/validate-op\", payload);\n return signature;\n };\n\n const sendAPDU = (_apdu: string): Promise<string> => {\n logger.info(\"Can't send arbitrary APDU to software device\");\n return Promise.resolve(\"9000\");\n };\n\n const getVersion = async (): Promise<string> => {\n return \"1.2.3\";\n };\n\n const poll = async (): Promise<{ isAppOpened: boolean }> => {\n await new Promise((r) => setTimeout(r, 1e3));\n return { isAppOpened: true };\n };\n\n const pollApp = async (): Promise<string> => {\n await new Promise((r) => setTimeout(r, 1e3));\n return \"9000\";\n };\n\n return {\n authenticate,\n finalizePairingHandshake,\n generateSeedFragment,\n generateWrappingKeyFragment,\n getAttestation,\n getConfidentialityKey,\n getConfidentialityPubKey,\n getDeviceUserId,\n getU2FPubKey,\n getValidationPubKey,\n hasPairing,\n initPairingHandshake,\n openSession,\n register,\n registerData,\n setPairing,\n startKKPatternAsResponder,\n startKPatternAsResponder,\n validateVaultOperation,\n getVersion,\n sendAPDU,\n poll,\n pollApp,\n };\n },\n };\n}\n","export const PATHS = {\n U2F: \"5655636'/5583430'\",\n CONFIDENTIALITY: \"5655636'/4410950'\",\n KEY_MATERIAL: \"5655636'/4932953'\",\n VALIDATION: \"5655636'/5652812'\",\n} as const;\n\nexport const STATUS_LENGTH = 2;\n\nexport const SCRIPT_IDS = {\n INIT_SESSION: 3,\n ACCOUNT_MANAGER_SESSION: 2,\n} as const;\n\nexport const STR_ROLES: Record<\"OPERATOR\" | \"ADMIN\" | \"SHARED_OWNER\", string> = {\n OPERATOR: \"00\",\n ADMIN: \"01\",\n SHARED_OWNER: \"02\",\n} as const;\n\nexport const APPID_VAULT_ADMINISTRATOR =\n \"ad5be1a1fe011ce7f53ae081a22ae000a42021f3f94106a3bac9f76e8230e4b9\";\n\nconst all61xxStatus = [];\nfor (let i = 0x6100; i <= 0x61ff; i++) {\n all61xxStatus.push(i);\n}\n\nexport const PAGINATED_STATUS = all61xxStatus;\nexport const STREAMING_RESPONSE = 0x02;\nexport const STREAMING_NEXT_ACTION = 0x01;\n","import { TransportStatusError } from \"@ledgerhq/errors\";\n\nexport const isTransportStatusError = (error: unknown): error is TransportStatusError => {\n return (\n error instanceof TransportStatusError ||\n (error instanceof Error && \"statusCode\" in error && error.message.startsWith(\"Ledger device:\"))\n );\n};\n\nconst CANCEL_STATUS_CODES = [\n 0x6985, // cancel on Ledger Blue\n\n // STAX\n 0xb00c, // cancel collect wk fragment\n 0xb00d, // cancel user registration\n 0xb010, // cancel review\n 0xb012, // cancel master seed generation\n];\n\nexport const isDeviceCancelError = (error: Error): boolean => {\n return isTransportStatusError(error) && CANCEL_STATUS_CODES.includes(error.statusCode);\n};\n","// ------------------------------------------------------------------------------------------------\n// ------------------------------------------------------------------------------------------------\n// THIS IS SOME HORRIBLE CODE IMPORTED JUST\n// FOR THE SAKE OF NOT IMPORTING UGLY, WICKED, AND\n// BROKEN @ledgerhq/live-* LIBRARIES\n//\n// IT'S MEANT TO BE CONSIDERED AS A BLACK BOX\n// ------------------------------------------------------------------------------------------------\n// ------------------------------------------------------------------------------------------------\nimport {\n BluetoothRequired,\n CantOpenDevice,\n DeviceHalted,\n FirmwareOrAppUpdateRequired,\n TransportInterfaceNotAvailable,\n TransportStatusError,\n TransportWebUSBGestureRequired,\n} from \"@ledgerhq/errors\";\nimport type Transport from \"@ledgerhq/hw-transport\";\nimport { Observable, throwError, timer } from \"rxjs\";\nimport { catchError, mergeMap, retryWhen } from \"rxjs/operators\";\n\nconst initialErrorRemapping = (error: Error): Observable<never> =>\n throwError(\n error && error instanceof TransportStatusError && error.statusCode === 0x6faa\n ? new DeviceHalted(error.message)\n : error instanceof TransportStatusError && error.statusCode === 0x6b00\n ? new FirmwareOrAppUpdateRequired(error.message)\n : error,\n );\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nconst never = new Promise(() => {});\n\ntype Discovery = Observable<{\n type: \"add\" | \"remove\";\n id: string;\n name: string;\n}>;\n\nexport type TransportModule = {\n // unique transport name that identify the transport module\n id: string;\n // open a device by an id, this id must be unique across all modules\n // you can typically prefix it with `something|` that identify it globally\n // returns falsy if the transport module can't handle this id\n // here, open means we want to START doing something with the transport\n open: (id: string) => Promise<Transport> | null | undefined;\n // here, close means we want to STOP doing something with the transport\n close?: (transport: Transport, id: string) => Promise<void> | null | undefined;\n // disconnect/interrupt a device connection globally\n // returns falsy if the transport module can't handle this id\n disconnect: (id: string) => Promise<void> | null | undefined;\n // optional observable that allows to discover a transport\n discovery?: Discovery;\n};\n\nconst modules: TransportModule[] = [];\n\nexport const registerTransportModule = (module: TransportModule): void => {\n modules.push(module);\n};\n\nexport const close = (transport: Transport, deviceId: string): Promise<void> => {\n for (const m of modules) {\n const p = m.close?.(transport, deviceId);\n if (p) return p;\n }\n // fallback on an actual close\n return transport.close();\n};\n\nexport const open = (deviceId: string): Promise<Transport> => {\n for (const m of modules) {\n const p = m.open(deviceId);\n if (p) return p;\n }\n return Promise.reject(new Error(`Can't find handler to open ${deviceId}`));\n};\n\nconst transportFinally =\n (cleanup: () => Promise<void>) =>\n <T>(observable: Observable<T>): Observable<T> =>\n // @ts-expect-error typescript not checking against the instanceof\n Observable.create((o) => {\n let done = false;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const finalize = (): any => {\n if (done) return never;\n done = true;\n return cleanup();\n };\n\n const sub = observable.subscribe({\n next: (e) => o.next(e),\n complete: () => {\n void finalize().then(() => o.complete());\n },\n error: (e) => {\n void finalize().then(() => o.error(e));\n },\n });\n return () => {\n sub.unsubscribe();\n void finalize();\n };\n });\n\ntype AccessHook = () => () => void;\n\nconst accessHooks: AccessHook[] = [];\n\nconst deviceQueues = {};\n\nexport const withDevice =\n (deviceId: string) =>\n <T>(job: (t: Transport) => Observable<T>): Observable<T> =>\n new Observable((o) => {\n // @ts-expect-error typescript not checking against the instanceof\n let unsubscribed;\n // @ts-expect-error typescript not checking against the instanceof\n let sub;\n // @ts-expect-error typescript not checking against the instanceof\n const deviceQueue = deviceQueues[deviceId] ?? Promise.resolve();\n\n // @ts-expect-error typescript not checking against the instanceof\n const finalize = (transport, cleanups): Promise<void> =>\n close(transport, deviceId)\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n .catch(() => {})\n .then(() => {\n // @ts-expect-error typescript not checking against the instanceof\n cleanups.forEach((c) => c());\n });\n\n // when we'll finish all the current job, we'll call finish\n // @ts-expect-error typescript not checking against the instanceof\n let finish;\n // this new promise is the next exec queue\n // @ts-expect-error typescript not checking against the instanceof\n deviceQueues[deviceId] = new Promise((resolve) => {\n finish = resolve;\n });\n // for any new job, we'll now wait the exec queue to be available\n deviceQueue\n .then(() => open(deviceId)) // open the transport\n // @ts-expect-error typescript not checking against the instanceof\n .then(async (transport) => {\n // @ts-expect-error typescript not checking against the instanceof\n if (unsubscribed) {\n // it was unsubscribed prematurely\n // @ts-expect-error typescript not checking against the instanceof\n return finalize(transport, [finish]);\n }\n\n if (\n transport.requestConnectionPriority &&\n typeof transport.requestConnectionPriority === \"function\"\n ) {\n await transport.requestConnectionPriority(\"High\");\n }\n\n return transport;\n })\n // @ts-expect-error typescript not checking against the instanceof\n .catch((e) => {\n // @ts-expect-error typescript not checking against the instanceof\n finish();\n if (e instanceof BluetoothRequired) throw e;\n if (e instanceof TransportWebUSBGestureRequired) throw e;\n if (e instanceof TransportInterfaceNotAvailable) throw e;\n throw new CantOpenDevice(e.message);\n })\n // @ts-expect-error typescript not checking against the instanceof\n .then((transport) => {\n if (!transport) return;\n\n // @ts-expect-error typescript not checking against the instanceof\n if (unsubscribed) {\n // it was unsubscribed prematurely\n // @ts-expect-error typescript not checking against the instanceof\n return finalize(transport, [finish]);\n }\n\n const cleanups = accessHooks.map((hook) => hook());\n sub = job(transport) // $FlowFixMe\n .pipe(\n catchError(initialErrorRemapping),\n // $FlowFixMe\n transportFinally(() =>\n // @ts-expect-error typescript not checking against the instanceof\n finalize(transport, [...cleanups, finish]),\n ),\n )\n .subscribe(o);\n })\n // @ts-expect-error typescript not checking against the instanceof\n .catch((error) => o.error(error));\n return () => {\n unsubscribed = true;\n // @ts-expect-error typescript not checking against the instanceof\n if (sub) sub.unsubscribe();\n };\n });\n\nconst WITH_DEVICE_POLLING_DELAY = 500;\n\nexport const retryWhileErrors =\n (acceptError: (arg0: Error) => boolean) =>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (attempts: Observable<any>): Observable<any> =>\n attempts.pipe(\n mergeMap((error) => {\n if (!acceptError(error)) {\n return throwError(error);\n }\n\n return timer(WITH_DEVICE_POLLING_DELAY);\n }),\n );\n\nexport const withDevicePolling =\n (deviceId: string) =>\n <T>(\n job: (arg0: Transport) => Observable<T>,\n acceptError: (arg0: Error) => boolean = () => false,\n ): Observable<T> =>\n withDevice(deviceId)(job).pipe(retryWhen(retryWhileErrors(acceptError)));\n"],"mappings":"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","names":["TransportStatusError"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/device/api-device.ts
|
|
2
2
|
var _axios = require('axios'); var _axios2 = _interopRequireDefault(_axios);
|
|
3
3
|
var _axiosretry = require('axios-retry'); var _axiosretry2 = _interopRequireDefault(_axiosretry);
|
|
4
|
+
var _uuid = require('uuid');
|
|
4
5
|
|
|
5
6
|
// src/device/constants.ts
|
|
6
7
|
var PATHS = {
|
|
@@ -31,7 +32,7 @@ var STREAMING_NEXT_ACTION = 1;
|
|
|
31
32
|
// src/device/api-device.ts
|
|
32
33
|
function createApiDevice(options, logger) {
|
|
33
34
|
const { genSeed } = options;
|
|
34
|
-
const sessionId =
|
|
35
|
+
const sessionId = _uuid.v4.call(void 0, );
|
|
35
36
|
const deviceApi = _axios2.default.create({ baseURL: options.deviceApiUrl });
|
|
36
37
|
_axiosretry2.default.call(void 0, deviceApi, {
|
|
37
38
|
retryDelay: _axiosretry2.default.linearDelay(1e3),
|
|
@@ -494,4 +495,4 @@ var withDevicePolling = (deviceId) => (job, acceptError = () => false) => withDe
|
|
|
494
495
|
|
|
495
496
|
|
|
496
497
|
exports.PATHS = PATHS; exports.STATUS_LENGTH = STATUS_LENGTH; exports.SCRIPT_IDS = SCRIPT_IDS; exports.STR_ROLES = STR_ROLES; exports.APPID_VAULT_ADMINISTRATOR = APPID_VAULT_ADMINISTRATOR; exports.PAGINATED_STATUS = PAGINATED_STATUS; exports.STREAMING_RESPONSE = STREAMING_RESPONSE; exports.STREAMING_NEXT_ACTION = STREAMING_NEXT_ACTION; exports.createApiDevice = createApiDevice; exports.isTransportStatusError = isTransportStatusError; exports.isDeviceCancelError = isDeviceCancelError; exports.registerTransportModule = registerTransportModule; exports.withDevicePolling = withDevicePolling;
|
|
497
|
-
//# sourceMappingURL=chunk-
|
|
498
|
+
//# sourceMappingURL=chunk-UCQR7XCK.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/_work/revault/revault/packages/sdk/lib/chunk-UCQR7XCK.cjs","../src/device/api-device.ts","../src/device/constants.ts","../src/device/utils.ts","../src/device/device-access.ts"],"names":["TransportStatusError"],"mappings":"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","file":"/home/runner/_work/revault/revault/packages/sdk/lib/chunk-UCQR7XCK.cjs","sourcesContent":[null,"import type {\n HsmCertificate,\n PsdAuthChallengeResponse,\n PsdPairingData,\n PubKey,\n} from \"@ledgerhq/revault-api\";\nimport axios from \"axios\";\nimport axiosRetry from \"axios-retry\";\nimport { v4 as uuidv4 } from \"uuid\";\n\nimport type { Logger } from \"@/types\";\n\nimport { APPID_VAULT_ADMINISTRATOR, PATHS, SCRIPT_IDS, STR_ROLES } from \"./constants\";\nimport type { Device } from \"./types\";\n\ntype Options = {\n /**\n * device-api URL\n */\n deviceApiUrl: string;\n\n /**\n * salt used by device-api\n */\n salt: string;\n\n /**\n * seed generation function used for device-api device spawn\n */\n genSeed: (p: { salt: string; index: number }) => string;\n};\n\nexport type ApiDevice = {\n /**\n * bind a device to the given device index\n */\n with: (index: number) => Device;\n};\n\nexport function createApiDevice(options: Options, logger: Logger): ApiDevice {\n const { genSeed } = options;\n const sessionId = uuidv4();\n const deviceApi = axios.create({ baseURL: options.deviceApiUrl });\n\n axiosRetry(deviceApi, {\n retryDelay: axiosRetry.linearDelay(1000),\n retries: 3,\n /* v8 ignore start */\n retryCondition: () => true,\n onRetry: (retryCount, err) => {\n const formattedErr = `${err.code ?? `UNKNOWN`} on ${err.config?.method?.toUpperCase() ?? \"UNKNOWN_METHOD\"} ${err.config?.url ?? \"UNKNOWN_URL\"}`;\n logger.warning(\n `Retrying request to device-api (attempt ${retryCount}) - Cause: ${formattedErr}`,\n );\n },\n /* v8 ignore stop */\n });\n\n deviceApi.interceptors.request.use(\n (config) => {\n /* v8 ignore start */\n logger.debug(\n `[device-api] ${config.method?.toUpperCase()} ${config.url}${config.data ? ` ${JSON.stringify(config.data)}` : \"\"}`,\n );\n if ([\"post\", \"put\"].includes(config.method!)) {\n // when interceptor is called in combination with axios-retry, config.data can be a string or an object\n // and we should return either a string or an object compatible with the original type\n const isStringMode = typeof config.data === \"string\";\n const data = (isStringMode ? JSON.parse(config.data) : config.data) as Record<\n string,\n unknown\n >;\n const enhancedData = {\n sessionID: sessionId, // Used to ensure dedicated session if someones else uses same seed\n psd_model: \"02\", // Stax\n version: 3, // Used to properly enable noise channel\n ...data,\n };\n config.data = isStringMode ? JSON.stringify(enhancedData) : enhancedData;\n }\n return config;\n },\n /* v8 ignore stop */\n (error: Error) => Promise.reject(error),\n );\n\n return {\n with: (index: number): Device => {\n const seed = genSeed({ salt: options.salt, index });\n\n const authenticate = async ({\n challenge,\n workspaceName,\n }: {\n challenge: PsdAuthChallengeResponse;\n workspaceName: string;\n }): Promise<string> => {\n const role = challenge.role;\n if (role === \"READ_ONLY_API_KEY\")\n throw new Error(\"Invalid role for authenticate: READ_ONLY_API_KEY\");\n const { data } = await deviceApi.post<string>(\"/authenticate\", {\n seed,\n challenge: Buffer.from(challenge.challenge, \"base64\").toString(\"hex\"),\n application: APPID_VAULT_ADMINISTRATOR,\n key_handle: challenge.keyHandle,\n name: challenge.name,\n role: STR_ROLES[role],\n type: \"02\", // Stax\n workspaceName,\n });\n return data;\n };\n\n const finalizePairingHandshake = async (params: {\n handshake: string;\n partitionId: string;\n }): Promise<string> => {\n const { handshake, partitionId } = params;\n const payload = { seed, handshake, ciphertext: partitionId };\n const res = await deviceApi.post<string>(\"/finalize-pairing-handshake\", payload);\n return res.data;\n };\n\n const generateSeedFragment = async (): Promise<string> => {\n const payload = {\n seed,\n path: PATHS.KEY_MATERIAL,\n goal: 0, // seed fragment\n };\n const { data } = await deviceApi.post<string>(\"/generate-key-component\", payload);\n return data;\n };\n\n const generateWrappingKeyFragment = async (): Promise<string> => {\n const goal = 1; // means Wrapping Key\n const payload = { seed, goal, path: PATHS.KEY_MATERIAL };\n const res = await deviceApi.post<string>(\"/generate-key-fragments\", payload);\n return Buffer.from(res.data, \"hex\").toString(\"base64\");\n };\n\n const getAttestation = async (): Promise<{\n attestation: {\n codeHash: string;\n attestationPub: string;\n certificate: string;\n };\n buffer: Buffer;\n }> => {\n const { data: attestationStr } = await deviceApi.post<string>(\"/get-attestation\", { seed });\n const attestationBuffer = Buffer.from(attestationStr, \"hex\");\n const certLen = attestationBuffer.readInt8(32 + 65 + 1);\n return {\n attestation: {\n codeHash: attestationBuffer.slice(0, 32).toString(\"base64\"),\n attestationPub: attestationBuffer.slice(32, 32 + 65).toString(\"base64\"),\n certificate: attestationBuffer.slice(32 + 65, 32 + 65 + 2 + certLen).toString(\"base64\"),\n },\n buffer: attestationBuffer,\n };\n };\n\n const getConfidentialityKey = async (): Promise<{\n confidentialityKeyCurve25519: string;\n confidentialityKeySignature: string;\n }> => {\n const res = await deviceApi.post<{\n confidentiality_key_curve25519: string;\n confidentiality_key_signature: string;\n }>(\"/get-confidentiality-key\", { seed });\n return {\n confidentialityKeyCurve25519: res.data.confidentiality_key_curve25519,\n confidentialityKeySignature: res.data.confidentiality_key_signature,\n };\n };\n\n const getConfidentialityPubKey = async (): Promise<{ pubKey: string; signature: string }> => {\n return getPublicKey({ path: PATHS.CONFIDENTIALITY });\n };\n\n const getDeviceUserId = async (): Promise<string> => {\n const res = await deviceApi.post<{ blue_device_id: string }>(\"/blue-device-id\", { seed });\n return res.data.blue_device_id.toUpperCase();\n };\n\n const getU2FPubKey = async (): Promise<{ pubKey: string; signature: string }> => {\n return getPublicKey({ path: PATHS.U2F });\n };\n\n const getValidationPubKey = async (): Promise<{ pubKey: string; signature: string }> => {\n return getPublicKey({ path: PATHS.VALIDATION });\n };\n\n const hasPairing = async (): Promise<boolean> => {\n const res = await deviceApi.post<string[]>(\"/get-partition-ids\", { seed });\n return res.data.length > 0;\n };\n\n const initPairingHandshake = async (): Promise<{ pubKey: string; signature: string }> => {\n const { data } = await deviceApi.post<{ attestation: string; pubKey: string }>(\n \"/initiate-pairing-handshake\",\n { seed },\n );\n return {\n pubKey: data.pubKey,\n signature: Buffer.from(data.attestation, \"hex\").toString(\"base64\"),\n };\n };\n\n const openSession = async (params: {\n secureChannel: { pubKey: PubKey; certificate: HsmCertificate };\n }): Promise<void> => {\n const { secureChannel } = params;\n const signature = Buffer.from(secureChannel.certificate.signature, \"base64\");\n const attestationPubKey = Buffer.from(secureChannel.certificate.attestationPub, \"base64\");\n const attestationCertificate = Buffer.from(secureChannel.certificate.certificate, \"base64\");\n const attestation = Buffer.concat([\n Buffer.from([signature.length]),\n signature,\n attestationPubKey,\n Buffer.from([attestationCertificate.length]),\n attestationCertificate,\n ]);\n const payload = {\n seed,\n path: PATHS.CONFIDENTIALITY,\n pubKey: secureChannel.pubKey,\n attestation: attestation.toString(\"hex\"),\n scriptId: SCRIPT_IDS.INIT_SESSION,\n };\n const { data: deviceResponse } = await deviceApi.post<string>(\"/open-session\", payload);\n if (deviceResponse !== \"9000\") {\n throw new Error(`device-api device failed with code: ${deviceResponse}`);\n }\n };\n\n const register = async (params: {\n challenge: string;\n u2fRegisterData: string;\n name: string;\n role: \"ADMIN\" | \"SHARED_OWNER\" | \"OPERATOR\";\n }): Promise<{\n u2fRegister: Buffer;\n keyHandle: Buffer;\n rfu: number;\n pubKey: string;\n }> => {\n const { challenge, u2fRegisterData, name, role } = params;\n const payload = {\n seed,\n challenge,\n name,\n role: STR_ROLES[role],\n hsm_data: u2fRegisterData,\n type: \"02\", // Stax\n application: APPID_VAULT_ADMINISTRATOR,\n };\n const { data } = await deviceApi.post<string>(\"/register\", payload);\n const response = Buffer.from(data, \"hex\");\n let i = 0;\n const rfu = response.slice(i, (i += 1))[0]!;\n const pubKey = response.slice(i, (i += 65)).toString(\"hex\");\n const keyHandleLength = response.slice(i, ++i)[0]!;\n const keyHandle = response.slice(i, (i += keyHandleLength));\n return {\n u2fRegister: response.slice(0, response.length - 2),\n keyHandle,\n rfu,\n pubKey,\n };\n };\n\n const registerData = async (params: { challenge: string }): Promise<string> => {\n const { challenge } = params;\n const payload = { seed, challenge };\n const res = await deviceApi.post<string>(\"/u2f-register-data\", payload);\n return res.data;\n };\n\n const setPairing = async ({ pairing }: { pairing: PsdPairingData }): Promise<void> => {\n const payload = {\n seed,\n blob: pairing.blob,\n pairing: {\n bootstrap_enabled: pairing.pairing.bootstrapEnabled,\n partition_key: pairing.pairing.partitionKey,\n workspace_name: pairing.pairing.workspaceName,\n },\n partition_key_attestation: {\n code_hash: pairing.partitionKeyAttestation.codeHash,\n attestation_pub: pairing.partitionKeyAttestation.attestationPub,\n certificate: pairing.partitionKeyAttestation.certificate,\n signature: pairing.partitionKeyAttestation.signature,\n },\n };\n await deviceApi.post(\"/set-partition-key\", payload);\n };\n\n const startKKPatternAsResponder = async (params: {\n handshake: string;\n handshakeAttestation: HsmCertificate;\n partitionId: string;\n }): Promise<{ handshake: string; signature: string }> => {\n const payload = {\n seed,\n script_id: SCRIPT_IDS.ACCOUNT_MANAGER_SESSION,\n handshake: params.handshake,\n // for device-api it's normal to have empty string as handshake_attestation\n handshake_attestation: \"\",\n partition_id: params.partitionId,\n };\n const { data } = await deviceApi.post<string>(\"/start-kk-pattern-as-responder\", payload);\n const buffer = Buffer.from(data, \"hex\");\n const dataLength = buffer.readInt8(0);\n return {\n handshake: buffer.slice(1, dataLength + 1).toString(\"base64\"),\n signature: buffer.slice(dataLength + 1).toString(\"base64\"),\n };\n };\n\n const startKPatternAsResponder = async (params: {\n handshake: string;\n handshakeAttestation: HsmCertificate;\n partitionId: string;\n }): Promise<void> => {\n const { handshake, partitionId } = params;\n const payload = {\n seed,\n script_id: SCRIPT_IDS.ACCOUNT_MANAGER_SESSION,\n handshake,\n // for device-api it's normal to have empty string as handshake_attestation\n handshake_attestation: \"\",\n partition_id: partitionId,\n };\n const { data: deviceResponse } = await deviceApi.post<string>(\n \"/start-k-pattern-as-responder\",\n payload,\n );\n if (deviceResponse !== \"9000\") {\n throw new Error(`device-api device failed with code: ${deviceResponse}`);\n }\n };\n\n const getPublicKey = async (params: {\n path: string;\n }): Promise<{ pubKey: string; signature: string }> => {\n const { path } = params;\n type DeviceApiGetPubKeyResponse = { attestation: string; pubKey: string };\n const payload = { seed, secp256k1: true, path };\n const res = await deviceApi.post<DeviceApiGetPubKeyResponse>(\"/get-public-key\", payload);\n return {\n pubKey: res.data.pubKey,\n signature: Buffer.from(res.data.attestation, \"hex\").toString(\"base64\"),\n };\n };\n\n const validateVaultOperation = async (params: { actions: string[] }): Promise<string> => {\n const { actions } = params;\n const payload = {\n seed,\n path: PATHS.VALIDATION,\n actions,\n };\n const { data: signature } = await deviceApi.post<string>(\"/validate-op\", payload);\n return signature;\n };\n\n const sendAPDU = (_apdu: string): Promise<string> => {\n logger.info(\"Can't send arbitrary APDU to software device\");\n return Promise.resolve(\"9000\");\n };\n\n const getVersion = async (): Promise<string> => {\n return \"1.2.3\";\n };\n\n const poll = async (): Promise<{ isAppOpened: boolean }> => {\n await new Promise((r) => setTimeout(r, 1e3));\n return { isAppOpened: true };\n };\n\n const pollApp = async (): Promise<string> => {\n await new Promise((r) => setTimeout(r, 1e3));\n return \"9000\";\n };\n\n return {\n authenticate,\n finalizePairingHandshake,\n generateSeedFragment,\n generateWrappingKeyFragment,\n getAttestation,\n getConfidentialityKey,\n getConfidentialityPubKey,\n getDeviceUserId,\n getU2FPubKey,\n getValidationPubKey,\n hasPairing,\n initPairingHandshake,\n openSession,\n register,\n registerData,\n setPairing,\n startKKPatternAsResponder,\n startKPatternAsResponder,\n validateVaultOperation,\n getVersion,\n sendAPDU,\n poll,\n pollApp,\n };\n },\n };\n}\n","export const PATHS = {\n U2F: \"5655636'/5583430'\",\n CONFIDENTIALITY: \"5655636'/4410950'\",\n KEY_MATERIAL: \"5655636'/4932953'\",\n VALIDATION: \"5655636'/5652812'\",\n} as const;\n\nexport const STATUS_LENGTH = 2;\n\nexport const SCRIPT_IDS = {\n INIT_SESSION: 3,\n ACCOUNT_MANAGER_SESSION: 2,\n} as const;\n\nexport const STR_ROLES: Record<\"OPERATOR\" | \"ADMIN\" | \"SHARED_OWNER\", string> = {\n OPERATOR: \"00\",\n ADMIN: \"01\",\n SHARED_OWNER: \"02\",\n} as const;\n\nexport const APPID_VAULT_ADMINISTRATOR =\n \"ad5be1a1fe011ce7f53ae081a22ae000a42021f3f94106a3bac9f76e8230e4b9\";\n\nconst all61xxStatus = [];\nfor (let i = 0x6100; i <= 0x61ff; i++) {\n all61xxStatus.push(i);\n}\n\nexport const PAGINATED_STATUS = all61xxStatus;\nexport const STREAMING_RESPONSE = 0x02;\nexport const STREAMING_NEXT_ACTION = 0x01;\n","import { TransportStatusError } from \"@ledgerhq/errors\";\n\nexport const isTransportStatusError = (error: unknown): error is TransportStatusError => {\n return (\n error instanceof TransportStatusError ||\n (error instanceof Error && \"statusCode\" in error && error.message.startsWith(\"Ledger device:\"))\n );\n};\n\nconst CANCEL_STATUS_CODES = [\n 0x6985, // cancel on Ledger Blue\n\n // STAX\n 0xb00c, // cancel collect wk fragment\n 0xb00d, // cancel user registration\n 0xb010, // cancel review\n 0xb012, // cancel master seed generation\n];\n\nexport const isDeviceCancelError = (error: Error): boolean => {\n return isTransportStatusError(error) && CANCEL_STATUS_CODES.includes(error.statusCode);\n};\n","// ------------------------------------------------------------------------------------------------\n// ------------------------------------------------------------------------------------------------\n// THIS IS SOME HORRIBLE CODE IMPORTED JUST\n// FOR THE SAKE OF NOT IMPORTING UGLY, WICKED, AND\n// BROKEN @ledgerhq/live-* LIBRARIES\n//\n// IT'S MEANT TO BE CONSIDERED AS A BLACK BOX\n// ------------------------------------------------------------------------------------------------\n// ------------------------------------------------------------------------------------------------\nimport {\n BluetoothRequired,\n CantOpenDevice,\n DeviceHalted,\n FirmwareOrAppUpdateRequired,\n TransportInterfaceNotAvailable,\n TransportStatusError,\n TransportWebUSBGestureRequired,\n} from \"@ledgerhq/errors\";\nimport type Transport from \"@ledgerhq/hw-transport\";\nimport { Observable, throwError, timer } from \"rxjs\";\nimport { catchError, mergeMap, retryWhen } from \"rxjs/operators\";\n\nconst initialErrorRemapping = (error: Error): Observable<never> =>\n throwError(\n error && error instanceof TransportStatusError && error.statusCode === 0x6faa\n ? new DeviceHalted(error.message)\n : error instanceof TransportStatusError && error.statusCode === 0x6b00\n ? new FirmwareOrAppUpdateRequired(error.message)\n : error,\n );\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nconst never = new Promise(() => {});\n\ntype Discovery = Observable<{\n type: \"add\" | \"remove\";\n id: string;\n name: string;\n}>;\n\nexport type TransportModule = {\n // unique transport name that identify the transport module\n id: string;\n // open a device by an id, this id must be unique across all modules\n // you can typically prefix it with `something|` that identify it globally\n // returns falsy if the transport module can't handle this id\n // here, open means we want to START doing something with the transport\n open: (id: string) => Promise<Transport> | null | undefined;\n // here, close means we want to STOP doing something with the transport\n close?: (transport: Transport, id: string) => Promise<void> | null | undefined;\n // disconnect/interrupt a device connection globally\n // returns falsy if the transport module can't handle this id\n disconnect: (id: string) => Promise<void> | null | undefined;\n // optional observable that allows to discover a transport\n discovery?: Discovery;\n};\n\nconst modules: TransportModule[] = [];\n\nexport const registerTransportModule = (module: TransportModule): void => {\n modules.push(module);\n};\n\nexport const close = (transport: Transport, deviceId: string): Promise<void> => {\n for (const m of modules) {\n const p = m.close?.(transport, deviceId);\n if (p) return p;\n }\n // fallback on an actual close\n return transport.close();\n};\n\nexport const open = (deviceId: string): Promise<Transport> => {\n for (const m of modules) {\n const p = m.open(deviceId);\n if (p) return p;\n }\n return Promise.reject(new Error(`Can't find handler to open ${deviceId}`));\n};\n\nconst transportFinally =\n (cleanup: () => Promise<void>) =>\n <T>(observable: Observable<T>): Observable<T> =>\n // @ts-expect-error typescript not checking against the instanceof\n Observable.create((o) => {\n let done = false;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const finalize = (): any => {\n if (done) return never;\n done = true;\n return cleanup();\n };\n\n const sub = observable.subscribe({\n next: (e) => o.next(e),\n complete: () => {\n void finalize().then(() => o.complete());\n },\n error: (e) => {\n void finalize().then(() => o.error(e));\n },\n });\n return () => {\n sub.unsubscribe();\n void finalize();\n };\n });\n\ntype AccessHook = () => () => void;\n\nconst accessHooks: AccessHook[] = [];\n\nconst deviceQueues = {};\n\nexport const withDevice =\n (deviceId: string) =>\n <T>(job: (t: Transport) => Observable<T>): Observable<T> =>\n new Observable((o) => {\n // @ts-expect-error typescript not checking against the instanceof\n let unsubscribed;\n // @ts-expect-error typescript not checking against the instanceof\n let sub;\n // @ts-expect-error typescript not checking against the instanceof\n const deviceQueue = deviceQueues[deviceId] ?? Promise.resolve();\n\n // @ts-expect-error typescript not checking against the instanceof\n const finalize = (transport, cleanups): Promise<void> =>\n close(transport, deviceId)\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n .catch(() => {})\n .then(() => {\n // @ts-expect-error typescript not checking against the instanceof\n cleanups.forEach((c) => c());\n });\n\n // when we'll finish all the current job, we'll call finish\n // @ts-expect-error typescript not checking against the instanceof\n let finish;\n // this new promise is the next exec queue\n // @ts-expect-error typescript not checking against the instanceof\n deviceQueues[deviceId] = new Promise((resolve) => {\n finish = resolve;\n });\n // for any new job, we'll now wait the exec queue to be available\n deviceQueue\n .then(() => open(deviceId)) // open the transport\n // @ts-expect-error typescript not checking against the instanceof\n .then(async (transport) => {\n // @ts-expect-error typescript not checking against the instanceof\n if (unsubscribed) {\n // it was unsubscribed prematurely\n // @ts-expect-error typescript not checking against the instanceof\n return finalize(transport, [finish]);\n }\n\n if (\n transport.requestConnectionPriority &&\n typeof transport.requestConnectionPriority === \"function\"\n ) {\n await transport.requestConnectionPriority(\"High\");\n }\n\n return transport;\n })\n // @ts-expect-error typescript not checking against the instanceof\n .catch((e) => {\n // @ts-expect-error typescript not checking against the instanceof\n finish();\n if (e instanceof BluetoothRequired) throw e;\n if (e instanceof TransportWebUSBGestureRequired) throw e;\n if (e instanceof TransportInterfaceNotAvailable) throw e;\n throw new CantOpenDevice(e.message);\n })\n // @ts-expect-error typescript not checking against the instanceof\n .then((transport) => {\n if (!transport) return;\n\n // @ts-expect-error typescript not checking against the instanceof\n if (unsubscribed) {\n // it was unsubscribed prematurely\n // @ts-expect-error typescript not checking against the instanceof\n return finalize(transport, [finish]);\n }\n\n const cleanups = accessHooks.map((hook) => hook());\n sub = job(transport) // $FlowFixMe\n .pipe(\n catchError(initialErrorRemapping),\n // $FlowFixMe\n transportFinally(() =>\n // @ts-expect-error typescript not checking against the instanceof\n finalize(transport, [...cleanups, finish]),\n ),\n )\n .subscribe(o);\n })\n // @ts-expect-error typescript not checking against the instanceof\n .catch((error) => o.error(error));\n return () => {\n unsubscribed = true;\n // @ts-expect-error typescript not checking against the instanceof\n if (sub) sub.unsubscribe();\n };\n });\n\nconst WITH_DEVICE_POLLING_DELAY = 500;\n\nexport const retryWhileErrors =\n (acceptError: (arg0: Error) => boolean) =>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (attempts: Observable<any>): Observable<any> =>\n attempts.pipe(\n mergeMap((error) => {\n if (!acceptError(error)) {\n return throwError(error);\n }\n\n return timer(WITH_DEVICE_POLLING_DELAY);\n }),\n );\n\nexport const withDevicePolling =\n (deviceId: string) =>\n <T>(\n job: (arg0: Transport) => Observable<T>,\n acceptError: (arg0: Error) => boolean = () => false,\n ): Observable<T> =>\n withDevice(deviceId)(job).pipe(retryWhen(retryWhileErrors(acceptError)));\n"]}
|
package/lib/device.cjs
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
var
|
|
6
|
+
var _chunkUCQR7XCKcjs = require('./chunk-UCQR7XCK.cjs');
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
exports.createApiDevice =
|
|
12
|
+
exports.createApiDevice = _chunkUCQR7XCKcjs.createApiDevice; exports.isDeviceCancelError = _chunkUCQR7XCKcjs.isDeviceCancelError; exports.isTransportStatusError = _chunkUCQR7XCKcjs.isTransportStatusError; exports.registerTransportModule = _chunkUCQR7XCKcjs.registerTransportModule;
|
|
13
13
|
//# sourceMappingURL=device.cjs.map
|
package/lib/device.js
CHANGED
package/lib/engine.cjs
CHANGED
|
@@ -13,7 +13,7 @@ var _chunkQOP5LA3Ecjs = require('./chunk-QOP5LA3E.cjs');
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
var
|
|
16
|
+
var _chunkUCQR7XCKcjs = require('./chunk-UCQR7XCK.cjs');
|
|
17
17
|
|
|
18
18
|
// src/engine/index.ts
|
|
19
19
|
var _invariant = require('invariant'); var _invariant2 = _interopRequireDefault(_invariant);
|
|
@@ -37,7 +37,7 @@ var splitChunks = (chunk, buffer) => {
|
|
|
37
37
|
}
|
|
38
38
|
return chunks;
|
|
39
39
|
};
|
|
40
|
-
var removeStatus = (result) => result.slice(0, result.length -
|
|
40
|
+
var removeStatus = (result) => result.slice(0, result.length - _chunkUCQR7XCKcjs.STATUS_LENGTH);
|
|
41
41
|
var sendByChunk = async (transport, command, data, chunkSize = MAX_CHUNK_LENGTH) => {
|
|
42
42
|
const chunks = splitChunks(chunkSize, data);
|
|
43
43
|
let response = Buffer.from([]);
|
|
@@ -47,7 +47,7 @@ var sendByChunk = async (transport, command, data, chunkSize = MAX_CHUNK_LENGTH)
|
|
|
47
47
|
apdu[2] = i && 128;
|
|
48
48
|
response = await transport.send(apdu[0], apdu[1], apdu[2], apdu[3], chunk, [
|
|
49
49
|
36864,
|
|
50
|
-
...
|
|
50
|
+
..._chunkUCQR7XCKcjs.PAGINATED_STATUS
|
|
51
51
|
]);
|
|
52
52
|
}
|
|
53
53
|
return response;
|
|
@@ -90,7 +90,7 @@ var buildAttestation = (params) => {
|
|
|
90
90
|
};
|
|
91
91
|
var genericRetryOnError = (err) => {
|
|
92
92
|
if (err instanceof _errors.LockedDeviceError) return true;
|
|
93
|
-
if (
|
|
93
|
+
if (_chunkUCQR7XCKcjs.isTransportStatusError.call(void 0, err) && err.statusCode === 27913) return true;
|
|
94
94
|
if (err instanceof _errors.DisconnectedDevice) return true;
|
|
95
95
|
return false;
|
|
96
96
|
};
|
|
@@ -98,7 +98,7 @@ function createRealDevice(options, logger) {
|
|
|
98
98
|
const { transportId } = options;
|
|
99
99
|
async function run(job, retryOnError = genericRetryOnError) {
|
|
100
100
|
return _rxjs.lastValueFrom.call(void 0,
|
|
101
|
-
|
|
101
|
+
_chunkUCQR7XCKcjs.withDevicePolling.call(void 0, transportId)((transport) => _rxjs.from.call(void 0, job(transport)), retryOnError)
|
|
102
102
|
);
|
|
103
103
|
}
|
|
104
104
|
const noRetry = () => false;
|
|
@@ -111,10 +111,10 @@ function createRealDevice(options, logger) {
|
|
|
111
111
|
_nullishCoalesce(opts.retryOnError, () => ( alwaysRetry))
|
|
112
112
|
);
|
|
113
113
|
try {
|
|
114
|
-
await getPublicKey(
|
|
114
|
+
await getPublicKey(_chunkUCQR7XCKcjs.PATHS.U2F, { secp256k1: true, retryOnError: noRetry });
|
|
115
115
|
return { isAppOpened: true };
|
|
116
116
|
} catch (err) {
|
|
117
|
-
if (
|
|
117
|
+
if (_chunkUCQR7XCKcjs.isTransportStatusError.call(void 0, err) && genericRetryOnError(err)) {
|
|
118
118
|
return { isAppOpened: false };
|
|
119
119
|
}
|
|
120
120
|
throw err;
|
|
@@ -145,18 +145,18 @@ function createRealDevice(options, logger) {
|
|
|
145
145
|
);
|
|
146
146
|
const pubKeyLength = response.readInt8(0);
|
|
147
147
|
const pubKey = response.slice(1, pubKeyLength + 1).toString("hex").toUpperCase();
|
|
148
|
-
const signature = response.slice(pubKeyLength + 1, response.length -
|
|
148
|
+
const signature = response.slice(pubKeyLength + 1, response.length - _chunkUCQR7XCKcjs.STATUS_LENGTH).toString("base64");
|
|
149
149
|
return { pubKey, signature };
|
|
150
150
|
};
|
|
151
151
|
const device = {
|
|
152
152
|
authenticate: async ({ challenge: { challenge, name, role, keyHandle }, workspaceName }) => {
|
|
153
153
|
logger.info(`[device] Authenticate`);
|
|
154
154
|
_invariant2.default.call(void 0, role !== "READ_ONLY_API_KEY", "Invalid role for authenticate: READ_ONLY_API_KEY");
|
|
155
|
-
const applicationBuffer = Buffer.from(
|
|
155
|
+
const applicationBuffer = Buffer.from(_chunkUCQR7XCKcjs.APPID_VAULT_ADMINISTRATOR, "hex");
|
|
156
156
|
const nameBuffer = Buffer.from(name);
|
|
157
157
|
const workspaceBuffer = Buffer.from(workspaceName);
|
|
158
158
|
const keyHandleData = Buffer.concat([
|
|
159
|
-
Buffer.from(
|
|
159
|
+
Buffer.from(_chunkUCQR7XCKcjs.STR_ROLES[role], "hex"),
|
|
160
160
|
Buffer.from([nameBuffer.length]),
|
|
161
161
|
nameBuffer,
|
|
162
162
|
Buffer.from([workspaceBuffer.length]),
|
|
@@ -210,10 +210,10 @@ function createRealDevice(options, logger) {
|
|
|
210
210
|
confidentialityKeySignature: sig.toString("hex")
|
|
211
211
|
};
|
|
212
212
|
},
|
|
213
|
-
getConfidentialityPubKey: () => getPublicKey(
|
|
213
|
+
getConfidentialityPubKey: () => getPublicKey(_chunkUCQR7XCKcjs.PATHS.CONFIDENTIALITY, { secp256k1: true }),
|
|
214
214
|
getDeviceUserId: FIXME_NOT_IMPLEMENTED("getDeviceUserId"),
|
|
215
|
-
getU2FPubKey: () => getPublicKey(
|
|
216
|
-
getValidationPubKey: () => getPublicKey(
|
|
215
|
+
getU2FPubKey: () => getPublicKey(_chunkUCQR7XCKcjs.PATHS.U2F, { secp256k1: false }),
|
|
216
|
+
getValidationPubKey: () => getPublicKey(_chunkUCQR7XCKcjs.PATHS.VALIDATION, { secp256k1: true }),
|
|
217
217
|
hasPairing: async () => {
|
|
218
218
|
logger.info(`[device] hasPairing`);
|
|
219
219
|
const apdu = [224, 81, 0, 0, 181];
|
|
@@ -225,7 +225,7 @@ function createRealDevice(options, logger) {
|
|
|
225
225
|
const res = await run((transport) => transport.send(224, 70, 0, 0));
|
|
226
226
|
const pubKeyLength = res.readInt8(0);
|
|
227
227
|
const pubKey = res.slice(1, pubKeyLength + 1).toString("hex").toUpperCase();
|
|
228
|
-
const signature = res.slice(pubKeyLength + 1, res.length -
|
|
228
|
+
const signature = res.slice(pubKeyLength + 1, res.length - _chunkUCQR7XCKcjs.STATUS_LENGTH).toString("base64");
|
|
229
229
|
return { pubKey, signature };
|
|
230
230
|
},
|
|
231
231
|
openSession: FIXME_NOT_IMPLEMENTED("openSession"),
|
|
@@ -233,11 +233,11 @@ function createRealDevice(options, logger) {
|
|
|
233
233
|
const { challenge: strChallenge, u2fRegisterData: _, name, role } = params;
|
|
234
234
|
const challenge = Buffer.from(strChallenge, "hex");
|
|
235
235
|
_invariant2.default.call(void 0, challenge.length === 32, "challenge hex is expected to have 32 bytes");
|
|
236
|
-
const applicationBuf = Buffer.from(
|
|
236
|
+
const applicationBuf = Buffer.from(_chunkUCQR7XCKcjs.APPID_VAULT_ADMINISTRATOR, "hex");
|
|
237
237
|
_invariant2.default.call(void 0, applicationBuf.length === 32, "application hex is expected to have 32 bytes");
|
|
238
238
|
const userNameBuff = Buffer.from(name);
|
|
239
239
|
const keyHandleData = Buffer.concat([
|
|
240
|
-
Buffer.from(
|
|
240
|
+
Buffer.from(_chunkUCQR7XCKcjs.STR_ROLES[role], "hex"),
|
|
241
241
|
Buffer.from([userNameBuff.length]),
|
|
242
242
|
userNameBuff
|
|
243
243
|
]);
|
|
@@ -279,12 +279,12 @@ function createRealDevice(options, logger) {
|
|
|
279
279
|
logger.info(`[device] startKPatternAsResponder`);
|
|
280
280
|
const data = buildAttestation(params);
|
|
281
281
|
await run(
|
|
282
|
-
(transport) => sendByChunk(transport, [224, 73, 0,
|
|
282
|
+
(transport) => sendByChunk(transport, [224, 73, 0, _chunkUCQR7XCKcjs.SCRIPT_IDS.ACCOUNT_MANAGER_SESSION], data)
|
|
283
283
|
);
|
|
284
284
|
},
|
|
285
285
|
validateVaultOperation: async (params) => {
|
|
286
286
|
logger.info(`[device] validateVaultOperation`);
|
|
287
|
-
const path = getPathFromPathString(
|
|
287
|
+
const path = getPathFromPathString(_chunkUCQR7XCKcjs.PATHS.VALIDATION);
|
|
288
288
|
const paths = Buffer.concat([
|
|
289
289
|
Buffer.from([path.length]),
|
|
290
290
|
...path.map((derivation) => {
|
|
@@ -305,9 +305,9 @@ function createRealDevice(options, logger) {
|
|
|
305
305
|
);
|
|
306
306
|
const responseType = response.readInt8(0);
|
|
307
307
|
let responseStatus = response.readUInt16BE(response.length - 2);
|
|
308
|
-
if (responseType ===
|
|
308
|
+
if (responseType === _chunkUCQR7XCKcjs.STREAMING_NEXT_ACTION) {
|
|
309
309
|
nextActionId = response.readUIntBE(1, 2);
|
|
310
|
-
} else if (responseType ===
|
|
310
|
+
} else if (responseType === _chunkUCQR7XCKcjs.STREAMING_RESPONSE) {
|
|
311
311
|
finalResponse = response.slice(3, response.length - 2);
|
|
312
312
|
while (responseStatus !== 36864) {
|
|
313
313
|
const resp = await run((transport) => transport.send(0, 192, 0, 0));
|
|
@@ -425,7 +425,7 @@ function createEngine(options, logger) {
|
|
|
425
425
|
baseUrl: options.baseUrl
|
|
426
426
|
};
|
|
427
427
|
const client = createClient(clientOptions);
|
|
428
|
-
const apiDevice =
|
|
428
|
+
const apiDevice = _chunkUCQR7XCKcjs.createApiDevice.call(void 0, options, logger);
|
|
429
429
|
const base = {
|
|
430
430
|
client,
|
|
431
431
|
createClient: (overrides) => createClient({ ...clientOptions, ...overrides })
|
package/lib/engine.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/revault-sdk",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -52,8 +52,9 @@
|
|
|
52
52
|
"lodash": "^4.17.21",
|
|
53
53
|
"rxjs": "^7.8.2",
|
|
54
54
|
"superjson": "^1.13.3",
|
|
55
|
+
"uuid": "^11.1.0",
|
|
55
56
|
"zod": "^3.25.76",
|
|
56
|
-
"@ledgerhq/revault-api": "1.
|
|
57
|
+
"@ledgerhq/revault-api": "1.33.0"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
59
60
|
"@eslint/js": "^9.32.0",
|
|
@@ -67,7 +68,7 @@
|
|
|
67
68
|
"eslint": "^9.32.0",
|
|
68
69
|
"eslint-plugin-unicorn": "^59.0.1",
|
|
69
70
|
"tsup": "^8.5.0",
|
|
70
|
-
"tsx": "^4.20.
|
|
71
|
+
"tsx": "^4.20.4",
|
|
71
72
|
"typescript": "^5.8.3",
|
|
72
73
|
"typescript-eslint": "^8.38.0",
|
|
73
74
|
"vite-plugin-node-polyfills": "^0.24.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/device/api-device.ts","../src/device/constants.ts","../src/device/utils.ts","../src/device/device-access.ts"],"sourcesContent":["import type {\n HsmCertificate,\n PsdAuthChallengeResponse,\n PsdPairingData,\n PubKey,\n} from \"@ledgerhq/revault-api\";\nimport axios from \"axios\";\nimport axiosRetry from \"axios-retry\";\n\nimport type { Logger } from \"@/types\";\n\nimport { APPID_VAULT_ADMINISTRATOR, PATHS, SCRIPT_IDS, STR_ROLES } from \"./constants\";\nimport type { Device } from \"./types\";\n\ntype Options = {\n /**\n * device-api URL\n */\n deviceApiUrl: string;\n\n /**\n * salt used by device-api\n */\n salt: string;\n\n /**\n * seed generation function used for device-api device spawn\n */\n genSeed: (p: { salt: string; index: number }) => string;\n};\n\nexport type ApiDevice = {\n /**\n * bind a device to the given device index\n */\n with: (index: number) => Device;\n};\n\nexport function createApiDevice(options: Options, logger: Logger): ApiDevice {\n const { genSeed } = options;\n const sessionId = Date.now().toString();\n const deviceApi = axios.create({ baseURL: options.deviceApiUrl });\n\n axiosRetry(deviceApi, {\n retryDelay: axiosRetry.linearDelay(1000),\n retries: 3,\n /* v8 ignore start */\n retryCondition: () => true,\n onRetry: (retryCount, err) => {\n const formattedErr = `${err.code ?? `UNKNOWN`} on ${err.config?.method?.toUpperCase() ?? \"UNKNOWN_METHOD\"} ${err.config?.url ?? \"UNKNOWN_URL\"}`;\n logger.warning(\n `Retrying request to device-api (attempt ${retryCount}) - Cause: ${formattedErr}`,\n );\n },\n /* v8 ignore stop */\n });\n\n deviceApi.interceptors.request.use(\n (config) => {\n /* v8 ignore start */\n logger.debug(\n `[device-api] ${config.method?.toUpperCase()} ${config.url}${config.data ? ` ${JSON.stringify(config.data)}` : \"\"}`,\n );\n if ([\"post\", \"put\"].includes(config.method!)) {\n // when interceptor is called in combination with axios-retry, config.data can be a string or an object\n // and we should return either a string or an object compatible with the original type\n const isStringMode = typeof config.data === \"string\";\n const data = (isStringMode ? JSON.parse(config.data) : config.data) as Record<\n string,\n unknown\n >;\n const enhancedData = {\n sessionID: sessionId, // Used to ensure dedicated session if someones else uses same seed\n psd_model: \"02\", // Stax\n version: 3, // Used to properly enable noise channel\n ...data,\n };\n config.data = isStringMode ? JSON.stringify(enhancedData) : enhancedData;\n }\n return config;\n },\n /* v8 ignore stop */\n (error: Error) => Promise.reject(error),\n );\n\n return {\n with: (index: number): Device => {\n const seed = genSeed({ salt: options.salt, index });\n\n const authenticate = async ({\n challenge,\n workspaceName,\n }: {\n challenge: PsdAuthChallengeResponse;\n workspaceName: string;\n }): Promise<string> => {\n const role = challenge.role;\n if (role === \"READ_ONLY_API_KEY\")\n throw new Error(\"Invalid role for authenticate: READ_ONLY_API_KEY\");\n const { data } = await deviceApi.post<string>(\"/authenticate\", {\n seed,\n challenge: Buffer.from(challenge.challenge, \"base64\").toString(\"hex\"),\n application: APPID_VAULT_ADMINISTRATOR,\n key_handle: challenge.keyHandle,\n name: challenge.name,\n role: STR_ROLES[role],\n type: \"02\", // Stax\n workspaceName,\n });\n return data;\n };\n\n const finalizePairingHandshake = async (params: {\n handshake: string;\n partitionId: string;\n }): Promise<string> => {\n const { handshake, partitionId } = params;\n const payload = { seed, handshake, ciphertext: partitionId };\n const res = await deviceApi.post<string>(\"/finalize-pairing-handshake\", payload);\n return res.data;\n };\n\n const generateSeedFragment = async (): Promise<string> => {\n const payload = {\n seed,\n path: PATHS.KEY_MATERIAL,\n goal: 0, // seed fragment\n };\n const { data } = await deviceApi.post<string>(\"/generate-key-component\", payload);\n return data;\n };\n\n const generateWrappingKeyFragment = async (): Promise<string> => {\n const goal = 1; // means Wrapping Key\n const payload = { seed, goal, path: PATHS.KEY_MATERIAL };\n const res = await deviceApi.post<string>(\"/generate-key-fragments\", payload);\n return Buffer.from(res.data, \"hex\").toString(\"base64\");\n };\n\n const getAttestation = async (): Promise<{\n attestation: {\n codeHash: string;\n attestationPub: string;\n certificate: string;\n };\n buffer: Buffer;\n }> => {\n const { data: attestationStr } = await deviceApi.post<string>(\"/get-attestation\", { seed });\n const attestationBuffer = Buffer.from(attestationStr, \"hex\");\n const certLen = attestationBuffer.readInt8(32 + 65 + 1);\n return {\n attestation: {\n codeHash: attestationBuffer.slice(0, 32).toString(\"base64\"),\n attestationPub: attestationBuffer.slice(32, 32 + 65).toString(\"base64\"),\n certificate: attestationBuffer.slice(32 + 65, 32 + 65 + 2 + certLen).toString(\"base64\"),\n },\n buffer: attestationBuffer,\n };\n };\n\n const getConfidentialityKey = async (): Promise<{\n confidentialityKeyCurve25519: string;\n confidentialityKeySignature: string;\n }> => {\n const res = await deviceApi.post<{\n confidentiality_key_curve25519: string;\n confidentiality_key_signature: string;\n }>(\"/get-confidentiality-key\", { seed });\n return {\n confidentialityKeyCurve25519: res.data.confidentiality_key_curve25519,\n confidentialityKeySignature: res.data.confidentiality_key_signature,\n };\n };\n\n const getConfidentialityPubKey = async (): Promise<{ pubKey: string; signature: string }> => {\n return getPublicKey({ path: PATHS.CONFIDENTIALITY });\n };\n\n const getDeviceUserId = async (): Promise<string> => {\n const res = await deviceApi.post<{ blue_device_id: string }>(\"/blue-device-id\", { seed });\n return res.data.blue_device_id.toUpperCase();\n };\n\n const getU2FPubKey = async (): Promise<{ pubKey: string; signature: string }> => {\n return getPublicKey({ path: PATHS.U2F });\n };\n\n const getValidationPubKey = async (): Promise<{ pubKey: string; signature: string }> => {\n return getPublicKey({ path: PATHS.VALIDATION });\n };\n\n const hasPairing = async (): Promise<boolean> => {\n const res = await deviceApi.post<string[]>(\"/get-partition-ids\", { seed });\n return res.data.length > 0;\n };\n\n const initPairingHandshake = async (): Promise<{ pubKey: string; signature: string }> => {\n const { data } = await deviceApi.post<{ attestation: string; pubKey: string }>(\n \"/initiate-pairing-handshake\",\n { seed },\n );\n return {\n pubKey: data.pubKey,\n signature: Buffer.from(data.attestation, \"hex\").toString(\"base64\"),\n };\n };\n\n const openSession = async (params: {\n secureChannel: { pubKey: PubKey; certificate: HsmCertificate };\n }): Promise<void> => {\n const { secureChannel } = params;\n const signature = Buffer.from(secureChannel.certificate.signature, \"base64\");\n const attestationPubKey = Buffer.from(secureChannel.certificate.attestationPub, \"base64\");\n const attestationCertificate = Buffer.from(secureChannel.certificate.certificate, \"base64\");\n const attestation = Buffer.concat([\n Buffer.from([signature.length]),\n signature,\n attestationPubKey,\n Buffer.from([attestationCertificate.length]),\n attestationCertificate,\n ]);\n const payload = {\n seed,\n path: PATHS.CONFIDENTIALITY,\n pubKey: secureChannel.pubKey,\n attestation: attestation.toString(\"hex\"),\n scriptId: SCRIPT_IDS.INIT_SESSION,\n };\n const { data: deviceResponse } = await deviceApi.post<string>(\"/open-session\", payload);\n if (deviceResponse !== \"9000\") {\n throw new Error(`device-api device failed with code: ${deviceResponse}`);\n }\n };\n\n const register = async (params: {\n challenge: string;\n u2fRegisterData: string;\n name: string;\n role: \"ADMIN\" | \"SHARED_OWNER\" | \"OPERATOR\";\n }): Promise<{\n u2fRegister: Buffer;\n keyHandle: Buffer;\n rfu: number;\n pubKey: string;\n }> => {\n const { challenge, u2fRegisterData, name, role } = params;\n const payload = {\n seed,\n challenge,\n name,\n role: STR_ROLES[role],\n hsm_data: u2fRegisterData,\n type: \"02\", // Stax\n application: APPID_VAULT_ADMINISTRATOR,\n };\n const { data } = await deviceApi.post<string>(\"/register\", payload);\n const response = Buffer.from(data, \"hex\");\n let i = 0;\n const rfu = response.slice(i, (i += 1))[0]!;\n const pubKey = response.slice(i, (i += 65)).toString(\"hex\");\n const keyHandleLength = response.slice(i, ++i)[0]!;\n const keyHandle = response.slice(i, (i += keyHandleLength));\n return {\n u2fRegister: response.slice(0, response.length - 2),\n keyHandle,\n rfu,\n pubKey,\n };\n };\n\n const registerData = async (params: { challenge: string }): Promise<string> => {\n const { challenge } = params;\n const payload = { seed, challenge };\n const res = await deviceApi.post<string>(\"/u2f-register-data\", payload);\n return res.data;\n };\n\n const setPairing = async ({ pairing }: { pairing: PsdPairingData }): Promise<void> => {\n const payload = {\n seed,\n blob: pairing.blob,\n pairing: {\n bootstrap_enabled: pairing.pairing.bootstrapEnabled,\n partition_key: pairing.pairing.partitionKey,\n workspace_name: pairing.pairing.workspaceName,\n },\n partition_key_attestation: {\n code_hash: pairing.partitionKeyAttestation.codeHash,\n attestation_pub: pairing.partitionKeyAttestation.attestationPub,\n certificate: pairing.partitionKeyAttestation.certificate,\n signature: pairing.partitionKeyAttestation.signature,\n },\n };\n await deviceApi.post(\"/set-partition-key\", payload);\n };\n\n const startKKPatternAsResponder = async (params: {\n handshake: string;\n handshakeAttestation: HsmCertificate;\n partitionId: string;\n }): Promise<{ handshake: string; signature: string }> => {\n const payload = {\n seed,\n script_id: SCRIPT_IDS.ACCOUNT_MANAGER_SESSION,\n handshake: params.handshake,\n // for device-api it's normal to have empty string as handshake_attestation\n handshake_attestation: \"\",\n partition_id: params.partitionId,\n };\n const { data } = await deviceApi.post<string>(\"/start-kk-pattern-as-responder\", payload);\n const buffer = Buffer.from(data, \"hex\");\n const dataLength = buffer.readInt8(0);\n return {\n handshake: buffer.slice(1, dataLength + 1).toString(\"base64\"),\n signature: buffer.slice(dataLength + 1).toString(\"base64\"),\n };\n };\n\n const startKPatternAsResponder = async (params: {\n handshake: string;\n handshakeAttestation: HsmCertificate;\n partitionId: string;\n }): Promise<void> => {\n const { handshake, partitionId } = params;\n const payload = {\n seed,\n script_id: SCRIPT_IDS.ACCOUNT_MANAGER_SESSION,\n handshake,\n // for device-api it's normal to have empty string as handshake_attestation\n handshake_attestation: \"\",\n partition_id: partitionId,\n };\n const { data: deviceResponse } = await deviceApi.post<string>(\n \"/start-k-pattern-as-responder\",\n payload,\n );\n if (deviceResponse !== \"9000\") {\n throw new Error(`device-api device failed with code: ${deviceResponse}`);\n }\n };\n\n const getPublicKey = async (params: {\n path: string;\n }): Promise<{ pubKey: string; signature: string }> => {\n const { path } = params;\n type DeviceApiGetPubKeyResponse = { attestation: string; pubKey: string };\n const payload = { seed, secp256k1: true, path };\n const res = await deviceApi.post<DeviceApiGetPubKeyResponse>(\"/get-public-key\", payload);\n return {\n pubKey: res.data.pubKey,\n signature: Buffer.from(res.data.attestation, \"hex\").toString(\"base64\"),\n };\n };\n\n const validateVaultOperation = async (params: { actions: string[] }): Promise<string> => {\n const { actions } = params;\n const payload = {\n seed,\n path: PATHS.VALIDATION,\n actions,\n };\n const { data: signature } = await deviceApi.post<string>(\"/validate-op\", payload);\n return signature;\n };\n\n const sendAPDU = (_apdu: string): Promise<string> => {\n logger.info(\"Can't send arbitrary APDU to software device\");\n return Promise.resolve(\"9000\");\n };\n\n const getVersion = async (): Promise<string> => {\n return \"1.2.3\";\n };\n\n const poll = async (): Promise<{ isAppOpened: boolean }> => {\n await new Promise((r) => setTimeout(r, 1e3));\n return { isAppOpened: true };\n };\n\n const pollApp = async (): Promise<string> => {\n await new Promise((r) => setTimeout(r, 1e3));\n return \"9000\";\n };\n\n return {\n authenticate,\n finalizePairingHandshake,\n generateSeedFragment,\n generateWrappingKeyFragment,\n getAttestation,\n getConfidentialityKey,\n getConfidentialityPubKey,\n getDeviceUserId,\n getU2FPubKey,\n getValidationPubKey,\n hasPairing,\n initPairingHandshake,\n openSession,\n register,\n registerData,\n setPairing,\n startKKPatternAsResponder,\n startKPatternAsResponder,\n validateVaultOperation,\n getVersion,\n sendAPDU,\n poll,\n pollApp,\n };\n },\n };\n}\n","export const PATHS = {\n U2F: \"5655636'/5583430'\",\n CONFIDENTIALITY: \"5655636'/4410950'\",\n KEY_MATERIAL: \"5655636'/4932953'\",\n VALIDATION: \"5655636'/5652812'\",\n} as const;\n\nexport const STATUS_LENGTH = 2;\n\nexport const SCRIPT_IDS = {\n INIT_SESSION: 3,\n ACCOUNT_MANAGER_SESSION: 2,\n} as const;\n\nexport const STR_ROLES: Record<\"OPERATOR\" | \"ADMIN\" | \"SHARED_OWNER\", string> = {\n OPERATOR: \"00\",\n ADMIN: \"01\",\n SHARED_OWNER: \"02\",\n} as const;\n\nexport const APPID_VAULT_ADMINISTRATOR =\n \"ad5be1a1fe011ce7f53ae081a22ae000a42021f3f94106a3bac9f76e8230e4b9\";\n\nconst all61xxStatus = [];\nfor (let i = 0x6100; i <= 0x61ff; i++) {\n all61xxStatus.push(i);\n}\n\nexport const PAGINATED_STATUS = all61xxStatus;\nexport const STREAMING_RESPONSE = 0x02;\nexport const STREAMING_NEXT_ACTION = 0x01;\n","import { TransportStatusError } from \"@ledgerhq/errors\";\n\nexport const isTransportStatusError = (error: unknown): error is TransportStatusError => {\n return (\n error instanceof TransportStatusError ||\n (error instanceof Error && \"statusCode\" in error && error.message.startsWith(\"Ledger device:\"))\n );\n};\n\nconst CANCEL_STATUS_CODES = [\n 0x6985, // cancel on Ledger Blue\n\n // STAX\n 0xb00c, // cancel collect wk fragment\n 0xb00d, // cancel user registration\n 0xb010, // cancel review\n 0xb012, // cancel master seed generation\n];\n\nexport const isDeviceCancelError = (error: Error): boolean => {\n return isTransportStatusError(error) && CANCEL_STATUS_CODES.includes(error.statusCode);\n};\n","// ------------------------------------------------------------------------------------------------\n// ------------------------------------------------------------------------------------------------\n// THIS IS SOME HORRIBLE CODE IMPORTED JUST\n// FOR THE SAKE OF NOT IMPORTING UGLY, WICKED, AND\n// BROKEN @ledgerhq/live-* LIBRARIES\n//\n// IT'S MEANT TO BE CONSIDERED AS A BLACK BOX\n// ------------------------------------------------------------------------------------------------\n// ------------------------------------------------------------------------------------------------\nimport {\n BluetoothRequired,\n CantOpenDevice,\n DeviceHalted,\n FirmwareOrAppUpdateRequired,\n TransportInterfaceNotAvailable,\n TransportStatusError,\n TransportWebUSBGestureRequired,\n} from \"@ledgerhq/errors\";\nimport type Transport from \"@ledgerhq/hw-transport\";\nimport { Observable, throwError, timer } from \"rxjs\";\nimport { catchError, mergeMap, retryWhen } from \"rxjs/operators\";\n\nconst initialErrorRemapping = (error: Error): Observable<never> =>\n throwError(\n error && error instanceof TransportStatusError && error.statusCode === 0x6faa\n ? new DeviceHalted(error.message)\n : error instanceof TransportStatusError && error.statusCode === 0x6b00\n ? new FirmwareOrAppUpdateRequired(error.message)\n : error,\n );\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nconst never = new Promise(() => {});\n\ntype Discovery = Observable<{\n type: \"add\" | \"remove\";\n id: string;\n name: string;\n}>;\n\nexport type TransportModule = {\n // unique transport name that identify the transport module\n id: string;\n // open a device by an id, this id must be unique across all modules\n // you can typically prefix it with `something|` that identify it globally\n // returns falsy if the transport module can't handle this id\n // here, open means we want to START doing something with the transport\n open: (id: string) => Promise<Transport> | null | undefined;\n // here, close means we want to STOP doing something with the transport\n close?: (transport: Transport, id: string) => Promise<void> | null | undefined;\n // disconnect/interrupt a device connection globally\n // returns falsy if the transport module can't handle this id\n disconnect: (id: string) => Promise<void> | null | undefined;\n // optional observable that allows to discover a transport\n discovery?: Discovery;\n};\n\nconst modules: TransportModule[] = [];\n\nexport const registerTransportModule = (module: TransportModule): void => {\n modules.push(module);\n};\n\nexport const close = (transport: Transport, deviceId: string): Promise<void> => {\n for (const m of modules) {\n const p = m.close?.(transport, deviceId);\n if (p) return p;\n }\n // fallback on an actual close\n return transport.close();\n};\n\nexport const open = (deviceId: string): Promise<Transport> => {\n for (const m of modules) {\n const p = m.open(deviceId);\n if (p) return p;\n }\n return Promise.reject(new Error(`Can't find handler to open ${deviceId}`));\n};\n\nconst transportFinally =\n (cleanup: () => Promise<void>) =>\n <T>(observable: Observable<T>): Observable<T> =>\n // @ts-expect-error typescript not checking against the instanceof\n Observable.create((o) => {\n let done = false;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const finalize = (): any => {\n if (done) return never;\n done = true;\n return cleanup();\n };\n\n const sub = observable.subscribe({\n next: (e) => o.next(e),\n complete: () => {\n void finalize().then(() => o.complete());\n },\n error: (e) => {\n void finalize().then(() => o.error(e));\n },\n });\n return () => {\n sub.unsubscribe();\n void finalize();\n };\n });\n\ntype AccessHook = () => () => void;\n\nconst accessHooks: AccessHook[] = [];\n\nconst deviceQueues = {};\n\nexport const withDevice =\n (deviceId: string) =>\n <T>(job: (t: Transport) => Observable<T>): Observable<T> =>\n new Observable((o) => {\n // @ts-expect-error typescript not checking against the instanceof\n let unsubscribed;\n // @ts-expect-error typescript not checking against the instanceof\n let sub;\n // @ts-expect-error typescript not checking against the instanceof\n const deviceQueue = deviceQueues[deviceId] ?? Promise.resolve();\n\n // @ts-expect-error typescript not checking against the instanceof\n const finalize = (transport, cleanups): Promise<void> =>\n close(transport, deviceId)\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n .catch(() => {})\n .then(() => {\n // @ts-expect-error typescript not checking against the instanceof\n cleanups.forEach((c) => c());\n });\n\n // when we'll finish all the current job, we'll call finish\n // @ts-expect-error typescript not checking against the instanceof\n let finish;\n // this new promise is the next exec queue\n // @ts-expect-error typescript not checking against the instanceof\n deviceQueues[deviceId] = new Promise((resolve) => {\n finish = resolve;\n });\n // for any new job, we'll now wait the exec queue to be available\n deviceQueue\n .then(() => open(deviceId)) // open the transport\n // @ts-expect-error typescript not checking against the instanceof\n .then(async (transport) => {\n // @ts-expect-error typescript not checking against the instanceof\n if (unsubscribed) {\n // it was unsubscribed prematurely\n // @ts-expect-error typescript not checking against the instanceof\n return finalize(transport, [finish]);\n }\n\n if (\n transport.requestConnectionPriority &&\n typeof transport.requestConnectionPriority === \"function\"\n ) {\n await transport.requestConnectionPriority(\"High\");\n }\n\n return transport;\n })\n // @ts-expect-error typescript not checking against the instanceof\n .catch((e) => {\n // @ts-expect-error typescript not checking against the instanceof\n finish();\n if (e instanceof BluetoothRequired) throw e;\n if (e instanceof TransportWebUSBGestureRequired) throw e;\n if (e instanceof TransportInterfaceNotAvailable) throw e;\n throw new CantOpenDevice(e.message);\n })\n // @ts-expect-error typescript not checking against the instanceof\n .then((transport) => {\n if (!transport) return;\n\n // @ts-expect-error typescript not checking against the instanceof\n if (unsubscribed) {\n // it was unsubscribed prematurely\n // @ts-expect-error typescript not checking against the instanceof\n return finalize(transport, [finish]);\n }\n\n const cleanups = accessHooks.map((hook) => hook());\n sub = job(transport) // $FlowFixMe\n .pipe(\n catchError(initialErrorRemapping),\n // $FlowFixMe\n transportFinally(() =>\n // @ts-expect-error typescript not checking against the instanceof\n finalize(transport, [...cleanups, finish]),\n ),\n )\n .subscribe(o);\n })\n // @ts-expect-error typescript not checking against the instanceof\n .catch((error) => o.error(error));\n return () => {\n unsubscribed = true;\n // @ts-expect-error typescript not checking against the instanceof\n if (sub) sub.unsubscribe();\n };\n });\n\nconst WITH_DEVICE_POLLING_DELAY = 500;\n\nexport const retryWhileErrors =\n (acceptError: (arg0: Error) => boolean) =>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (attempts: Observable<any>): Observable<any> =>\n attempts.pipe(\n mergeMap((error) => {\n if (!acceptError(error)) {\n return throwError(error);\n }\n\n return timer(WITH_DEVICE_POLLING_DELAY);\n }),\n );\n\nexport const withDevicePolling =\n (deviceId: string) =>\n <T>(\n job: (arg0: Transport) => Observable<T>,\n acceptError: (arg0: Error) => boolean = () => false,\n ): Observable<T> =>\n withDevice(deviceId)(job).pipe(retryWhen(retryWhileErrors(acceptError)));\n"],"mappings":"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","names":["TransportStatusError"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/_work/revault/revault/packages/sdk/lib/chunk-R2LW7L74.cjs","../src/device/api-device.ts","../src/device/constants.ts","../src/device/utils.ts","../src/device/device-access.ts"],"names":["TransportStatusError"],"mappings":"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","file":"/home/runner/_work/revault/revault/packages/sdk/lib/chunk-R2LW7L74.cjs","sourcesContent":[null,"import type {\n HsmCertificate,\n PsdAuthChallengeResponse,\n PsdPairingData,\n PubKey,\n} from \"@ledgerhq/revault-api\";\nimport axios from \"axios\";\nimport axiosRetry from \"axios-retry\";\n\nimport type { Logger } from \"@/types\";\n\nimport { APPID_VAULT_ADMINISTRATOR, PATHS, SCRIPT_IDS, STR_ROLES } from \"./constants\";\nimport type { Device } from \"./types\";\n\ntype Options = {\n /**\n * device-api URL\n */\n deviceApiUrl: string;\n\n /**\n * salt used by device-api\n */\n salt: string;\n\n /**\n * seed generation function used for device-api device spawn\n */\n genSeed: (p: { salt: string; index: number }) => string;\n};\n\nexport type ApiDevice = {\n /**\n * bind a device to the given device index\n */\n with: (index: number) => Device;\n};\n\nexport function createApiDevice(options: Options, logger: Logger): ApiDevice {\n const { genSeed } = options;\n const sessionId = Date.now().toString();\n const deviceApi = axios.create({ baseURL: options.deviceApiUrl });\n\n axiosRetry(deviceApi, {\n retryDelay: axiosRetry.linearDelay(1000),\n retries: 3,\n /* v8 ignore start */\n retryCondition: () => true,\n onRetry: (retryCount, err) => {\n const formattedErr = `${err.code ?? `UNKNOWN`} on ${err.config?.method?.toUpperCase() ?? \"UNKNOWN_METHOD\"} ${err.config?.url ?? \"UNKNOWN_URL\"}`;\n logger.warning(\n `Retrying request to device-api (attempt ${retryCount}) - Cause: ${formattedErr}`,\n );\n },\n /* v8 ignore stop */\n });\n\n deviceApi.interceptors.request.use(\n (config) => {\n /* v8 ignore start */\n logger.debug(\n `[device-api] ${config.method?.toUpperCase()} ${config.url}${config.data ? ` ${JSON.stringify(config.data)}` : \"\"}`,\n );\n if ([\"post\", \"put\"].includes(config.method!)) {\n // when interceptor is called in combination with axios-retry, config.data can be a string or an object\n // and we should return either a string or an object compatible with the original type\n const isStringMode = typeof config.data === \"string\";\n const data = (isStringMode ? JSON.parse(config.data) : config.data) as Record<\n string,\n unknown\n >;\n const enhancedData = {\n sessionID: sessionId, // Used to ensure dedicated session if someones else uses same seed\n psd_model: \"02\", // Stax\n version: 3, // Used to properly enable noise channel\n ...data,\n };\n config.data = isStringMode ? JSON.stringify(enhancedData) : enhancedData;\n }\n return config;\n },\n /* v8 ignore stop */\n (error: Error) => Promise.reject(error),\n );\n\n return {\n with: (index: number): Device => {\n const seed = genSeed({ salt: options.salt, index });\n\n const authenticate = async ({\n challenge,\n workspaceName,\n }: {\n challenge: PsdAuthChallengeResponse;\n workspaceName: string;\n }): Promise<string> => {\n const role = challenge.role;\n if (role === \"READ_ONLY_API_KEY\")\n throw new Error(\"Invalid role for authenticate: READ_ONLY_API_KEY\");\n const { data } = await deviceApi.post<string>(\"/authenticate\", {\n seed,\n challenge: Buffer.from(challenge.challenge, \"base64\").toString(\"hex\"),\n application: APPID_VAULT_ADMINISTRATOR,\n key_handle: challenge.keyHandle,\n name: challenge.name,\n role: STR_ROLES[role],\n type: \"02\", // Stax\n workspaceName,\n });\n return data;\n };\n\n const finalizePairingHandshake = async (params: {\n handshake: string;\n partitionId: string;\n }): Promise<string> => {\n const { handshake, partitionId } = params;\n const payload = { seed, handshake, ciphertext: partitionId };\n const res = await deviceApi.post<string>(\"/finalize-pairing-handshake\", payload);\n return res.data;\n };\n\n const generateSeedFragment = async (): Promise<string> => {\n const payload = {\n seed,\n path: PATHS.KEY_MATERIAL,\n goal: 0, // seed fragment\n };\n const { data } = await deviceApi.post<string>(\"/generate-key-component\", payload);\n return data;\n };\n\n const generateWrappingKeyFragment = async (): Promise<string> => {\n const goal = 1; // means Wrapping Key\n const payload = { seed, goal, path: PATHS.KEY_MATERIAL };\n const res = await deviceApi.post<string>(\"/generate-key-fragments\", payload);\n return Buffer.from(res.data, \"hex\").toString(\"base64\");\n };\n\n const getAttestation = async (): Promise<{\n attestation: {\n codeHash: string;\n attestationPub: string;\n certificate: string;\n };\n buffer: Buffer;\n }> => {\n const { data: attestationStr } = await deviceApi.post<string>(\"/get-attestation\", { seed });\n const attestationBuffer = Buffer.from(attestationStr, \"hex\");\n const certLen = attestationBuffer.readInt8(32 + 65 + 1);\n return {\n attestation: {\n codeHash: attestationBuffer.slice(0, 32).toString(\"base64\"),\n attestationPub: attestationBuffer.slice(32, 32 + 65).toString(\"base64\"),\n certificate: attestationBuffer.slice(32 + 65, 32 + 65 + 2 + certLen).toString(\"base64\"),\n },\n buffer: attestationBuffer,\n };\n };\n\n const getConfidentialityKey = async (): Promise<{\n confidentialityKeyCurve25519: string;\n confidentialityKeySignature: string;\n }> => {\n const res = await deviceApi.post<{\n confidentiality_key_curve25519: string;\n confidentiality_key_signature: string;\n }>(\"/get-confidentiality-key\", { seed });\n return {\n confidentialityKeyCurve25519: res.data.confidentiality_key_curve25519,\n confidentialityKeySignature: res.data.confidentiality_key_signature,\n };\n };\n\n const getConfidentialityPubKey = async (): Promise<{ pubKey: string; signature: string }> => {\n return getPublicKey({ path: PATHS.CONFIDENTIALITY });\n };\n\n const getDeviceUserId = async (): Promise<string> => {\n const res = await deviceApi.post<{ blue_device_id: string }>(\"/blue-device-id\", { seed });\n return res.data.blue_device_id.toUpperCase();\n };\n\n const getU2FPubKey = async (): Promise<{ pubKey: string; signature: string }> => {\n return getPublicKey({ path: PATHS.U2F });\n };\n\n const getValidationPubKey = async (): Promise<{ pubKey: string; signature: string }> => {\n return getPublicKey({ path: PATHS.VALIDATION });\n };\n\n const hasPairing = async (): Promise<boolean> => {\n const res = await deviceApi.post<string[]>(\"/get-partition-ids\", { seed });\n return res.data.length > 0;\n };\n\n const initPairingHandshake = async (): Promise<{ pubKey: string; signature: string }> => {\n const { data } = await deviceApi.post<{ attestation: string; pubKey: string }>(\n \"/initiate-pairing-handshake\",\n { seed },\n );\n return {\n pubKey: data.pubKey,\n signature: Buffer.from(data.attestation, \"hex\").toString(\"base64\"),\n };\n };\n\n const openSession = async (params: {\n secureChannel: { pubKey: PubKey; certificate: HsmCertificate };\n }): Promise<void> => {\n const { secureChannel } = params;\n const signature = Buffer.from(secureChannel.certificate.signature, \"base64\");\n const attestationPubKey = Buffer.from(secureChannel.certificate.attestationPub, \"base64\");\n const attestationCertificate = Buffer.from(secureChannel.certificate.certificate, \"base64\");\n const attestation = Buffer.concat([\n Buffer.from([signature.length]),\n signature,\n attestationPubKey,\n Buffer.from([attestationCertificate.length]),\n attestationCertificate,\n ]);\n const payload = {\n seed,\n path: PATHS.CONFIDENTIALITY,\n pubKey: secureChannel.pubKey,\n attestation: attestation.toString(\"hex\"),\n scriptId: SCRIPT_IDS.INIT_SESSION,\n };\n const { data: deviceResponse } = await deviceApi.post<string>(\"/open-session\", payload);\n if (deviceResponse !== \"9000\") {\n throw new Error(`device-api device failed with code: ${deviceResponse}`);\n }\n };\n\n const register = async (params: {\n challenge: string;\n u2fRegisterData: string;\n name: string;\n role: \"ADMIN\" | \"SHARED_OWNER\" | \"OPERATOR\";\n }): Promise<{\n u2fRegister: Buffer;\n keyHandle: Buffer;\n rfu: number;\n pubKey: string;\n }> => {\n const { challenge, u2fRegisterData, name, role } = params;\n const payload = {\n seed,\n challenge,\n name,\n role: STR_ROLES[role],\n hsm_data: u2fRegisterData,\n type: \"02\", // Stax\n application: APPID_VAULT_ADMINISTRATOR,\n };\n const { data } = await deviceApi.post<string>(\"/register\", payload);\n const response = Buffer.from(data, \"hex\");\n let i = 0;\n const rfu = response.slice(i, (i += 1))[0]!;\n const pubKey = response.slice(i, (i += 65)).toString(\"hex\");\n const keyHandleLength = response.slice(i, ++i)[0]!;\n const keyHandle = response.slice(i, (i += keyHandleLength));\n return {\n u2fRegister: response.slice(0, response.length - 2),\n keyHandle,\n rfu,\n pubKey,\n };\n };\n\n const registerData = async (params: { challenge: string }): Promise<string> => {\n const { challenge } = params;\n const payload = { seed, challenge };\n const res = await deviceApi.post<string>(\"/u2f-register-data\", payload);\n return res.data;\n };\n\n const setPairing = async ({ pairing }: { pairing: PsdPairingData }): Promise<void> => {\n const payload = {\n seed,\n blob: pairing.blob,\n pairing: {\n bootstrap_enabled: pairing.pairing.bootstrapEnabled,\n partition_key: pairing.pairing.partitionKey,\n workspace_name: pairing.pairing.workspaceName,\n },\n partition_key_attestation: {\n code_hash: pairing.partitionKeyAttestation.codeHash,\n attestation_pub: pairing.partitionKeyAttestation.attestationPub,\n certificate: pairing.partitionKeyAttestation.certificate,\n signature: pairing.partitionKeyAttestation.signature,\n },\n };\n await deviceApi.post(\"/set-partition-key\", payload);\n };\n\n const startKKPatternAsResponder = async (params: {\n handshake: string;\n handshakeAttestation: HsmCertificate;\n partitionId: string;\n }): Promise<{ handshake: string; signature: string }> => {\n const payload = {\n seed,\n script_id: SCRIPT_IDS.ACCOUNT_MANAGER_SESSION,\n handshake: params.handshake,\n // for device-api it's normal to have empty string as handshake_attestation\n handshake_attestation: \"\",\n partition_id: params.partitionId,\n };\n const { data } = await deviceApi.post<string>(\"/start-kk-pattern-as-responder\", payload);\n const buffer = Buffer.from(data, \"hex\");\n const dataLength = buffer.readInt8(0);\n return {\n handshake: buffer.slice(1, dataLength + 1).toString(\"base64\"),\n signature: buffer.slice(dataLength + 1).toString(\"base64\"),\n };\n };\n\n const startKPatternAsResponder = async (params: {\n handshake: string;\n handshakeAttestation: HsmCertificate;\n partitionId: string;\n }): Promise<void> => {\n const { handshake, partitionId } = params;\n const payload = {\n seed,\n script_id: SCRIPT_IDS.ACCOUNT_MANAGER_SESSION,\n handshake,\n // for device-api it's normal to have empty string as handshake_attestation\n handshake_attestation: \"\",\n partition_id: partitionId,\n };\n const { data: deviceResponse } = await deviceApi.post<string>(\n \"/start-k-pattern-as-responder\",\n payload,\n );\n if (deviceResponse !== \"9000\") {\n throw new Error(`device-api device failed with code: ${deviceResponse}`);\n }\n };\n\n const getPublicKey = async (params: {\n path: string;\n }): Promise<{ pubKey: string; signature: string }> => {\n const { path } = params;\n type DeviceApiGetPubKeyResponse = { attestation: string; pubKey: string };\n const payload = { seed, secp256k1: true, path };\n const res = await deviceApi.post<DeviceApiGetPubKeyResponse>(\"/get-public-key\", payload);\n return {\n pubKey: res.data.pubKey,\n signature: Buffer.from(res.data.attestation, \"hex\").toString(\"base64\"),\n };\n };\n\n const validateVaultOperation = async (params: { actions: string[] }): Promise<string> => {\n const { actions } = params;\n const payload = {\n seed,\n path: PATHS.VALIDATION,\n actions,\n };\n const { data: signature } = await deviceApi.post<string>(\"/validate-op\", payload);\n return signature;\n };\n\n const sendAPDU = (_apdu: string): Promise<string> => {\n logger.info(\"Can't send arbitrary APDU to software device\");\n return Promise.resolve(\"9000\");\n };\n\n const getVersion = async (): Promise<string> => {\n return \"1.2.3\";\n };\n\n const poll = async (): Promise<{ isAppOpened: boolean }> => {\n await new Promise((r) => setTimeout(r, 1e3));\n return { isAppOpened: true };\n };\n\n const pollApp = async (): Promise<string> => {\n await new Promise((r) => setTimeout(r, 1e3));\n return \"9000\";\n };\n\n return {\n authenticate,\n finalizePairingHandshake,\n generateSeedFragment,\n generateWrappingKeyFragment,\n getAttestation,\n getConfidentialityKey,\n getConfidentialityPubKey,\n getDeviceUserId,\n getU2FPubKey,\n getValidationPubKey,\n hasPairing,\n initPairingHandshake,\n openSession,\n register,\n registerData,\n setPairing,\n startKKPatternAsResponder,\n startKPatternAsResponder,\n validateVaultOperation,\n getVersion,\n sendAPDU,\n poll,\n pollApp,\n };\n },\n };\n}\n","export const PATHS = {\n U2F: \"5655636'/5583430'\",\n CONFIDENTIALITY: \"5655636'/4410950'\",\n KEY_MATERIAL: \"5655636'/4932953'\",\n VALIDATION: \"5655636'/5652812'\",\n} as const;\n\nexport const STATUS_LENGTH = 2;\n\nexport const SCRIPT_IDS = {\n INIT_SESSION: 3,\n ACCOUNT_MANAGER_SESSION: 2,\n} as const;\n\nexport const STR_ROLES: Record<\"OPERATOR\" | \"ADMIN\" | \"SHARED_OWNER\", string> = {\n OPERATOR: \"00\",\n ADMIN: \"01\",\n SHARED_OWNER: \"02\",\n} as const;\n\nexport const APPID_VAULT_ADMINISTRATOR =\n \"ad5be1a1fe011ce7f53ae081a22ae000a42021f3f94106a3bac9f76e8230e4b9\";\n\nconst all61xxStatus = [];\nfor (let i = 0x6100; i <= 0x61ff; i++) {\n all61xxStatus.push(i);\n}\n\nexport const PAGINATED_STATUS = all61xxStatus;\nexport const STREAMING_RESPONSE = 0x02;\nexport const STREAMING_NEXT_ACTION = 0x01;\n","import { TransportStatusError } from \"@ledgerhq/errors\";\n\nexport const isTransportStatusError = (error: unknown): error is TransportStatusError => {\n return (\n error instanceof TransportStatusError ||\n (error instanceof Error && \"statusCode\" in error && error.message.startsWith(\"Ledger device:\"))\n );\n};\n\nconst CANCEL_STATUS_CODES = [\n 0x6985, // cancel on Ledger Blue\n\n // STAX\n 0xb00c, // cancel collect wk fragment\n 0xb00d, // cancel user registration\n 0xb010, // cancel review\n 0xb012, // cancel master seed generation\n];\n\nexport const isDeviceCancelError = (error: Error): boolean => {\n return isTransportStatusError(error) && CANCEL_STATUS_CODES.includes(error.statusCode);\n};\n","// ------------------------------------------------------------------------------------------------\n// ------------------------------------------------------------------------------------------------\n// THIS IS SOME HORRIBLE CODE IMPORTED JUST\n// FOR THE SAKE OF NOT IMPORTING UGLY, WICKED, AND\n// BROKEN @ledgerhq/live-* LIBRARIES\n//\n// IT'S MEANT TO BE CONSIDERED AS A BLACK BOX\n// ------------------------------------------------------------------------------------------------\n// ------------------------------------------------------------------------------------------------\nimport {\n BluetoothRequired,\n CantOpenDevice,\n DeviceHalted,\n FirmwareOrAppUpdateRequired,\n TransportInterfaceNotAvailable,\n TransportStatusError,\n TransportWebUSBGestureRequired,\n} from \"@ledgerhq/errors\";\nimport type Transport from \"@ledgerhq/hw-transport\";\nimport { Observable, throwError, timer } from \"rxjs\";\nimport { catchError, mergeMap, retryWhen } from \"rxjs/operators\";\n\nconst initialErrorRemapping = (error: Error): Observable<never> =>\n throwError(\n error && error instanceof TransportStatusError && error.statusCode === 0x6faa\n ? new DeviceHalted(error.message)\n : error instanceof TransportStatusError && error.statusCode === 0x6b00\n ? new FirmwareOrAppUpdateRequired(error.message)\n : error,\n );\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nconst never = new Promise(() => {});\n\ntype Discovery = Observable<{\n type: \"add\" | \"remove\";\n id: string;\n name: string;\n}>;\n\nexport type TransportModule = {\n // unique transport name that identify the transport module\n id: string;\n // open a device by an id, this id must be unique across all modules\n // you can typically prefix it with `something|` that identify it globally\n // returns falsy if the transport module can't handle this id\n // here, open means we want to START doing something with the transport\n open: (id: string) => Promise<Transport> | null | undefined;\n // here, close means we want to STOP doing something with the transport\n close?: (transport: Transport, id: string) => Promise<void> | null | undefined;\n // disconnect/interrupt a device connection globally\n // returns falsy if the transport module can't handle this id\n disconnect: (id: string) => Promise<void> | null | undefined;\n // optional observable that allows to discover a transport\n discovery?: Discovery;\n};\n\nconst modules: TransportModule[] = [];\n\nexport const registerTransportModule = (module: TransportModule): void => {\n modules.push(module);\n};\n\nexport const close = (transport: Transport, deviceId: string): Promise<void> => {\n for (const m of modules) {\n const p = m.close?.(transport, deviceId);\n if (p) return p;\n }\n // fallback on an actual close\n return transport.close();\n};\n\nexport const open = (deviceId: string): Promise<Transport> => {\n for (const m of modules) {\n const p = m.open(deviceId);\n if (p) return p;\n }\n return Promise.reject(new Error(`Can't find handler to open ${deviceId}`));\n};\n\nconst transportFinally =\n (cleanup: () => Promise<void>) =>\n <T>(observable: Observable<T>): Observable<T> =>\n // @ts-expect-error typescript not checking against the instanceof\n Observable.create((o) => {\n let done = false;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const finalize = (): any => {\n if (done) return never;\n done = true;\n return cleanup();\n };\n\n const sub = observable.subscribe({\n next: (e) => o.next(e),\n complete: () => {\n void finalize().then(() => o.complete());\n },\n error: (e) => {\n void finalize().then(() => o.error(e));\n },\n });\n return () => {\n sub.unsubscribe();\n void finalize();\n };\n });\n\ntype AccessHook = () => () => void;\n\nconst accessHooks: AccessHook[] = [];\n\nconst deviceQueues = {};\n\nexport const withDevice =\n (deviceId: string) =>\n <T>(job: (t: Transport) => Observable<T>): Observable<T> =>\n new Observable((o) => {\n // @ts-expect-error typescript not checking against the instanceof\n let unsubscribed;\n // @ts-expect-error typescript not checking against the instanceof\n let sub;\n // @ts-expect-error typescript not checking against the instanceof\n const deviceQueue = deviceQueues[deviceId] ?? Promise.resolve();\n\n // @ts-expect-error typescript not checking against the instanceof\n const finalize = (transport, cleanups): Promise<void> =>\n close(transport, deviceId)\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n .catch(() => {})\n .then(() => {\n // @ts-expect-error typescript not checking against the instanceof\n cleanups.forEach((c) => c());\n });\n\n // when we'll finish all the current job, we'll call finish\n // @ts-expect-error typescript not checking against the instanceof\n let finish;\n // this new promise is the next exec queue\n // @ts-expect-error typescript not checking against the instanceof\n deviceQueues[deviceId] = new Promise((resolve) => {\n finish = resolve;\n });\n // for any new job, we'll now wait the exec queue to be available\n deviceQueue\n .then(() => open(deviceId)) // open the transport\n // @ts-expect-error typescript not checking against the instanceof\n .then(async (transport) => {\n // @ts-expect-error typescript not checking against the instanceof\n if (unsubscribed) {\n // it was unsubscribed prematurely\n // @ts-expect-error typescript not checking against the instanceof\n return finalize(transport, [finish]);\n }\n\n if (\n transport.requestConnectionPriority &&\n typeof transport.requestConnectionPriority === \"function\"\n ) {\n await transport.requestConnectionPriority(\"High\");\n }\n\n return transport;\n })\n // @ts-expect-error typescript not checking against the instanceof\n .catch((e) => {\n // @ts-expect-error typescript not checking against the instanceof\n finish();\n if (e instanceof BluetoothRequired) throw e;\n if (e instanceof TransportWebUSBGestureRequired) throw e;\n if (e instanceof TransportInterfaceNotAvailable) throw e;\n throw new CantOpenDevice(e.message);\n })\n // @ts-expect-error typescript not checking against the instanceof\n .then((transport) => {\n if (!transport) return;\n\n // @ts-expect-error typescript not checking against the instanceof\n if (unsubscribed) {\n // it was unsubscribed prematurely\n // @ts-expect-error typescript not checking against the instanceof\n return finalize(transport, [finish]);\n }\n\n const cleanups = accessHooks.map((hook) => hook());\n sub = job(transport) // $FlowFixMe\n .pipe(\n catchError(initialErrorRemapping),\n // $FlowFixMe\n transportFinally(() =>\n // @ts-expect-error typescript not checking against the instanceof\n finalize(transport, [...cleanups, finish]),\n ),\n )\n .subscribe(o);\n })\n // @ts-expect-error typescript not checking against the instanceof\n .catch((error) => o.error(error));\n return () => {\n unsubscribed = true;\n // @ts-expect-error typescript not checking against the instanceof\n if (sub) sub.unsubscribe();\n };\n });\n\nconst WITH_DEVICE_POLLING_DELAY = 500;\n\nexport const retryWhileErrors =\n (acceptError: (arg0: Error) => boolean) =>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (attempts: Observable<any>): Observable<any> =>\n attempts.pipe(\n mergeMap((error) => {\n if (!acceptError(error)) {\n return throwError(error);\n }\n\n return timer(WITH_DEVICE_POLLING_DELAY);\n }),\n );\n\nexport const withDevicePolling =\n (deviceId: string) =>\n <T>(\n job: (arg0: Transport) => Observable<T>,\n acceptError: (arg0: Error) => boolean = () => false,\n ): Observable<T> =>\n withDevice(deviceId)(job).pipe(retryWhen(retryWhileErrors(acceptError)));\n"]}
|