mirror of
https://github.com/arx-research/libhalo.git
synced 2026-01-10 13:48:08 -05:00
CLI: Integrate simulator with halocli and halo-bridge (#383)
This commit is contained in:
committed by
GitHub
parent
60953d858c
commit
2f07653fa0
@@ -52,6 +52,7 @@
|
||||
"nunjucks": "^3.2.4",
|
||||
"open": "^10.1.0",
|
||||
"promise-socket": "^8.0.0",
|
||||
"websocket": "^1.0.35",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -379,6 +379,23 @@ storeGraffitiParser.add_argument("--data", {
|
||||
|
||||
subparsers.add_parser("pcsc_detect", {help: "Detect PC/SC readers and HaLo tags (for debugging)."});
|
||||
|
||||
const cfgSimParser = subparsers.add_parser("sim_cfg", {help: "Configure simulation."})
|
||||
cfgSimParser.add_argument("--url", {required: true});
|
||||
cfgSimParser.add_argument("--secret", {required: true});
|
||||
cfgSimParser.add_argument("--cset-id", {required: true});
|
||||
|
||||
const simSwapParser = subparsers.add_parser("sim_set_card", {help: "Activate card on simulator."})
|
||||
simSwapParser.add_argument("id");
|
||||
|
||||
const simResetParser = subparsers.add_parser("sim_reset", {help: "Reset card set on simulator."})
|
||||
simResetParser.add_argument("--options", {action: JSONParseAction, help: 'Reset options (JSON string), optional.', default: {}});
|
||||
|
||||
subparsers.add_parser("sim_enable", {help: "Enable simulation."});
|
||||
|
||||
subparsers.add_parser("sim_disable", {help: "Disable simulation."});
|
||||
|
||||
subparsers.add_parser("sim_console", {help: "Get simulator console URL."});
|
||||
|
||||
function parseArgs() {
|
||||
const args = parser.parse_args();
|
||||
|
||||
|
||||
@@ -21,10 +21,29 @@ import {
|
||||
wsEventReaderDisconnected
|
||||
} from "./ws_server.js";
|
||||
import {execHaloCmdPCSC} from "@arx-research/libhalo/api/desktop";
|
||||
import {HaloCommandObject, Reader} from "@arx-research/libhalo/types";
|
||||
import {ConnectSimulatorOptions, HaloCommandObject, Reader} from "@arx-research/libhalo/types";
|
||||
import {Namespace} from "argparse";
|
||||
import {INFC, SimNFC} from "./simulator/nfc.js";
|
||||
import fs from "fs";
|
||||
import {getSimConfig, getSimConfigPath} from "./util.js";
|
||||
|
||||
let simOptions: ConnectSimulatorOptions | null = null;
|
||||
let nfc: INFC;
|
||||
|
||||
if (fs.existsSync(getSimConfigPath())) {
|
||||
const simConfig = getSimConfig();
|
||||
|
||||
if (simConfig.enabled) {
|
||||
simOptions = simConfig as ConnectSimulatorOptions;
|
||||
}
|
||||
}
|
||||
|
||||
if (!simOptions) {
|
||||
nfc = new NFC();
|
||||
} else {
|
||||
nfc = new SimNFC(simOptions);
|
||||
}
|
||||
|
||||
const nfc = new NFC();
|
||||
let stopPCSCTimeout: number | null = null;
|
||||
let isConnected = false;
|
||||
let isClosing = false;
|
||||
@@ -40,6 +59,15 @@ async function checkCard(reader: Reader) {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureSimulator() {
|
||||
if (!(nfc instanceof SimNFC)) {
|
||||
console.error('Simulator is not enabled!');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return nfc;
|
||||
}
|
||||
|
||||
function runHalo(entryMode: string, args: Namespace) {
|
||||
nfc.on('reader', (reader: Reader) => {
|
||||
reader.autoProcessing = false;
|
||||
@@ -59,6 +87,12 @@ function runHalo(entryMode: string, args: Namespace) {
|
||||
}
|
||||
|
||||
reader.on('card', card => {
|
||||
if (args.output === "color") {
|
||||
if (nfc instanceof SimNFC) {
|
||||
console.warn('[!] Running on simulator (cset_id=' + nfc.getCardSetID() + ')');
|
||||
}
|
||||
}
|
||||
|
||||
if (entryMode === "server") {
|
||||
(async () => {
|
||||
if (await checkCard(reader)) {
|
||||
@@ -88,6 +122,17 @@ function runHalo(entryMode: string, args: Namespace) {
|
||||
if (args.name === "pcsc_detect") {
|
||||
console.log("HaLo tag detected:", reader.reader.name);
|
||||
res = {"status": "ok"};
|
||||
} else if (args.name === "sim_console") {
|
||||
console.log(ensureSimulator().getConsoleURL());
|
||||
process.exit(0);
|
||||
} else if (args.name === "sim_set_card") {
|
||||
await ensureSimulator().swapCard(args.id);
|
||||
console.log('Card swapped on simulator.');
|
||||
process.exit(0);
|
||||
} else if (args.name === "sim_reset") {
|
||||
await ensureSimulator().resetCardSet(args.options);
|
||||
console.log('Card set was reset.');
|
||||
process.exit(0);
|
||||
} else if (args.name === "test") {
|
||||
res = await __runTestSuite({"__this_is_unsafe": true},
|
||||
"pcsc", async (command: HaloCommandObject) => await execHaloCmdPCSC(command, reader));
|
||||
|
||||
@@ -7,19 +7,39 @@
|
||||
import {parseArgs} from './args_cli.js';
|
||||
import {runHalo} from "./cli.js";
|
||||
import {printVersionInfo, getVersionInfo} from "./version.js";
|
||||
import {getSimConfig, saveSimConfig} from "./util.js";
|
||||
|
||||
let args = parseArgs();
|
||||
const args = parseArgs();
|
||||
|
||||
if (args && args.name === "cli_version") {
|
||||
if (args.output === "json") {
|
||||
let versionInfo = getVersionInfo() ?? {};
|
||||
const versionInfo = getVersionInfo() ?? {};
|
||||
console.log(JSON.stringify(versionInfo));
|
||||
} else {
|
||||
printVersionInfo();
|
||||
}
|
||||
} else if (args && args.name === "sim_cfg") {
|
||||
const simConfig = {
|
||||
enabled: true,
|
||||
url: args.url,
|
||||
authSecret: args.secret,
|
||||
cardId: args.cset_id
|
||||
};
|
||||
saveSimConfig(simConfig);
|
||||
console.log('Config updated.');
|
||||
} else if (args && args.name === "sim_enable") {
|
||||
const simConfig = getSimConfig();
|
||||
simConfig.enabled = true;
|
||||
saveSimConfig(simConfig);
|
||||
console.log('Config updated.');
|
||||
} else if (args && args.name === "sim_disable") {
|
||||
const simConfig = getSimConfig();
|
||||
simConfig.enabled = false;
|
||||
saveSimConfig(simConfig);
|
||||
console.log('Config updated.');
|
||||
}
|
||||
|
||||
if (!args || args.name === "cli_version") {
|
||||
if (!args || ["cli_version", "sim_cfg", "sim_enable", "sim_disable"].indexOf(args.name) !== -1) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
|
||||
129
cli/src.ts/simulator/nfc.ts
Normal file
129
cli/src.ts/simulator/nfc.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import {Reader, ReaderEventListener, Card, ConnectSimulatorOptions} from "@arx-research/libhalo/types";
|
||||
import {Buffer} from 'buffer/index.js';
|
||||
import {HaloSimulator} from "@arx-research/libhalo/api/common";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
import websocket from 'websocket';
|
||||
|
||||
export interface INFCOn {
|
||||
(eventName: 'reader', listener: (reader: Reader) => void): void;
|
||||
(eventName: 'error', listener: (error: Error) => void): void;
|
||||
}
|
||||
|
||||
export interface INFC {
|
||||
on: INFCOn;
|
||||
readers: Record<string, Reader>;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
export class SimNFC implements INFC {
|
||||
_connectedReader = new SimReader();
|
||||
readers: Record<string, Reader>;
|
||||
_onReader: ((reader: Reader) => void)[] = [];
|
||||
_onError: ((error: Error) => void)[] = [];
|
||||
_options: ConnectSimulatorOptions;
|
||||
|
||||
constructor(options: ConnectSimulatorOptions) {
|
||||
this._options = options;
|
||||
this.readers = {};
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
try {
|
||||
await this._connectedReader.initialize(this._options);
|
||||
} catch (e) {
|
||||
this._onError.forEach((listener) => {
|
||||
listener(e as Error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._onReader.forEach((listener) => {
|
||||
const rdrName = this._connectedReader.name;
|
||||
this.readers = {rdrName: this._connectedReader};
|
||||
listener(this._connectedReader);
|
||||
});
|
||||
}
|
||||
|
||||
getCardSetID() {
|
||||
return this._options.cardId;
|
||||
}
|
||||
|
||||
getConsoleURL() {
|
||||
return this._connectedReader.sim.getConsoleURL();
|
||||
}
|
||||
|
||||
async swapCard(selectedId: number) {
|
||||
return await this._connectedReader.sim.swapCard(selectedId);
|
||||
}
|
||||
|
||||
async resetCardSet(options: Record<string, string>) {
|
||||
return await this._connectedReader.sim.resetCardSet(options);
|
||||
}
|
||||
|
||||
on: INFCOn = (eventName, listener) => {
|
||||
if (eventName === "reader") {
|
||||
const _listener = listener as (reader: Reader) => void;
|
||||
|
||||
if (this._onReader.length === 0) {
|
||||
setTimeout(() => this.initialize(), 1);
|
||||
}
|
||||
|
||||
this._onReader.push(_listener);
|
||||
} else if (eventName === "error") {
|
||||
const _listener = listener as (error: Error) => void;
|
||||
this._onError.push(_listener);
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this._connectedReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
export class SimCard implements Card {
|
||||
type = "TAG_ISO_14443_4"
|
||||
atr = Buffer.from("3b8c80019067464a010088060000000079", "hex")
|
||||
}
|
||||
|
||||
export class SimReader implements Reader {
|
||||
autoProcessing: boolean = false;
|
||||
name: string;
|
||||
reader: { name: string };
|
||||
sim: HaloSimulator;
|
||||
_insertedCard = new SimCard();
|
||||
|
||||
constructor() {
|
||||
this.autoProcessing = false;
|
||||
this.name = "Simulator"
|
||||
this.reader = {name: this.name};
|
||||
this.sim = new HaloSimulator({
|
||||
createWebSocket: (url) => new websocket.w3cwebsocket(url),
|
||||
noDebugPrints: true
|
||||
});
|
||||
}
|
||||
|
||||
async initialize(options: ConnectSimulatorOptions) {
|
||||
this.name = "Simulator " + options.cardId
|
||||
this.reader.name = this.name;
|
||||
await this.sim.connect(options);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.sim.disconnect();
|
||||
}
|
||||
|
||||
async transmit(data: Buffer, responseMaxLength: number): Promise<Buffer> {
|
||||
return await this.sim.execRawAPDU(data);
|
||||
}
|
||||
|
||||
on: ReaderEventListener = (eventName, listener) => {
|
||||
const _listener = listener as (card: Card) => void;
|
||||
|
||||
if (eventName === "card") {
|
||||
setTimeout(() => _listener(this._insertedCard as Card), 1);
|
||||
}
|
||||
|
||||
// remaining types of events are not simulated at all
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ import { fileURLToPath } from 'node:url';
|
||||
import { dirname as path_dirname, join as path_join } from 'node:path';
|
||||
import crypto from "crypto";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
|
||||
function randomBuffer() {
|
||||
return Buffer.from(crypto.getRandomValues(new Uint8Array(32)));
|
||||
@@ -34,6 +36,22 @@ function saveLog(log: Record<string, string | string[]>) {
|
||||
});
|
||||
}
|
||||
|
||||
function getSimConfigPath() {
|
||||
return path.join(os.homedir(), '.halo-simulator.json');
|
||||
}
|
||||
|
||||
function simConfigExists() {
|
||||
return fs.existsSync(getSimConfigPath());
|
||||
}
|
||||
|
||||
function getSimConfig() {
|
||||
return JSON.parse(fs.readFileSync(getSimConfigPath(), 'utf-8'));
|
||||
}
|
||||
|
||||
function saveSimConfig(simConfig: unknown) {
|
||||
fs.writeFileSync(getSimConfigPath(), JSON.stringify(simConfig, null, 4));
|
||||
}
|
||||
|
||||
let dirname: string;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
@@ -43,7 +61,6 @@ if (process.pkg && process.pkg.entrypoint) {
|
||||
} else {
|
||||
const filename = fileURLToPath(import.meta.url);
|
||||
dirname = path_join(path_dirname(filename), '..');
|
||||
console.log(dirname);
|
||||
}
|
||||
|
||||
export {dirname, randomBuffer, saveLog};
|
||||
export {dirname, randomBuffer, saveLog, getSimConfigPath, simConfigExists, getSimConfig, saveSimConfig};
|
||||
|
||||
112
cli/yarn.lock
112
cli/yarn.lock
@@ -8,7 +8,7 @@
|
||||
integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==
|
||||
|
||||
"@arx-research/libhalo@../core":
|
||||
version "1.6.3"
|
||||
version "1.7.6"
|
||||
dependencies:
|
||||
buffer "^6.0.3"
|
||||
elliptic "^6.5.5"
|
||||
@@ -835,7 +835,7 @@ buffer@^6.0.3:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.2.1"
|
||||
|
||||
bufferutil@^4.0.8:
|
||||
bufferutil@^4.0.1, bufferutil@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea"
|
||||
integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==
|
||||
@@ -1034,6 +1034,14 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
d@1, d@^1.0.1, d@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de"
|
||||
integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==
|
||||
dependencies:
|
||||
es5-ext "^0.10.64"
|
||||
type "^2.7.2"
|
||||
|
||||
data-view-buffer@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2"
|
||||
@@ -1061,7 +1069,7 @@ data-view-byte-offset@^1.0.0:
|
||||
es-errors "^1.3.0"
|
||||
is-data-view "^1.0.1"
|
||||
|
||||
debug@2.6.9:
|
||||
debug@2.6.9, debug@^2.2.0:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
@@ -1324,6 +1332,33 @@ es-to-primitive@^1.2.1:
|
||||
is-date-object "^1.0.1"
|
||||
is-symbol "^1.0.2"
|
||||
|
||||
es5-ext@^0.10.35, es5-ext@^0.10.62, es5-ext@^0.10.63, es5-ext@^0.10.64, es5-ext@~0.10.14:
|
||||
version "0.10.64"
|
||||
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714"
|
||||
integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==
|
||||
dependencies:
|
||||
es6-iterator "^2.0.3"
|
||||
es6-symbol "^3.1.3"
|
||||
esniff "^2.0.1"
|
||||
next-tick "^1.1.0"
|
||||
|
||||
es6-iterator@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
|
||||
integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "^0.10.35"
|
||||
es6-symbol "^3.1.1"
|
||||
|
||||
es6-symbol@^3.1.1, es6-symbol@^3.1.3:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c"
|
||||
integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==
|
||||
dependencies:
|
||||
d "^1.0.2"
|
||||
ext "^1.7.0"
|
||||
|
||||
escalade@^3.1.1, escalade@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
|
||||
@@ -1405,6 +1440,16 @@ eslint@^9.7.0:
|
||||
strip-ansi "^6.0.1"
|
||||
text-table "^0.2.0"
|
||||
|
||||
esniff@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308"
|
||||
integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==
|
||||
dependencies:
|
||||
d "^1.0.1"
|
||||
es5-ext "^0.10.62"
|
||||
event-emitter "^0.3.5"
|
||||
type "^2.7.2"
|
||||
|
||||
espree@^10.0.1, espree@^10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56"
|
||||
@@ -1461,6 +1506,14 @@ ethers@^6.13.1:
|
||||
tslib "2.4.0"
|
||||
ws "8.17.1"
|
||||
|
||||
event-emitter@^0.3.5:
|
||||
version "0.3.5"
|
||||
resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
|
||||
integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "~0.10.14"
|
||||
|
||||
events@^3.2.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
||||
@@ -1508,6 +1561,13 @@ express@^4.19.2:
|
||||
utils-merge "1.0.1"
|
||||
vary "~1.1.2"
|
||||
|
||||
ext@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f"
|
||||
integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==
|
||||
dependencies:
|
||||
type "^2.7.2"
|
||||
|
||||
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
@@ -2084,6 +2144,11 @@ is-typed-array@^1.1.13:
|
||||
dependencies:
|
||||
which-typed-array "^1.1.14"
|
||||
|
||||
is-typedarray@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
|
||||
|
||||
is-weakref@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
|
||||
@@ -2443,6 +2508,11 @@ neo-async@^2.6.2:
|
||||
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
|
||||
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
|
||||
|
||||
next-tick@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
|
||||
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
|
||||
|
||||
nfc-pcsc@^0.8.1:
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/nfc-pcsc/-/nfc-pcsc-0.8.1.tgz#76882be17842cb832a72b76345e665c8108ff049"
|
||||
@@ -3398,6 +3468,11 @@ type-is@~1.6.18:
|
||||
media-typer "0.3.0"
|
||||
mime-types "~2.1.24"
|
||||
|
||||
type@^2.7.2:
|
||||
version "2.7.3"
|
||||
resolved "https://registry.yarnpkg.com/type/-/type-2.7.3.tgz#436981652129285cc3ba94f392886c2637ea0486"
|
||||
integrity sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==
|
||||
|
||||
typed-array-buffer@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3"
|
||||
@@ -3442,6 +3517,13 @@ typed-array-length@^1.0.6:
|
||||
is-typed-array "^1.1.13"
|
||||
possible-typed-array-names "^1.0.0"
|
||||
|
||||
typedarray-to-buffer@^3.1.5:
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
|
||||
integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
|
||||
dependencies:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
typescript-eslint@^7.16.0:
|
||||
version "7.16.0"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-7.16.0.tgz#8466a7c6d8b5acfdcaf53ecc8ecf240072b346d8"
|
||||
@@ -3496,6 +3578,13 @@ uri-js@^4.2.2:
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
utf-8-validate@^5.0.2:
|
||||
version "5.0.10"
|
||||
resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2"
|
||||
integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==
|
||||
dependencies:
|
||||
node-gyp-build "^4.3.0"
|
||||
|
||||
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
@@ -3597,6 +3686,18 @@ websocket-as-promised@^2.1.0:
|
||||
promise.prototype.finally "^3.1.2"
|
||||
promised-map "^1.0.0"
|
||||
|
||||
websocket@^1.0.35:
|
||||
version "1.0.35"
|
||||
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.35.tgz#374197207d7d4cc4c36cbf8a1bb886ee52a07885"
|
||||
integrity sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==
|
||||
dependencies:
|
||||
bufferutil "^4.0.1"
|
||||
debug "^2.2.0"
|
||||
es5-ext "^0.10.63"
|
||||
typedarray-to-buffer "^3.1.5"
|
||||
utf-8-validate "^5.0.2"
|
||||
yaeti "^0.0.6"
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||
@@ -3692,6 +3793,11 @@ y18n@^5.0.5:
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
||||
|
||||
yaeti@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577"
|
||||
integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==
|
||||
|
||||
yargs-parser@^18.1.2:
|
||||
version "18.1.3"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
|
||||
|
||||
@@ -23,13 +23,15 @@ class HaloSimulator {
|
||||
protected readonly createWebSocket: (url: string) => WebSocket;
|
||||
protected ws: WebSocketAsPromised | null;
|
||||
protected _onDisconnected = new SignalDispatcher();
|
||||
protected noDebugPrints: boolean = false;
|
||||
|
||||
constructor(options?: SimulatorOptions) {
|
||||
options = Object.assign({}, options);
|
||||
|
||||
this.url = null;
|
||||
this.consoleUrl = null;
|
||||
this.ws = null;
|
||||
this.consoleUrl = null;
|
||||
this.noDebugPrints = !!options.noDebugPrints;
|
||||
|
||||
this.createWebSocket = options.createWebSocket
|
||||
? options.createWebSocket
|
||||
@@ -52,7 +54,9 @@ class HaloSimulator {
|
||||
}
|
||||
|
||||
async connect(options: ConnectSimulatorOptions) {
|
||||
console.log('[libhalo][simulator] Simulator connecting...');
|
||||
if (!this.noDebugPrints) {
|
||||
console.log('[libhalo][simulator] Simulator connecting...');
|
||||
}
|
||||
this.url = await this.makeSignedURL(options.url + "/ws", options.authSecret, options.cardId, "180 seconds");
|
||||
const tmpConsoleUrl = (options.url + "/console")
|
||||
.replace("ws://", "http://")
|
||||
@@ -73,7 +77,11 @@ class HaloSimulator {
|
||||
|
||||
await this.ws.open();
|
||||
const welcomePacket = await this.waitForWelcomePacket();
|
||||
console.log('[libhalo][simulator] Connected, console URL: ', this.consoleUrl);
|
||||
|
||||
if (!this.noDebugPrints) {
|
||||
console.log('[libhalo][simulator] Connected, console URL: ', this.consoleUrl);
|
||||
}
|
||||
|
||||
return welcomePacket;
|
||||
}
|
||||
|
||||
@@ -144,6 +152,19 @@ class HaloSimulator {
|
||||
return buf;
|
||||
}
|
||||
|
||||
async execRawAPDU(data: Buffer): Promise<Buffer> {
|
||||
const res = await this.ws!.sendRequest({
|
||||
"type": "apdu",
|
||||
"data": data.toString('hex').toUpperCase()
|
||||
});
|
||||
|
||||
if (res.type !== "rapdu") {
|
||||
throw new Error("Unexpected packet returned by simulator.");
|
||||
}
|
||||
|
||||
return Buffer.from(res.data, "hex");
|
||||
}
|
||||
|
||||
async execHaloCmd(command: HaloCommandObject) {
|
||||
const cmdOpts: ExecHaloCmdOptions = {
|
||||
method: "simulator",
|
||||
@@ -189,14 +210,20 @@ class HaloSimulator {
|
||||
};
|
||||
|
||||
let res;
|
||||
console.log('[libhalo][simulator] => ', command);
|
||||
if (!this.noDebugPrints) {
|
||||
console.log('[libhalo][simulator] => ', command);
|
||||
}
|
||||
try {
|
||||
res = await execHaloCmd(command, cmdOpts);
|
||||
} catch (e) {
|
||||
console.error('[libhalo][simulator] err', e);
|
||||
if (!this.noDebugPrints) {
|
||||
console.error('[libhalo][simulator] err', e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
console.log('[libhalo][simulator] <= ', res);
|
||||
if (!this.noDebugPrints) {
|
||||
console.log('[libhalo][simulator] <= ', res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,14 @@ export interface ReaderEventListener {
|
||||
}
|
||||
|
||||
export interface Reader {
|
||||
name: string
|
||||
reader: {
|
||||
name: string
|
||||
}
|
||||
autoProcessing: boolean
|
||||
transmit: (data: Buffer, responseMaxLength: number) => Promise<Buffer>;
|
||||
transmit: (data: Buffer, responseMaxLength: number) => Promise<Buffer>
|
||||
on: ReaderEventListener
|
||||
close: () => void
|
||||
}
|
||||
|
||||
export interface TransceiveFunc {
|
||||
@@ -104,7 +106,9 @@ export interface BaseCreateWSOptions {
|
||||
}
|
||||
|
||||
export interface BridgeOptions extends BaseCreateWSOptions {}
|
||||
export interface SimulatorOptions extends BaseCreateWSOptions {}
|
||||
export interface SimulatorOptions extends BaseCreateWSOptions {
|
||||
noDebugPrints?: boolean
|
||||
}
|
||||
|
||||
export interface ConnectSimulatorOptions {
|
||||
url: string
|
||||
|
||||
Reference in New Issue
Block a user