bsv-bap 0.2.0-alpha.2 → 0.2.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 +276 -52
- package/dist/MasterID.d.ts +2 -1
- package/dist/constants.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.modern.js +2 -2
- package/dist/index.modern.js.map +5 -5
- package/dist/index.module.js +2 -2
- package/dist/index.module.js.map +5 -5
- package/dist/interface.d.ts +4 -0
- package/dist/poa.d.ts +1 -1
- package/package.json +7 -6
- package/src/cli.ts +328 -323
package/src/cli.ts
CHANGED
|
@@ -1,268 +1,282 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
* BAP CLI - Bitcoin Attestation Protocol Command Line Interface
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* bap create [--name <name>] [--wif <wif>] Create new BAP identity
|
|
7
|
-
* bap sign <message> Sign a message
|
|
8
|
-
* bap verify <message> <sig> <address> Verify a signature
|
|
9
|
-
* bap friend-pubkey <friendBapId> Get friend public key
|
|
10
|
-
* bap encrypt <data> <friendBapId> Encrypt for friend
|
|
11
|
-
* bap decrypt <ciphertext> <friendBapId> Decrypt from friend
|
|
12
|
-
* bap export Export identity backup
|
|
13
|
-
* bap import <backup> Import identity from backup
|
|
14
|
-
* bap info Show current identity info
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
18
3
|
import { homedir } from "node:os";
|
|
19
4
|
import { join } from "node:path";
|
|
20
|
-
import { PrivateKey
|
|
21
|
-
import {
|
|
5
|
+
import { PrivateKey } from "@bsv/sdk";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { BAP, bapIdFromAddress, bapIdFromPubkey } from "bsv-bap";
|
|
22
8
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// Default config path
|
|
9
|
+
// Storage paths
|
|
26
10
|
const CONFIG_DIR = join(homedir(), ".bap");
|
|
27
11
|
const CONFIG_FILE = join(CONFIG_DIR, "identity.json");
|
|
12
|
+
const ACTIVE_FILE = join(CONFIG_DIR, "active");
|
|
28
13
|
|
|
29
|
-
|
|
30
|
-
|
|
14
|
+
// Stored config shape
|
|
15
|
+
interface StoredConfig {
|
|
16
|
+
rootPk: string;
|
|
31
17
|
ids: string;
|
|
32
|
-
|
|
18
|
+
labels: Record<string, string>;
|
|
33
19
|
createdAt: string;
|
|
34
20
|
}
|
|
35
21
|
|
|
36
22
|
function ensureConfigDir(): void {
|
|
37
|
-
const { mkdirSync } = require("node:fs");
|
|
38
23
|
if (!existsSync(CONFIG_DIR)) {
|
|
39
24
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
40
25
|
}
|
|
41
26
|
}
|
|
42
27
|
|
|
43
|
-
function
|
|
44
|
-
if (!existsSync(CONFIG_FILE))
|
|
45
|
-
|
|
28
|
+
function loadConfig(): StoredConfig | null {
|
|
29
|
+
if (!existsSync(CONFIG_FILE)) return null;
|
|
30
|
+
return JSON.parse(readFileSync(CONFIG_FILE, "utf-8")) as StoredConfig;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createBAP(key: string): BAP {
|
|
34
|
+
// xprv keys start with "xprv" — use BIP32 mode; otherwise Type42
|
|
35
|
+
if (key.startsWith("xprv")) {
|
|
36
|
+
return new BAP(key);
|
|
46
37
|
}
|
|
38
|
+
return new BAP({ rootPk: key });
|
|
39
|
+
}
|
|
47
40
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return null;
|
|
41
|
+
function loadBAP(): { bap: BAP; config: StoredConfig } {
|
|
42
|
+
const config = loadConfig();
|
|
43
|
+
if (!config) {
|
|
44
|
+
console.error("No identity found. Run 'bap create' first.");
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
const bap = createBAP(config.rootPk);
|
|
48
|
+
if (config.ids) {
|
|
49
|
+
bap.importIds(config.ids);
|
|
58
50
|
}
|
|
51
|
+
return { bap, config };
|
|
59
52
|
}
|
|
60
53
|
|
|
61
|
-
function
|
|
54
|
+
function saveConfig(
|
|
55
|
+
bap: BAP,
|
|
56
|
+
rootPk: string,
|
|
57
|
+
labels: Record<string, string>,
|
|
58
|
+
createdAt?: string,
|
|
59
|
+
): void {
|
|
62
60
|
ensureConfigDir();
|
|
63
|
-
const
|
|
64
|
-
|
|
61
|
+
const config: StoredConfig = {
|
|
62
|
+
rootPk,
|
|
65
63
|
ids: bap.exportIds(),
|
|
66
|
-
|
|
67
|
-
createdAt: new Date().toISOString(),
|
|
64
|
+
labels,
|
|
65
|
+
createdAt: createdAt ?? new Date().toISOString(),
|
|
68
66
|
};
|
|
69
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(
|
|
67
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
70
68
|
}
|
|
71
69
|
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
Commands:
|
|
77
|
-
create [--name <name>] [--wif <wif>] Create new BAP identity
|
|
78
|
-
sign <message> Sign a message with identity
|
|
79
|
-
verify <message> <sig> <address> Verify a BSM signature
|
|
80
|
-
friend-pubkey <friendBapId> Get encryption pubkey for friend
|
|
81
|
-
encrypt <data> <friendBapId> Encrypt data for friend
|
|
82
|
-
decrypt <ciphertext> <friendBapId> Decrypt data from friend
|
|
83
|
-
export Export identity backup (JSON)
|
|
84
|
-
import <file> Import identity from backup file
|
|
85
|
-
info Show current identity info
|
|
86
|
-
help Show this help message
|
|
87
|
-
|
|
88
|
-
Options:
|
|
89
|
-
--name <name> Identity name (for create)
|
|
90
|
-
--wif <wif> Use existing WIF key (for create)
|
|
91
|
-
|
|
92
|
-
Examples:
|
|
93
|
-
bap create --name "My Identity"
|
|
94
|
-
bap sign "Hello World"
|
|
95
|
-
bap verify "Hello World" <signature> <address>
|
|
96
|
-
bap friend-pubkey "abc123..."
|
|
97
|
-
bap encrypt "secret message" "abc123..."
|
|
98
|
-
bap decrypt "<ciphertext>" "abc123..."
|
|
99
|
-
bap export > backup.json
|
|
100
|
-
bap import backup.json
|
|
101
|
-
`);
|
|
70
|
+
function getActiveBapId(): string | null {
|
|
71
|
+
if (!existsSync(ACTIVE_FILE)) return null;
|
|
72
|
+
return readFileSync(ACTIVE_FILE, "utf-8").trim();
|
|
102
73
|
}
|
|
103
74
|
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// Parse arguments
|
|
109
|
-
for (let i = 0; i < args.length; i++) {
|
|
110
|
-
if (args[i] === "--name" && args[i + 1]) {
|
|
111
|
-
name = args[i + 1];
|
|
112
|
-
i++;
|
|
113
|
-
} else if (args[i] === "--wif" && args[i + 1]) {
|
|
114
|
-
wif = args[i + 1];
|
|
115
|
-
i++;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Generate or use provided WIF
|
|
120
|
-
if (!wif) {
|
|
121
|
-
const privateKey = PrivateKey.fromRandom();
|
|
122
|
-
wif = privateKey.toWif();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Create BAP instance with Type42
|
|
126
|
-
const bap = new BAP({ rootPk: wif });
|
|
127
|
-
const identity = bap.newId(name);
|
|
128
|
-
|
|
129
|
-
// Save to config
|
|
130
|
-
saveIdentity(bap, wif);
|
|
131
|
-
|
|
132
|
-
console.log("Identity created successfully!");
|
|
133
|
-
console.log(` Name: ${name}`);
|
|
134
|
-
console.log(` Identity Key: ${identity.getIdentityKey()}`);
|
|
135
|
-
console.log(` Root Address: ${identity.rootAddress}`);
|
|
136
|
-
console.log(` Signing Address: ${identity.getCurrentAddress()}`);
|
|
137
|
-
console.log(`\nStored at: ${CONFIG_FILE}`);
|
|
75
|
+
function setActiveBapId(bapId: string): void {
|
|
76
|
+
ensureConfigDir();
|
|
77
|
+
writeFileSync(ACTIVE_FILE, bapId);
|
|
138
78
|
}
|
|
139
79
|
|
|
140
|
-
function
|
|
141
|
-
const
|
|
142
|
-
if (!bap) {
|
|
143
|
-
console.error("No identity found. Run 'bap create' first.");
|
|
144
|
-
process.exit(1);
|
|
145
|
-
}
|
|
146
|
-
|
|
80
|
+
function getActiveIdentity(bap: BAP, config: StoredConfig) {
|
|
81
|
+
const activeBapId = getActiveBapId();
|
|
147
82
|
const ids = bap.listIds();
|
|
83
|
+
|
|
148
84
|
if (ids.length === 0) {
|
|
149
|
-
console.error("No identities
|
|
85
|
+
console.error("No identities found. Run 'bap create' first.");
|
|
150
86
|
process.exit(1);
|
|
151
87
|
}
|
|
152
88
|
|
|
153
|
-
const
|
|
89
|
+
const bapId = activeBapId && ids.includes(activeBapId) ? activeBapId : ids[0];
|
|
90
|
+
const identity = bap.getId(bapId);
|
|
154
91
|
if (!identity) {
|
|
155
|
-
console.error(
|
|
92
|
+
console.error(`Identity ${bapId} not found.`);
|
|
156
93
|
process.exit(1);
|
|
157
94
|
}
|
|
158
|
-
|
|
159
|
-
const { address, signature } = identity.signMessage(toArray(message, "utf8"));
|
|
160
|
-
|
|
161
|
-
console.log(JSON.stringify({ message, address, signature }, null, 2));
|
|
95
|
+
return { identity, bapId, label: config.labels[bapId] };
|
|
162
96
|
}
|
|
163
97
|
|
|
164
|
-
|
|
165
|
-
const bap = new BAP({ rootPk: PrivateKey.fromRandom().toWif() }); // Temporary instance
|
|
166
|
-
const isValid = bap.verifySignature(message, address, signature);
|
|
98
|
+
// --- CLI ---
|
|
167
99
|
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function getFriendPubkey(friendBapId: string): void {
|
|
172
|
-
const bap = loadIdentity();
|
|
173
|
-
if (!bap) {
|
|
174
|
-
console.error("No identity found. Run 'bap create' first.");
|
|
175
|
-
process.exit(1);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const ids = bap.listIds();
|
|
179
|
-
if (ids.length === 0) {
|
|
180
|
-
console.error("No identities in BAP instance.");
|
|
181
|
-
process.exit(1);
|
|
182
|
-
}
|
|
100
|
+
const program = new Command();
|
|
183
101
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
102
|
+
program
|
|
103
|
+
.name("bap")
|
|
104
|
+
.description("BAP - Bitcoin Attestation Protocol CLI")
|
|
105
|
+
.version("0.2.0");
|
|
189
106
|
|
|
190
|
-
|
|
191
|
-
console.log(JSON.stringify({ friendBapId, publicKey }, null, 2));
|
|
192
|
-
}
|
|
107
|
+
// Identity Management
|
|
193
108
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
109
|
+
program
|
|
110
|
+
.command("create")
|
|
111
|
+
.description("Create a new identity")
|
|
112
|
+
.option("--name <name>", "Human-readable label for the identity")
|
|
113
|
+
.option("--wif <wif>", "Use an existing WIF key as the master key")
|
|
114
|
+
.action((opts) => {
|
|
115
|
+
let config = loadConfig();
|
|
116
|
+
let rootPk: string;
|
|
117
|
+
let labels: Record<string, string>;
|
|
118
|
+
let createdAt: string | undefined;
|
|
119
|
+
let bap: BAP;
|
|
200
120
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
121
|
+
if (config) {
|
|
122
|
+
// Existing master — add a new identity
|
|
123
|
+
rootPk = config.rootPk;
|
|
124
|
+
labels = config.labels;
|
|
125
|
+
createdAt = config.createdAt;
|
|
126
|
+
bap = createBAP(rootPk);
|
|
127
|
+
bap.importIds(config.ids);
|
|
128
|
+
} else {
|
|
129
|
+
// New master
|
|
130
|
+
rootPk = opts.wif ?? PrivateKey.fromRandom().toWif();
|
|
131
|
+
labels = {};
|
|
132
|
+
bap = createBAP(rootPk);
|
|
133
|
+
}
|
|
206
134
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
console.error("Failed to get identity.");
|
|
210
|
-
process.exit(1);
|
|
211
|
-
}
|
|
135
|
+
const identity = bap.newId();
|
|
136
|
+
const bapId = identity.bapId;
|
|
212
137
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
138
|
+
if (opts.name) {
|
|
139
|
+
labels[bapId] = opts.name;
|
|
140
|
+
}
|
|
216
141
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
console.
|
|
221
|
-
|
|
222
|
-
|
|
142
|
+
saveConfig(bap, rootPk, labels, createdAt);
|
|
143
|
+
setActiveBapId(bapId);
|
|
144
|
+
|
|
145
|
+
console.log("Identity created:");
|
|
146
|
+
console.log(` BAP ID: ${bapId}`);
|
|
147
|
+
if (opts.name) console.log(` Label: ${opts.name}`);
|
|
148
|
+
console.log(` Root Address: ${identity.rootAddress}`);
|
|
149
|
+
console.log(` Root Path: ${identity.rootPath}`);
|
|
150
|
+
console.log(` Stored at: ${CONFIG_FILE}`);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
program
|
|
154
|
+
.command("list")
|
|
155
|
+
.description("List all identities (* = active)")
|
|
156
|
+
.action(() => {
|
|
157
|
+
const { bap, config } = loadBAP();
|
|
158
|
+
const ids = bap.listIds();
|
|
159
|
+
const active = getActiveBapId();
|
|
223
160
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
161
|
+
if (ids.length === 0) {
|
|
162
|
+
console.log("No identities. Run 'bap create' to get started.");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
229
165
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
166
|
+
for (const bapId of ids) {
|
|
167
|
+
const marker = bapId === active ? " *" : " ";
|
|
168
|
+
const label = config.labels[bapId];
|
|
169
|
+
const suffix = label ? ` (${label})` : "";
|
|
170
|
+
console.log(`${marker} ${bapId}${suffix}`);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
program
|
|
175
|
+
.command("use")
|
|
176
|
+
.description("Set active identity")
|
|
177
|
+
.argument("<bapId>", "BAP ID to activate")
|
|
178
|
+
.action((bapId: string) => {
|
|
179
|
+
const { bap } = loadBAP();
|
|
180
|
+
const ids = bap.listIds();
|
|
235
181
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
console.error("Decryption failed:", error instanceof Error ? error.message : error);
|
|
241
|
-
process.exit(1);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
182
|
+
if (!ids.includes(bapId)) {
|
|
183
|
+
console.error(`Identity ${bapId} not found.`);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
244
186
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
187
|
+
setActiveBapId(bapId);
|
|
188
|
+
console.log(`Active identity: ${bapId}`);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
program
|
|
192
|
+
.command("info")
|
|
193
|
+
.description("Show active identity details")
|
|
194
|
+
.action(() => {
|
|
195
|
+
const { bap, config } = loadBAP();
|
|
196
|
+
const { identity, bapId, label } = getActiveIdentity(bap, config);
|
|
197
|
+
|
|
198
|
+
console.log("Active Identity:");
|
|
199
|
+
console.log(` BAP ID: ${bapId}`);
|
|
200
|
+
if (label) console.log(` Label: ${label}`);
|
|
201
|
+
console.log(` Root Address: ${identity.rootAddress}`);
|
|
202
|
+
console.log(` Root Path: ${identity.rootPath}`);
|
|
203
|
+
console.log(` Current Path: ${identity.currentPath}`);
|
|
204
|
+
console.log(` Previous Path: ${identity.previousPath}`);
|
|
205
|
+
console.log(` Account Key: ${identity.getAccountKey().toPublicKey().toString()}`);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
program
|
|
209
|
+
.command("remove")
|
|
210
|
+
.description("Remove an identity")
|
|
211
|
+
.argument("<bapId>", "BAP ID to remove")
|
|
212
|
+
.action((bapId: string) => {
|
|
213
|
+
const { bap, config } = loadBAP();
|
|
214
|
+
|
|
215
|
+
if (!bap.getId(bapId)) {
|
|
216
|
+
console.error(`Identity ${bapId} not found.`);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
251
219
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
220
|
+
bap.removeId(bapId);
|
|
221
|
+
delete config.labels[bapId];
|
|
222
|
+
saveConfig(bap, config.rootPk, config.labels, config.createdAt);
|
|
223
|
+
|
|
224
|
+
// Clear active if it was this one
|
|
225
|
+
if (getActiveBapId() === bapId) {
|
|
226
|
+
const remaining = bap.listIds();
|
|
227
|
+
if (remaining.length > 0) {
|
|
228
|
+
setActiveBapId(remaining[0]);
|
|
229
|
+
} else {
|
|
230
|
+
writeFileSync(ACTIVE_FILE, "");
|
|
231
|
+
}
|
|
232
|
+
}
|
|
255
233
|
|
|
256
|
-
|
|
257
|
-
}
|
|
234
|
+
console.log(`Removed identity: ${bapId}`);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Backup
|
|
238
|
+
|
|
239
|
+
program
|
|
240
|
+
.command("export")
|
|
241
|
+
.description("Export master backup (JSON to stdout)")
|
|
242
|
+
.action(() => {
|
|
243
|
+
const { bap } = loadBAP();
|
|
244
|
+
const backup = bap.exportForBackup();
|
|
245
|
+
console.log(JSON.stringify(backup, null, 2));
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
program
|
|
249
|
+
.command("export-account")
|
|
250
|
+
.description("Export account backup for active or specified identity")
|
|
251
|
+
.option("--id <bapId>", "Specific BAP ID to export")
|
|
252
|
+
.action((opts) => {
|
|
253
|
+
const { bap, config } = loadBAP();
|
|
254
|
+
|
|
255
|
+
let identity;
|
|
256
|
+
if (opts.id) {
|
|
257
|
+
identity = bap.getId(opts.id);
|
|
258
|
+
if (!identity) {
|
|
259
|
+
console.error(`Identity ${opts.id} not found.`);
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
({ identity } = getActiveIdentity(bap, config));
|
|
264
|
+
}
|
|
258
265
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
266
|
+
const backup = identity.exportAccountBackup();
|
|
267
|
+
console.log(JSON.stringify(backup, null, 2));
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
program
|
|
271
|
+
.command("import")
|
|
272
|
+
.description("Import from backup file")
|
|
273
|
+
.argument("<file>", "Path to backup JSON file")
|
|
274
|
+
.action((file: string) => {
|
|
275
|
+
if (!existsSync(file)) {
|
|
276
|
+
console.error(`File not found: ${file}`);
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
264
279
|
|
|
265
|
-
try {
|
|
266
280
|
const backup = JSON.parse(readFileSync(file, "utf-8"));
|
|
267
281
|
|
|
268
282
|
if (!backup.rootPk && !backup.xprv) {
|
|
@@ -271,136 +285,127 @@ function importIdentity(file: string): void {
|
|
|
271
285
|
}
|
|
272
286
|
|
|
273
287
|
let bap: BAP;
|
|
274
|
-
let
|
|
288
|
+
let rootPk: string;
|
|
275
289
|
|
|
276
290
|
if (backup.rootPk) {
|
|
277
|
-
// Type42 format
|
|
278
291
|
bap = new BAP({ rootPk: backup.rootPk });
|
|
279
|
-
|
|
292
|
+
rootPk = backup.rootPk;
|
|
280
293
|
} else {
|
|
281
|
-
// BIP32 format (legacy)
|
|
282
294
|
bap = new BAP(backup.xprv);
|
|
283
|
-
|
|
295
|
+
rootPk = backup.xprv;
|
|
284
296
|
}
|
|
285
297
|
|
|
286
298
|
if (backup.ids) {
|
|
287
299
|
bap.importIds(backup.ids);
|
|
288
300
|
}
|
|
289
301
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const ids = bap.listIds();
|
|
293
|
-
console.log("Identity imported successfully!");
|
|
294
|
-
console.log(` Identities: ${ids.length}`);
|
|
302
|
+
const labels: Record<string, string> = {};
|
|
295
303
|
if (backup.label) {
|
|
296
|
-
|
|
304
|
+
// Apply label to first identity as a default
|
|
305
|
+
const ids = bap.listIds();
|
|
306
|
+
if (ids.length > 0) {
|
|
307
|
+
labels[ids[0]] = backup.label;
|
|
308
|
+
}
|
|
297
309
|
}
|
|
298
|
-
console.log(`\nStored at: ${CONFIG_FILE}`);
|
|
299
|
-
} catch (error) {
|
|
300
|
-
console.error("Failed to import identity:", error);
|
|
301
|
-
process.exit(1);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
310
|
|
|
305
|
-
|
|
306
|
-
const bap = loadIdentity();
|
|
307
|
-
if (!bap) {
|
|
308
|
-
console.error("No identity found. Run 'bap create' first.");
|
|
309
|
-
process.exit(1);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const ids = bap.listIds();
|
|
313
|
-
console.log("BAP Identity Info");
|
|
314
|
-
console.log(` Config: ${CONFIG_FILE}`);
|
|
315
|
-
console.log(` Identities: ${ids.length}`);
|
|
316
|
-
|
|
317
|
-
for (const idKey of ids) {
|
|
318
|
-
const identity = bap.getId(idKey);
|
|
319
|
-
if (identity) {
|
|
320
|
-
console.log(`\n Identity: ${identity.idName}`);
|
|
321
|
-
console.log(` Key: ${idKey}`);
|
|
322
|
-
console.log(` Root Address: ${identity.rootAddress}`);
|
|
323
|
-
console.log(` Current Address: ${identity.getCurrentAddress()}`);
|
|
324
|
-
console.log(` Encryption Pubkey: ${identity.getEncryptionPublicKey()}`);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
311
|
+
saveConfig(bap, rootPk, labels);
|
|
328
312
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
switch (command) {
|
|
334
|
-
case "create":
|
|
335
|
-
createIdentity(args.slice(1));
|
|
336
|
-
break;
|
|
337
|
-
|
|
338
|
-
case "sign":
|
|
339
|
-
if (!args[1]) {
|
|
340
|
-
console.error("Usage: bap sign <message>");
|
|
341
|
-
process.exit(1);
|
|
342
|
-
}
|
|
343
|
-
signMessage(args[1]);
|
|
344
|
-
break;
|
|
345
|
-
|
|
346
|
-
case "verify":
|
|
347
|
-
if (!args[1] || !args[2] || !args[3]) {
|
|
348
|
-
console.error("Usage: bap verify <message> <signature> <address>");
|
|
349
|
-
process.exit(1);
|
|
350
|
-
}
|
|
351
|
-
verifySignature(args[1], args[2], args[3]);
|
|
352
|
-
break;
|
|
353
|
-
|
|
354
|
-
case "friend-pubkey":
|
|
355
|
-
if (!args[1]) {
|
|
356
|
-
console.error("Usage: bap friend-pubkey <friendBapId>");
|
|
357
|
-
process.exit(1);
|
|
358
|
-
}
|
|
359
|
-
getFriendPubkey(args[1]);
|
|
360
|
-
break;
|
|
361
|
-
|
|
362
|
-
case "encrypt":
|
|
363
|
-
if (!args[1] || !args[2]) {
|
|
364
|
-
console.error("Usage: bap encrypt <data> <friendBapId>");
|
|
365
|
-
process.exit(1);
|
|
366
|
-
}
|
|
367
|
-
encryptForFriend(args[1], args[2]);
|
|
368
|
-
break;
|
|
369
|
-
|
|
370
|
-
case "decrypt":
|
|
371
|
-
if (!args[1] || !args[2]) {
|
|
372
|
-
console.error("Usage: bap decrypt <ciphertext> <friendBapId>");
|
|
373
|
-
process.exit(1);
|
|
313
|
+
const ids = bap.listIds();
|
|
314
|
+
if (ids.length > 0) {
|
|
315
|
+
setActiveBapId(ids[0]);
|
|
374
316
|
}
|
|
375
|
-
decryptFromFriend(args[1], args[2]);
|
|
376
|
-
break;
|
|
377
317
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
318
|
+
console.log("Backup imported:");
|
|
319
|
+
console.log(` Identities: ${ids.length}`);
|
|
320
|
+
if (backup.label) console.log(` Label: ${backup.label}`);
|
|
321
|
+
console.log(` Stored at: ${CONFIG_FILE}`);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Crypto
|
|
325
|
+
|
|
326
|
+
program
|
|
327
|
+
.command("encrypt")
|
|
328
|
+
.description("Encrypt data with master key (ECIES)")
|
|
329
|
+
.argument("<data>", "Data to encrypt")
|
|
330
|
+
.action((data: string) => {
|
|
331
|
+
const { bap } = loadBAP();
|
|
332
|
+
console.log(bap.encrypt(data));
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
program
|
|
336
|
+
.command("decrypt")
|
|
337
|
+
.description("Decrypt ciphertext with master key")
|
|
338
|
+
.argument("<ciphertext>", "Base64 ciphertext to decrypt")
|
|
339
|
+
.action((ciphertext: string) => {
|
|
340
|
+
const { bap } = loadBAP();
|
|
341
|
+
console.log(bap.decrypt(ciphertext));
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
program
|
|
345
|
+
.command("verify")
|
|
346
|
+
.description("Verify a BSM signature")
|
|
347
|
+
.argument("<message>", "Original message")
|
|
348
|
+
.argument("<signature>", "Base64 signature")
|
|
349
|
+
.argument("<address>", "Signing address")
|
|
350
|
+
.action((message: string, signature: string, address: string) => {
|
|
351
|
+
const bap = new BAP({ rootPk: PrivateKey.fromRandom().toWif() });
|
|
352
|
+
let valid = false;
|
|
353
|
+
try {
|
|
354
|
+
valid = bap.verifySignature(message, address, signature);
|
|
355
|
+
} catch {
|
|
356
|
+
// Invalid signature format — treat as not valid
|
|
403
357
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
358
|
+
console.log(JSON.stringify({ valid, message, address, signature }, null, 2));
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// API Lookups
|
|
362
|
+
|
|
363
|
+
program
|
|
364
|
+
.command("lookup")
|
|
365
|
+
.description("Lookup identity on the BAP overlay")
|
|
366
|
+
.argument("<bapId>", "BAP ID to lookup")
|
|
367
|
+
.action(async (bapId: string) => {
|
|
368
|
+
const bap = new BAP({ rootPk: PrivateKey.fromRandom().toWif() });
|
|
369
|
+
const result = await bap.getIdentity(bapId);
|
|
370
|
+
console.log(JSON.stringify(result, null, 2));
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
program
|
|
374
|
+
.command("lookup-address")
|
|
375
|
+
.description("Lookup identity by Bitcoin address")
|
|
376
|
+
.argument("<address>", "Bitcoin address to lookup")
|
|
377
|
+
.action(async (address: string) => {
|
|
378
|
+
const bap = new BAP({ rootPk: PrivateKey.fromRandom().toWif() });
|
|
379
|
+
const result = await bap.getIdentityFromAddress(address);
|
|
380
|
+
console.log(JSON.stringify(result, null, 2));
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
program
|
|
384
|
+
.command("attestations")
|
|
385
|
+
.description("Get attestations for an attribute hash")
|
|
386
|
+
.argument("<hash>", "Attribute hash to lookup")
|
|
387
|
+
.action(async (hash: string) => {
|
|
388
|
+
const bap = new BAP({ rootPk: PrivateKey.fromRandom().toWif() });
|
|
389
|
+
const result = await bap.getAttestationsForHash(hash);
|
|
390
|
+
console.log(JSON.stringify(result, null, 2));
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// Utilities
|
|
394
|
+
|
|
395
|
+
program
|
|
396
|
+
.command("id-from-address")
|
|
397
|
+
.description("Derive BAP ID from a Bitcoin address")
|
|
398
|
+
.argument("<address>", "Bitcoin address (must be the root/member address)")
|
|
399
|
+
.action((address: string) => {
|
|
400
|
+
console.log(bapIdFromAddress(address));
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
program
|
|
404
|
+
.command("id-from-pubkey")
|
|
405
|
+
.description("Derive BAP ID from a compressed public key")
|
|
406
|
+
.argument("<pubkey>", "Compressed public key hex (must be the member key)")
|
|
407
|
+
.action((pubkey: string) => {
|
|
408
|
+
console.log(bapIdFromPubkey(pubkey));
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
program.parse();
|