keyfabe 0.2.0 → 0.4.0
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/README.md +23 -12
- package/dist/commands/clone.js +42 -15
- package/dist/commands/clone.js.map +1 -1
- package/dist/commands/delete.d.ts +1 -1
- package/dist/commands/delete.js +17 -3
- package/dist/commands/delete.js.map +1 -1
- package/dist/commands/doctor.js +28 -23
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/export.js +2 -3
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/import.js +5 -5
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/list.js +8 -9
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/read.js +34 -9
- package/dist/commands/read.js.map +1 -1
- package/dist/commands/rename.d.ts +1 -1
- package/dist/commands/rename.js +24 -4
- package/dist/commands/rename.js.map +1 -1
- package/dist/commands/setup.js +65 -56
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/show.d.ts +1 -1
- package/dist/commands/show.js +19 -10
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/write.d.ts +1 -1
- package/dist/commands/write.js +15 -6
- package/dist/commands/write.js.map +1 -1
- package/dist/index.js +44 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/card-ops.js +29 -16
- package/dist/lib/card-ops.js.map +1 -1
- package/dist/lib/display.js +7 -9
- package/dist/lib/display.js.map +1 -1
- package/dist/lib/parsers.d.ts +1 -0
- package/dist/lib/parsers.js +22 -0
- package/dist/lib/parsers.js.map +1 -1
- package/dist/lib/pm3.js +2 -2
- package/dist/lib/pm3.js.map +1 -1
- package/dist/lib/prompts.d.ts +2 -1
- package/dist/lib/prompts.js +25 -17
- package/dist/lib/prompts.js.map +1 -1
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
A TypeScript CLI tool that wraps the Proxmark3 client to provide an ergonomic keyfob cloning workflow.
|
|
4
4
|
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="demo.gif" alt="keyfabe demo" width="800" />
|
|
7
|
+
</p>
|
|
8
|
+
|
|
5
9
|
## What it does
|
|
6
10
|
|
|
7
11
|
- Automates the multi-step read/detect/clone/verify process into a single guided flow
|
|
@@ -35,7 +39,12 @@ npx keyfabe
|
|
|
35
39
|
|
|
36
40
|
## Usage
|
|
37
41
|
|
|
42
|
+
Running `keyfabe` with no arguments launches an interactive wizard menu that guides you through all available actions.
|
|
43
|
+
|
|
38
44
|
```sh
|
|
45
|
+
# launch interactive wizard
|
|
46
|
+
keyfabe
|
|
47
|
+
|
|
39
48
|
# check device connection, firmware, and antenna health
|
|
40
49
|
keyfabe doctor
|
|
41
50
|
|
|
@@ -49,19 +58,19 @@ keyfabe clone
|
|
|
49
58
|
keyfabe read
|
|
50
59
|
|
|
51
60
|
# write a previously-saved identity to a blank fob
|
|
52
|
-
keyfabe write
|
|
61
|
+
keyfabe write [name]
|
|
53
62
|
|
|
54
63
|
# list saved identities
|
|
55
64
|
keyfabe list
|
|
56
65
|
|
|
57
66
|
# show details of a saved identity
|
|
58
|
-
keyfabe show
|
|
67
|
+
keyfabe show [name]
|
|
59
68
|
|
|
60
69
|
# rename a saved identity
|
|
61
|
-
keyfabe rename
|
|
70
|
+
keyfabe rename [old-name] [new-name]
|
|
62
71
|
|
|
63
72
|
# delete a saved identity
|
|
64
|
-
keyfabe delete
|
|
73
|
+
keyfabe delete [name]
|
|
65
74
|
|
|
66
75
|
# export all saved identities as JSON
|
|
67
76
|
keyfabe export > fobs.json
|
|
@@ -70,6 +79,8 @@ keyfabe export > fobs.json
|
|
|
70
79
|
keyfabe import fobs.json
|
|
71
80
|
```
|
|
72
81
|
|
|
82
|
+
Commands that take `[name]` arguments are fully optional — when omitted, you'll get an interactive fob picker.
|
|
83
|
+
|
|
73
84
|
## Commands
|
|
74
85
|
|
|
75
86
|
### `keyfabe doctor`
|
|
@@ -88,25 +99,25 @@ Guided clone flow: reads the original fob, detects a blank T55x7, writes the ID,
|
|
|
88
99
|
|
|
89
100
|
Reads and identifies whatever fob is on the antenna. Supports EM410x and HID Prox. Optionally saves the identity.
|
|
90
101
|
|
|
91
|
-
### `keyfabe write
|
|
102
|
+
### `keyfabe write [name]`
|
|
92
103
|
|
|
93
|
-
Writes a previously-saved identity to a blank T55x7 fob.
|
|
104
|
+
Writes a previously-saved identity to a blank T55x7 fob. Without a name, presents an interactive picker.
|
|
94
105
|
|
|
95
106
|
### `keyfabe list`
|
|
96
107
|
|
|
97
108
|
Lists all saved fob identities from `~/.keyfabe/fobs.json`.
|
|
98
109
|
|
|
99
|
-
### `keyfabe show
|
|
110
|
+
### `keyfabe show [name]`
|
|
100
111
|
|
|
101
|
-
Displays full details of a saved fob identity (type, ID, encoding, save date).
|
|
112
|
+
Displays full details of a saved fob identity (type, ID, encoding, save date). Without a name, presents an interactive picker.
|
|
102
113
|
|
|
103
|
-
### `keyfabe rename
|
|
114
|
+
### `keyfabe rename [old-name] [new-name]`
|
|
104
115
|
|
|
105
|
-
Renames a saved fob identity.
|
|
116
|
+
Renames a saved fob identity. Missing arguments are prompted interactively.
|
|
106
117
|
|
|
107
|
-
### `keyfabe delete
|
|
118
|
+
### `keyfabe delete [name]`
|
|
108
119
|
|
|
109
|
-
Deletes a saved fob identity.
|
|
120
|
+
Deletes a saved fob identity. Without a name, presents an interactive picker.
|
|
110
121
|
|
|
111
122
|
### `keyfabe export`
|
|
112
123
|
|
package/dist/commands/clone.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import ora from "ora";
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
3
2
|
import { writeAndVerify } from "../lib/card-ops.js";
|
|
4
3
|
import { printCardInfo, printDoctorHint } from "../lib/display.js";
|
|
5
|
-
import { parseLfSearch } from "../lib/parsers.js";
|
|
4
|
+
import { parseHfSearch, parseLfSearch } from "../lib/parsers.js";
|
|
6
5
|
import { Pm3Error, pm3Exec, requireDevice } from "../lib/pm3.js";
|
|
7
6
|
import { promptName, waitForEnter } from "../lib/prompts.js";
|
|
8
7
|
import { saveFob } from "../lib/store.js";
|
|
@@ -11,47 +10,74 @@ const READ_RETRY_DELAY = 2000;
|
|
|
11
10
|
export async function clone() {
|
|
12
11
|
if (!(await requireDevice()))
|
|
13
12
|
return false;
|
|
14
|
-
|
|
13
|
+
p.intro("Keyfob Clone");
|
|
15
14
|
// Step 1: Read original
|
|
16
15
|
await waitForEnter("Place your original keyfob on the antenna.");
|
|
17
16
|
let card = null;
|
|
18
17
|
for (let attempt = 1; attempt <= MAX_READ_RETRIES; attempt++) {
|
|
19
|
-
const
|
|
18
|
+
const s = p.spinner();
|
|
19
|
+
s.start(`Reading original (attempt ${attempt}/${MAX_READ_RETRIES})...`);
|
|
20
20
|
try {
|
|
21
21
|
const { stdout } = await pm3Exec("lf search");
|
|
22
22
|
card = parseLfSearch(stdout);
|
|
23
23
|
if (card) {
|
|
24
|
-
|
|
24
|
+
s.stop("Original card read");
|
|
25
25
|
break;
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
s.stop("No card detected.");
|
|
28
|
+
p.log.error("No card detected.");
|
|
28
29
|
}
|
|
29
30
|
catch (err) {
|
|
30
31
|
if (err instanceof Pm3Error) {
|
|
31
|
-
|
|
32
|
+
s.stop(err.message);
|
|
33
|
+
p.log.error(err.message);
|
|
32
34
|
if (err.message.includes("not found")) {
|
|
33
35
|
printDoctorHint();
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
38
|
else {
|
|
37
|
-
|
|
39
|
+
s.stop("Read failed.");
|
|
40
|
+
p.log.error("Read failed.");
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
43
|
if (attempt < MAX_READ_RETRIES) {
|
|
41
|
-
|
|
44
|
+
p.log.info(`Retrying in ${READ_RETRY_DELAY / 1000}s...`);
|
|
42
45
|
await new Promise((r) => setTimeout(r, READ_RETRY_DELAY));
|
|
43
46
|
}
|
|
44
47
|
}
|
|
48
|
+
// If LF search failed, try HF
|
|
45
49
|
if (!card) {
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
const hfSpinner = p.spinner();
|
|
51
|
+
hfSpinner.start("No LF card found, trying HF...");
|
|
52
|
+
try {
|
|
53
|
+
const { stdout } = await pm3Exec("hf search");
|
|
54
|
+
const hfCard = parseHfSearch(stdout);
|
|
55
|
+
if (hfCard) {
|
|
56
|
+
hfSpinner.stop("HF card detected");
|
|
57
|
+
printCardInfo(hfCard);
|
|
58
|
+
p.log.warn(`HF card cloning (${hfCard.type}) is not yet supported. Only LF keyfobs (EM410x, HID Prox) can be cloned.`);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
if (err instanceof Pm3Error) {
|
|
64
|
+
hfSpinner.stop(err.message);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
hfSpinner.stop("HF search failed.");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (!card) {
|
|
71
|
+
p.log.error("Failed to read original card after all attempts.");
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
48
74
|
}
|
|
49
75
|
printCardInfo(card);
|
|
50
76
|
// Step 2: Write to blank
|
|
51
77
|
await waitForEnter("Remove original and place a blank T55x7 fob on the antenna.");
|
|
52
78
|
const success = await writeAndVerify(card);
|
|
53
79
|
if (success) {
|
|
54
|
-
|
|
80
|
+
p.log.success("Clone successful!");
|
|
55
81
|
const name = await promptName();
|
|
56
82
|
if (name) {
|
|
57
83
|
await saveFob({
|
|
@@ -61,11 +87,12 @@ export async function clone() {
|
|
|
61
87
|
encoding: card.encoding,
|
|
62
88
|
savedAt: new Date().toISOString(),
|
|
63
89
|
});
|
|
64
|
-
|
|
90
|
+
p.log.success(`Saved as "${name}".`);
|
|
65
91
|
}
|
|
92
|
+
p.outro("Done!");
|
|
66
93
|
return true;
|
|
67
94
|
}
|
|
68
|
-
|
|
95
|
+
p.log.error("Clone failed.");
|
|
69
96
|
return false;
|
|
70
97
|
}
|
|
71
98
|
//# sourceMappingURL=clone.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clone.js","sourceRoot":"","sources":["../../src/commands/clone.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"clone.js","sourceRoot":"","sources":["../../src/commands/clone.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAC3B,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B,MAAM,CAAC,KAAK,UAAU,KAAK;IACvB,IAAI,CAAC,CAAC,MAAM,aAAa,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAE3C,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAExB,wBAAwB;IACxB,MAAM,YAAY,CAAC,4CAA4C,CAAC,CAAC;IAEjE,IAAI,IAAI,GAAqC,IAAI,CAAC;IAElD,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,gBAAgB,EAAE,OAAO,EAAE,EAAE,CAAC;QAC3D,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;QACtB,CAAC,CAAC,KAAK,CAAC,6BAA6B,OAAO,IAAI,gBAAgB,MAAM,CAAC,CAAC;QACxE,IAAI,CAAC;YACD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;YAC9C,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;YAC7B,IAAI,IAAI,EAAE,CAAC;gBACP,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;gBAC7B,MAAM;YACV,CAAC;YACD,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YAC5B,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;gBAC1B,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACpB,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACzB,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;oBACpC,eAAe,EAAE,CAAC;gBACtB,CAAC;YACL,CAAC;iBAAM,CAAC;gBACJ,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;gBACvB,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;YAChC,CAAC;QACL,CAAC;QAED,IAAI,OAAO,GAAG,gBAAgB,EAAE,CAAC;YAC7B,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,gBAAgB,GAAG,IAAI,MAAM,CAAC,CAAC;YACzD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;QAC9D,CAAC;IACL,CAAC;IAED,8BAA8B;IAC9B,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,MAAM,SAAS,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;QAC9B,SAAS,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAClD,IAAI,CAAC;YACD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;YACrC,IAAI,MAAM,EAAE,CAAC;gBACT,SAAS,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;gBACnC,aAAa,CAAC,MAAM,CAAC,CAAC;gBACtB,CAAC,CAAC,GAAG,CAAC,IAAI,CACN,oBAAoB,MAAM,CAAC,IAAI,2EAA2E,CAC7G,CAAC;gBACF,OAAO,KAAK,CAAC;YACjB,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;gBAC1B,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAChC,CAAC;iBAAM,CAAC;gBACJ,SAAS,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YACxC,CAAC;QACL,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;YAChE,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IAED,aAAa,CAAC,IAAI,CAAC,CAAC;IAEpB,yBAAyB;IACzB,MAAM,YAAY,CAAC,6DAA6D,CAAC,CAAC;IAElF,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAE3C,IAAI,OAAO,EAAE,CAAC;QACV,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,MAAM,UAAU,EAAE,CAAC;QAChC,IAAI,IAAI,EAAE,CAAC;YACP,MAAM,OAAO,CAAC;gBACV,IAAI;gBACJ,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC,CAAC,CAAC;YACH,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,CAAC;QACzC,CAAC;QACD,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAC7B,OAAO,KAAK,CAAC;AACjB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function deleteFob(name
|
|
1
|
+
export declare function deleteFob(name?: string): Promise<boolean>;
|
package/dist/commands/delete.js
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
2
|
import { printFobNotFound } from "../lib/display.js";
|
|
3
|
-
import {
|
|
3
|
+
import { confirm, selectFob } from "../lib/prompts.js";
|
|
4
|
+
import { loadFobs, removeFob } from "../lib/store.js";
|
|
4
5
|
export async function deleteFob(name) {
|
|
6
|
+
if (!name) {
|
|
7
|
+
const fobs = await loadFobs();
|
|
8
|
+
if (fobs.length === 0) {
|
|
9
|
+
p.log.warn("No saved fobs. Use `keyfabe read` or `keyfabe clone` first.");
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
name = await selectFob(fobs, "Which fob to delete?");
|
|
13
|
+
}
|
|
14
|
+
const shouldDelete = await confirm(`Delete "${name}"?`);
|
|
15
|
+
if (!shouldDelete) {
|
|
16
|
+
p.cancel("Deletion cancelled.");
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
5
19
|
const removed = await removeFob(name);
|
|
6
20
|
if (!removed) {
|
|
7
21
|
printFobNotFound(name);
|
|
8
22
|
return false;
|
|
9
23
|
}
|
|
10
|
-
|
|
24
|
+
p.log.success(`Deleted "${name}".`);
|
|
11
25
|
return true;
|
|
12
26
|
}
|
|
13
27
|
//# sourceMappingURL=delete.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/commands/delete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/commands/delete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEtD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAa;IACzC,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,MAAM,IAAI,GAAG,MAAM,QAAQ,EAAE,CAAC;QAC9B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpB,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;YAC1E,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC,YAAY,EAAE,CAAC;QAChB,CAAC,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAChC,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACvB,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC;IACpC,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,78 +1,83 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
1
2
|
import chalk from "chalk";
|
|
2
|
-
import ora from "ora";
|
|
3
3
|
import { HF_VOLTAGE_THRESHOLD, LF_VOLTAGE_THRESHOLD, printBrewInstall } from "../lib/display.js";
|
|
4
4
|
import { parseHwStatus, parseHwTune } from "../lib/parsers.js";
|
|
5
5
|
import { detectPort, Pm3Error, pm3Exec } from "../lib/pm3.js";
|
|
6
6
|
export async function doctor() {
|
|
7
|
-
|
|
7
|
+
p.intro("Proxmark3 Health Check");
|
|
8
8
|
// 1. Port detection
|
|
9
9
|
const port = await detectPort();
|
|
10
10
|
if (port) {
|
|
11
|
-
|
|
11
|
+
p.log.success(`Port: ${port}`);
|
|
12
12
|
}
|
|
13
13
|
else {
|
|
14
|
-
|
|
14
|
+
p.log.error("No Proxmark3 detected. Check USB connection.");
|
|
15
15
|
return false;
|
|
16
16
|
}
|
|
17
17
|
// 2. Communication check
|
|
18
|
-
const
|
|
18
|
+
const s = p.spinner();
|
|
19
|
+
s.start("Checking device communication...");
|
|
19
20
|
try {
|
|
20
21
|
const { stdout } = await pm3Exec("hw status");
|
|
21
22
|
const status = parseHwStatus(stdout);
|
|
22
23
|
if (status.isStockFirmware) {
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
s.stop("Stock firmware detected.");
|
|
25
|
+
p.log.warn("Stock firmware detected. Run `keyfabe setup` to flash Iceman firmware.");
|
|
25
26
|
return false;
|
|
26
27
|
}
|
|
27
28
|
if (!status.connected) {
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
s.stop("Cannot communicate with device.");
|
|
30
|
+
p.log.warn("Cannot communicate with device. Check connection and try `keyfabe setup`.");
|
|
30
31
|
return false;
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
s.stop("Device communication OK");
|
|
34
|
+
p.log.success(`Firmware: ${status.firmwareVersion}`);
|
|
34
35
|
}
|
|
35
36
|
catch (err) {
|
|
36
37
|
if (err instanceof Pm3Error) {
|
|
37
38
|
if (err.message.includes("not found")) {
|
|
38
|
-
|
|
39
|
+
s.stop("pm3 command not found.");
|
|
39
40
|
printBrewInstall();
|
|
40
41
|
}
|
|
41
42
|
else {
|
|
42
|
-
|
|
43
|
+
s.stop(err.message);
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
else {
|
|
46
|
-
|
|
47
|
+
s.stop("Failed to communicate with device.");
|
|
47
48
|
}
|
|
48
49
|
return false;
|
|
49
50
|
}
|
|
50
51
|
// 3. Antenna tuning
|
|
51
|
-
const tuneSpinner =
|
|
52
|
+
const tuneSpinner = p.spinner();
|
|
53
|
+
tuneSpinner.start("Checking antenna tuning...");
|
|
52
54
|
try {
|
|
53
55
|
const { stdout } = await pm3Exec("hw tune");
|
|
54
56
|
const tune = parseHwTune(stdout);
|
|
55
|
-
tuneSpinner.
|
|
57
|
+
tuneSpinner.stop("Antenna tuning complete");
|
|
56
58
|
const lfColor = tune.lfVoltage >= LF_VOLTAGE_THRESHOLD ? chalk.green : chalk.red;
|
|
57
59
|
const hfColor = tune.hfVoltage >= HF_VOLTAGE_THRESHOLD ? chalk.green : chalk.red;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
const voltageLines = [
|
|
61
|
+
lfColor(`LF antenna: ${tune.lfVoltage.toFixed(2)}V (125 kHz)`),
|
|
62
|
+
hfColor(`HF antenna: ${tune.hfVoltage.toFixed(2)}V (13.56 MHz)`),
|
|
63
|
+
];
|
|
64
|
+
p.note(voltageLines.join("\n"), "Antenna Tuning");
|
|
60
65
|
if (tune.lfVoltage < LF_VOLTAGE_THRESHOLD) {
|
|
61
|
-
|
|
66
|
+
p.log.warn("LF antenna reading low. Check antenna connection.");
|
|
62
67
|
}
|
|
63
68
|
if (tune.hfVoltage < HF_VOLTAGE_THRESHOLD) {
|
|
64
|
-
|
|
69
|
+
p.log.warn("HF antenna reading low. Check antenna connection.");
|
|
65
70
|
}
|
|
66
71
|
}
|
|
67
72
|
catch (err) {
|
|
68
73
|
if (err instanceof Pm3Error) {
|
|
69
|
-
tuneSpinner.
|
|
74
|
+
tuneSpinner.stop(err.message);
|
|
70
75
|
}
|
|
71
76
|
else {
|
|
72
|
-
tuneSpinner.
|
|
77
|
+
tuneSpinner.stop("Failed to check antenna tuning.");
|
|
73
78
|
}
|
|
74
79
|
}
|
|
75
|
-
|
|
80
|
+
p.outro("Device is healthy!");
|
|
76
81
|
return true;
|
|
77
82
|
}
|
|
78
83
|
//# sourceMappingURL=doctor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/export.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
1
|
import { loadFobs } from "../lib/store.js";
|
|
3
2
|
export async function exportFobs() {
|
|
4
3
|
const fobs = await loadFobs();
|
|
5
4
|
if (fobs.length === 0) {
|
|
6
|
-
console.error(
|
|
5
|
+
console.error("No saved fobs to export.");
|
|
7
6
|
return false;
|
|
8
7
|
}
|
|
9
8
|
// Output clean JSON to stdout for piping
|
|
10
9
|
process.stdout.write(`${JSON.stringify(fobs, null, 2)}\n`);
|
|
11
|
-
console.error(
|
|
10
|
+
console.error(`Exported ${fobs.length} fob(s).`);
|
|
12
11
|
return true;
|
|
13
12
|
}
|
|
14
13
|
//# sourceMappingURL=export.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,UAAU;IAC5B,MAAM,IAAI,GAAG,MAAM,QAAQ,EAAE,CAAC;IAE9B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC1C,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,yCAAyC;IACzC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3D,OAAO,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,MAAM,UAAU,CAAC,CAAC;IACjD,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
package/dist/commands/import.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
|
-
import
|
|
2
|
+
import * as p from "@clack/prompts";
|
|
3
3
|
import { importFobs } from "../lib/store.js";
|
|
4
4
|
function validateFobs(data) {
|
|
5
5
|
if (!Array.isArray(data))
|
|
@@ -17,7 +17,7 @@ export async function importFile(filePath) {
|
|
|
17
17
|
raw = await readFile(filePath, "utf-8");
|
|
18
18
|
}
|
|
19
19
|
catch {
|
|
20
|
-
|
|
20
|
+
p.log.error(`Cannot read file: ${filePath}`);
|
|
21
21
|
return false;
|
|
22
22
|
}
|
|
23
23
|
let data;
|
|
@@ -25,15 +25,15 @@ export async function importFile(filePath) {
|
|
|
25
25
|
data = JSON.parse(raw);
|
|
26
26
|
}
|
|
27
27
|
catch {
|
|
28
|
-
|
|
28
|
+
p.log.error("Invalid JSON.");
|
|
29
29
|
return false;
|
|
30
30
|
}
|
|
31
31
|
if (!validateFobs(data)) {
|
|
32
|
-
|
|
32
|
+
p.log.error("Invalid format. Expected an array of fob objects with name, type, id, and savedAt.");
|
|
33
33
|
return false;
|
|
34
34
|
}
|
|
35
35
|
const { added, updated } = await importFobs(data);
|
|
36
|
-
|
|
36
|
+
p.log.success(`Imported ${added} new, updated ${updated} existing.`);
|
|
37
37
|
return true;
|
|
38
38
|
}
|
|
39
39
|
//# sourceMappingURL=import.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/commands/import.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/commands/import.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AAEpC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,SAAS,YAAY,CAAC,IAAa;IAC/B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,OAAO,IAAI,CAAC,KAAK,CACb,CAAC,IAAI,EAAE,EAAE,CACL,OAAO,IAAI,KAAK,QAAQ;QACxB,IAAI,KAAK,IAAI;QACb,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;QAC7B,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;QAC7B,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ;QAC3B,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CACvC,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC7C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACD,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACL,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,qBAAqB,QAAQ,EAAE,CAAC,CAAC;QAC7C,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACL,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAC7B,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,oFAAoF,CAAC,CAAC;QAClG,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,iBAAiB,OAAO,YAAY,CAAC,CAAC;IACrE,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
package/dist/commands/list.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
2
|
import { loadFobs } from "../lib/store.js";
|
|
3
3
|
export async function list(options = {}) {
|
|
4
4
|
const fobs = await loadFobs();
|
|
@@ -7,7 +7,7 @@ export async function list(options = {}) {
|
|
|
7
7
|
return true;
|
|
8
8
|
}
|
|
9
9
|
if (fobs.length === 0) {
|
|
10
|
-
|
|
10
|
+
p.log.info("No saved fobs. Use `keyfabe read` or `keyfabe clone` to save one.");
|
|
11
11
|
return true;
|
|
12
12
|
}
|
|
13
13
|
const hasEncoding = fobs.some((f) => f.encoding);
|
|
@@ -17,13 +17,12 @@ export async function list(options = {}) {
|
|
|
17
17
|
id: Math.max(16, ...fobs.map((f) => f.id.length + 2)),
|
|
18
18
|
encoding: hasEncoding ? Math.max(12, ...fobs.map((f) => (f.encoding ?? "").length + 2)) : 0,
|
|
19
19
|
};
|
|
20
|
-
|
|
21
|
-
let header = chalk.bold("Name".padEnd(cols.name)) + chalk.bold("Type".padEnd(cols.type)) + chalk.bold("ID".padEnd(cols.id));
|
|
20
|
+
let header = "Name".padEnd(cols.name) + "Type".padEnd(cols.type) + "ID".padEnd(cols.id);
|
|
22
21
|
if (hasEncoding) {
|
|
23
|
-
header +=
|
|
22
|
+
header += "Encoding".padEnd(cols.encoding);
|
|
24
23
|
}
|
|
25
|
-
header +=
|
|
26
|
-
|
|
24
|
+
header += "Saved";
|
|
25
|
+
const rows = [];
|
|
27
26
|
for (const fob of fobs) {
|
|
28
27
|
const date = fob.savedAt.slice(0, 10);
|
|
29
28
|
let row = fob.name.padEnd(cols.name) + fob.type.padEnd(cols.type) + fob.id.padEnd(cols.id);
|
|
@@ -31,9 +30,9 @@ export async function list(options = {}) {
|
|
|
31
30
|
row += (fob.encoding ?? "").padEnd(cols.encoding);
|
|
32
31
|
}
|
|
33
32
|
row += date;
|
|
34
|
-
|
|
33
|
+
rows.push(row);
|
|
35
34
|
}
|
|
36
|
-
|
|
35
|
+
p.note(`${header}\n${rows.join("\n")}`, `Saved Fobs (${fobs.length})`);
|
|
37
36
|
return true;
|
|
38
37
|
}
|
|
39
38
|
//# sourceMappingURL=list.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/read.js
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
import ora from "ora";
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
3
2
|
import { printCardInfo, printDoctorHint } from "../lib/display.js";
|
|
4
|
-
import { parseLfSearch } from "../lib/parsers.js";
|
|
3
|
+
import { parseHfSearch, parseLfSearch } from "../lib/parsers.js";
|
|
5
4
|
import { Pm3Error, pm3Exec, requireDevice } from "../lib/pm3.js";
|
|
6
5
|
import { promptName } from "../lib/prompts.js";
|
|
7
6
|
import { saveFob } from "../lib/store.js";
|
|
8
7
|
export async function read() {
|
|
9
8
|
if (!(await requireDevice()))
|
|
10
9
|
return false;
|
|
10
|
+
p.intro("Read Card");
|
|
11
11
|
let card = null;
|
|
12
|
-
const
|
|
12
|
+
const s = p.spinner();
|
|
13
|
+
s.start("Searching for card (LF)...");
|
|
13
14
|
try {
|
|
14
15
|
const { stdout } = await pm3Exec("lf search");
|
|
15
16
|
card = parseLfSearch(stdout);
|
|
16
17
|
}
|
|
17
18
|
catch (err) {
|
|
18
19
|
if (err instanceof Pm3Error) {
|
|
19
|
-
|
|
20
|
+
s.stop(err.message);
|
|
21
|
+
p.log.error(err.message);
|
|
20
22
|
if (err.message.includes("not found")) {
|
|
21
23
|
printDoctorHint();
|
|
22
24
|
}
|
|
@@ -24,10 +26,33 @@ export async function read() {
|
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
if (!card) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
s.stop("No LF card found, trying HF...");
|
|
30
|
+
const hf = p.spinner();
|
|
31
|
+
hf.start("Searching for card (HF)...");
|
|
32
|
+
try {
|
|
33
|
+
const { stdout } = await pm3Exec("hf search");
|
|
34
|
+
card = parseHfSearch(stdout);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
if (err instanceof Pm3Error) {
|
|
38
|
+
hf.stop(err.message);
|
|
39
|
+
p.log.error(err.message);
|
|
40
|
+
if (err.message.includes("not found")) {
|
|
41
|
+
printDoctorHint();
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!card) {
|
|
47
|
+
hf.stop("No card detected. Make sure the card is flat against the antenna.");
|
|
48
|
+
p.log.error("No card detected. Make sure the card is flat against the antenna.");
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
hf.stop("Card detected");
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
s.stop("Card detected");
|
|
29
55
|
}
|
|
30
|
-
spinner.succeed("Card detected");
|
|
31
56
|
printCardInfo(card);
|
|
32
57
|
// Prompt to save
|
|
33
58
|
const name = await promptName();
|
|
@@ -39,7 +64,7 @@ export async function read() {
|
|
|
39
64
|
encoding: card.encoding,
|
|
40
65
|
savedAt: new Date().toISOString(),
|
|
41
66
|
});
|
|
42
|
-
|
|
67
|
+
p.log.success(`Saved as "${name}".`);
|
|
43
68
|
}
|
|
44
69
|
return true;
|
|
45
70
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/commands/read.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/commands/read.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function rename(oldName
|
|
1
|
+
export declare function rename(oldName?: string, newName?: string): Promise<boolean>;
|