CLI: Integrate simulator with halocli and halo-bridge (#383)

This commit is contained in:
Michał Leszczyński
2024-09-08 02:18:04 +02:00
committed by GitHub
parent 60953d858c
commit 2f07653fa0
9 changed files with 384 additions and 18 deletions

View File

@@ -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": {

View File

@@ -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();

View File

@@ -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));

View File

@@ -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
View 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
}
}

View File

@@ -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};

View File

@@ -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"

View File

@@ -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;
}
}

View File

@@ -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