@bcts/seedtool-cli 1.0.0-alpha.14 → 1.0.0-alpha.16
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/LICENSE +1 -1
- package/README.md +72 -3
- package/dist/index.cjs +78 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +69 -62
- package/dist/index.mjs.map +1 -1
- package/dist/main.mjs +81 -71
- package/dist/main.mjs.map +1 -1
- package/package.json +21 -17
- package/src/cli.ts +25 -24
- package/src/formats/bip39.ts +1 -1
- package/src/formats/format.ts +32 -36
- package/src/formats/multipart.ts +1 -1
- package/src/formats/sskr.ts +41 -32
- package/src/main.ts +39 -16
- package/src/random.ts +3 -3
- package/src/seed.ts +4 -4
- package/src/styles.ts +1 -1
package/dist/main.mjs
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
import { Command, Option } from "commander";
|
|
3
3
|
import { SecureRandomNumberGenerator } from "@bcts/rand";
|
|
4
4
|
import { SSKRGroupSpec, SSKRSecret, SSKRShare, SSKRSpec, Seed, sskrCombine, sskrGenerate } from "@bcts/components";
|
|
5
|
-
import * as readline from "readline";
|
|
5
|
+
import * as readline from "node:readline";
|
|
6
6
|
import { Envelope, SymmetricKey } from "@bcts/envelope";
|
|
7
7
|
import { DATE, NAME, NOTE, SEED_TYPE } from "@bcts/known-values";
|
|
8
8
|
import { CborDate, decodeCbor, expectBytes, toByteString, toTaggedValue } from "@bcts/dcbor";
|
|
9
9
|
import { entropyToMnemonic, mnemonicToEntropy, validateMnemonic } from "@scure/bip39";
|
|
10
|
-
import { wordlist } from "@scure/bip39/wordlists/english";
|
|
10
|
+
import { wordlist } from "@scure/bip39/wordlists/english.js";
|
|
11
11
|
import { BytewordsStyle, MultipartDecoder, MultipartEncoder, UR, decodeBytewords, encodeBytewords } from "@bcts/uniform-resources";
|
|
12
12
|
import { SSKR_SHARE, SSKR_SHARE_V1 } from "@bcts/tags";
|
|
13
|
-
import { sha256 } from "@noble/hashes/
|
|
14
|
-
import { hkdf } from "@noble/hashes/hkdf";
|
|
15
|
-
import
|
|
13
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
14
|
+
import { hkdf } from "@noble/hashes/hkdf.js";
|
|
15
|
+
import fs from "node:fs";
|
|
16
16
|
|
|
17
17
|
//#region src/cli.ts
|
|
18
18
|
/**
|
|
@@ -137,7 +137,7 @@ var Cli = class Cli {
|
|
|
137
137
|
terminal: false
|
|
138
138
|
});
|
|
139
139
|
rl.on("line", (line) => {
|
|
140
|
-
data += line
|
|
140
|
+
data += `${line}\n`;
|
|
141
141
|
});
|
|
142
142
|
rl.on("close", () => {
|
|
143
143
|
resolve(data.trim());
|
|
@@ -194,23 +194,23 @@ var Cli = class Cli {
|
|
|
194
194
|
*/
|
|
195
195
|
clone() {
|
|
196
196
|
const cli = new Cli();
|
|
197
|
-
cli.input = this.input;
|
|
197
|
+
if (this.input !== void 0) cli.input = this.input;
|
|
198
198
|
cli.count = this.count;
|
|
199
199
|
cli.in = this.in;
|
|
200
200
|
cli.out = this.out;
|
|
201
201
|
cli.low = this.low;
|
|
202
202
|
cli.high = this.high;
|
|
203
|
-
cli.name = this.name;
|
|
204
|
-
cli.note = this.note;
|
|
205
|
-
cli.date = this.date;
|
|
203
|
+
if (this.name !== void 0) cli.name = this.name;
|
|
204
|
+
if (this.note !== void 0) cli.note = this.note;
|
|
205
|
+
if (this.date !== void 0) cli.date = this.date;
|
|
206
206
|
cli.maxFragmentLen = this.maxFragmentLen;
|
|
207
207
|
cli.additionalParts = this.additionalParts;
|
|
208
208
|
cli.groups = [...this.groups];
|
|
209
209
|
cli.groupThreshold = this.groupThreshold;
|
|
210
210
|
cli.sskrFormat = this.sskrFormat;
|
|
211
|
-
cli.deterministic = this.deterministic;
|
|
212
|
-
cli.seed = this.seed
|
|
213
|
-
cli.rng = this.rng;
|
|
211
|
+
if (this.deterministic !== void 0) cli.deterministic = this.deterministic;
|
|
212
|
+
if (this.seed !== void 0) cli.seed = this.seed.clone();
|
|
213
|
+
if (this.rng !== void 0) cli.rng = this.rng;
|
|
214
214
|
return cli;
|
|
215
215
|
}
|
|
216
216
|
};
|
|
@@ -332,7 +332,7 @@ var Seed$1 = class Seed$1 {
|
|
|
332
332
|
* - Optional note assertion (if not empty)
|
|
333
333
|
*/
|
|
334
334
|
toEnvelope() {
|
|
335
|
-
let envelope = Envelope.new(
|
|
335
|
+
let envelope = Envelope.new(this._data);
|
|
336
336
|
envelope = envelope.addType(SEED_TYPE);
|
|
337
337
|
if (this._creationDate !== void 0) {
|
|
338
338
|
const cborDate = CborDate.fromDatetime(this._creationDate);
|
|
@@ -580,6 +580,10 @@ var Bip39Format = class {
|
|
|
580
580
|
//#endregion
|
|
581
581
|
//#region src/formats/sskr.ts
|
|
582
582
|
/**
|
|
583
|
+
* SSKR format
|
|
584
|
+
* Ported from seedtool-cli-rust/src/formats/sskr.rs
|
|
585
|
+
*/
|
|
586
|
+
/**
|
|
583
587
|
* SSKR format handler.
|
|
584
588
|
* Round-trippable: sskr shares → seed → sskr shares.
|
|
585
589
|
* Supports multiple sub-formats: envelope, btw, btwm, btwu, ur.
|
|
@@ -604,16 +608,16 @@ var SSKRFormat = class {
|
|
|
604
608
|
};
|
|
605
609
|
function outputSskrSeed(seed, spec, format) {
|
|
606
610
|
switch (format) {
|
|
607
|
-
case
|
|
608
|
-
const
|
|
611
|
+
case SSKRFormatKey.Envelope: {
|
|
612
|
+
const seedEnvelope = seed.toEnvelope();
|
|
609
613
|
const contentKey = SymmetricKey.new();
|
|
610
|
-
return
|
|
614
|
+
return seedEnvelope.wrap().encryptSubject(contentKey).sskrSplitFlattened(spec, contentKey).map((env) => env.urString()).join("\n");
|
|
611
615
|
}
|
|
612
|
-
case
|
|
613
|
-
case
|
|
614
|
-
case
|
|
615
|
-
case
|
|
616
|
-
return UR.
|
|
616
|
+
case SSKRFormatKey.Btw: return makeBytewordsShares(spec, seed, BytewordsStyle.Standard);
|
|
617
|
+
case SSKRFormatKey.Btwm: return makeBytewordsShares(spec, seed, BytewordsStyle.Minimal);
|
|
618
|
+
case SSKRFormatKey.Btwu: return makeBytewordsShares(spec, seed, BytewordsStyle.Uri);
|
|
619
|
+
case SSKRFormatKey.Ur: return makeShares(spec, seed).map((share) => {
|
|
620
|
+
return UR.new("sskr", toByteString(share.asBytes())).toString();
|
|
617
621
|
}).join("\n");
|
|
618
622
|
}
|
|
619
623
|
}
|
|
@@ -635,7 +639,8 @@ function parseEnvelopes(input) {
|
|
|
635
639
|
shareEnvelopes.push(envelope);
|
|
636
640
|
} catch {}
|
|
637
641
|
if (shareEnvelopes.length === 0) return null;
|
|
638
|
-
const
|
|
642
|
+
const sskrJoin = Envelope.sskrJoin;
|
|
643
|
+
const recoveredEnvelope = sskrJoin(shareEnvelopes).unwrap();
|
|
639
644
|
return Seed$1.fromEnvelope(recoveredEnvelope);
|
|
640
645
|
} catch {
|
|
641
646
|
return null;
|
|
@@ -656,7 +661,7 @@ function fromTaggedCborShares(taggedCborDataShares) {
|
|
|
656
661
|
const cbor = decodeCbor(data);
|
|
657
662
|
const tagged = cbor;
|
|
658
663
|
if (tagged.tag !== SSKR_SHARE.value && tagged.tag !== SSKR_SHARE_V1.value) return null;
|
|
659
|
-
const bytes = expectBytes(tagged.value
|
|
664
|
+
const bytes = expectBytes(tagged.value !== void 0 ? tagged.value : cbor);
|
|
660
665
|
untaggedShares.push(bytes);
|
|
661
666
|
}
|
|
662
667
|
return fromUntaggedCborShares(untaggedShares);
|
|
@@ -690,7 +695,7 @@ function parseUr(input, expectedTagValue, allowTaggedCbor) {
|
|
|
690
695
|
urs.push(ur);
|
|
691
696
|
} catch {}
|
|
692
697
|
if (urs.length === 0) return null;
|
|
693
|
-
for (const ur of urs) if (ur.
|
|
698
|
+
for (const ur of urs) if (ur.urTypeStr() !== expectedType) return null;
|
|
694
699
|
const untaggedCborShares = [];
|
|
695
700
|
for (const ur of urs) {
|
|
696
701
|
let cbor = ur.cbor();
|
|
@@ -715,9 +720,9 @@ function parseSskrSeed(input) {
|
|
|
715
720
|
if (btwmResult !== null) return btwmResult;
|
|
716
721
|
const btwuResult = parseBytewords(input, BytewordsStyle.Uri);
|
|
717
722
|
if (btwuResult !== null) return btwuResult;
|
|
718
|
-
const urResult = parseUr(input, SSKR_SHARE.value, false);
|
|
723
|
+
const urResult = parseUr(input, Number(SSKR_SHARE.value), false);
|
|
719
724
|
if (urResult !== null) return urResult;
|
|
720
|
-
const urLegacyResult = parseUr(input, SSKR_SHARE_V1.value, true);
|
|
725
|
+
const urLegacyResult = parseUr(input, Number(SSKR_SHARE_V1.value), true);
|
|
721
726
|
if (urLegacyResult !== null) return urLegacyResult;
|
|
722
727
|
throw new Error("Insufficient or invalid SSKR shares.");
|
|
723
728
|
}
|
|
@@ -794,7 +799,7 @@ var MultipartFormat = class {
|
|
|
794
799
|
}
|
|
795
800
|
if (!decoder.isComplete()) throw new Error("Insufficient multipart shares");
|
|
796
801
|
const ur = decoder.message();
|
|
797
|
-
if (ur === void 0) throw new Error("Failed to decode multipart message");
|
|
802
|
+
if (ur === void 0 || ur === null) throw new Error("Failed to decode multipart message");
|
|
798
803
|
const envelope = Envelope.fromUR(ur);
|
|
799
804
|
state.seed = Seed$1.fromEnvelope(envelope);
|
|
800
805
|
return state;
|
|
@@ -1205,28 +1210,31 @@ var BytewordsUriFormat = class {
|
|
|
1205
1210
|
//#endregion
|
|
1206
1211
|
//#region src/formats/format.ts
|
|
1207
1212
|
/**
|
|
1213
|
+
* Format traits and factory functions
|
|
1214
|
+
* Ported from seedtool-cli-rust/src/formats/format.rs
|
|
1215
|
+
*/
|
|
1216
|
+
/**
|
|
1208
1217
|
* Select input format by key.
|
|
1209
1218
|
* Matches Rust select_input_format function.
|
|
1210
1219
|
*/
|
|
1211
1220
|
function selectInputFormat(key) {
|
|
1212
1221
|
switch (key) {
|
|
1213
|
-
case
|
|
1214
|
-
case
|
|
1215
|
-
case
|
|
1216
|
-
case
|
|
1217
|
-
case
|
|
1218
|
-
case
|
|
1219
|
-
case
|
|
1220
|
-
case
|
|
1221
|
-
case
|
|
1222
|
-
case
|
|
1223
|
-
case
|
|
1224
|
-
case
|
|
1225
|
-
case
|
|
1226
|
-
case
|
|
1227
|
-
case
|
|
1228
|
-
case
|
|
1229
|
-
default: throw new Error(`Unknown input format: ${key}`);
|
|
1222
|
+
case InputFormatKey.Random: return new RandomFormat();
|
|
1223
|
+
case InputFormatKey.Hex: return new HexFormat();
|
|
1224
|
+
case InputFormatKey.Btw: return new BytewordsStandardFormat();
|
|
1225
|
+
case InputFormatKey.Btwu: return new BytewordsUriFormat();
|
|
1226
|
+
case InputFormatKey.Btwm: return new BytewordsMinimalFormat();
|
|
1227
|
+
case InputFormatKey.Bits: return new BitsFormat();
|
|
1228
|
+
case InputFormatKey.Cards: return new CardsFormat();
|
|
1229
|
+
case InputFormatKey.Dice: return new DiceFormat();
|
|
1230
|
+
case InputFormatKey.Base6: return new Base6Format();
|
|
1231
|
+
case InputFormatKey.Base10: return new Base10Format();
|
|
1232
|
+
case InputFormatKey.Ints: return new IntsFormat();
|
|
1233
|
+
case InputFormatKey.Bip39: return new Bip39Format();
|
|
1234
|
+
case InputFormatKey.Sskr: return new SSKRFormat();
|
|
1235
|
+
case InputFormatKey.Envelope: return new EnvelopeFormat();
|
|
1236
|
+
case InputFormatKey.Multipart: return new MultipartFormat();
|
|
1237
|
+
case InputFormatKey.Seed: return new SeedFormat();
|
|
1230
1238
|
}
|
|
1231
1239
|
}
|
|
1232
1240
|
/**
|
|
@@ -1235,22 +1243,21 @@ function selectInputFormat(key) {
|
|
|
1235
1243
|
*/
|
|
1236
1244
|
function selectOutputFormat(key) {
|
|
1237
1245
|
switch (key) {
|
|
1238
|
-
case
|
|
1239
|
-
case
|
|
1240
|
-
case
|
|
1241
|
-
case
|
|
1242
|
-
case
|
|
1243
|
-
case
|
|
1244
|
-
case
|
|
1245
|
-
case
|
|
1246
|
-
case
|
|
1247
|
-
case
|
|
1248
|
-
case
|
|
1249
|
-
case
|
|
1250
|
-
case
|
|
1251
|
-
case
|
|
1252
|
-
case
|
|
1253
|
-
default: throw new Error(`Unknown output format: ${key}`);
|
|
1246
|
+
case OutputFormatKey.Hex: return new HexFormat();
|
|
1247
|
+
case OutputFormatKey.Btw: return new BytewordsStandardFormat();
|
|
1248
|
+
case OutputFormatKey.Btwu: return new BytewordsUriFormat();
|
|
1249
|
+
case OutputFormatKey.Btwm: return new BytewordsMinimalFormat();
|
|
1250
|
+
case OutputFormatKey.Bits: return new BitsFormat();
|
|
1251
|
+
case OutputFormatKey.Cards: return new CardsFormat();
|
|
1252
|
+
case OutputFormatKey.Dice: return new DiceFormat();
|
|
1253
|
+
case OutputFormatKey.Base6: return new Base6Format();
|
|
1254
|
+
case OutputFormatKey.Base10: return new Base10Format();
|
|
1255
|
+
case OutputFormatKey.Ints: return new IntsFormat();
|
|
1256
|
+
case OutputFormatKey.Bip39: return new Bip39Format();
|
|
1257
|
+
case OutputFormatKey.Sskr: return new SSKRFormat();
|
|
1258
|
+
case OutputFormatKey.Envelope: return new EnvelopeFormat();
|
|
1259
|
+
case OutputFormatKey.Multipart: return new MultipartFormat();
|
|
1260
|
+
case OutputFormatKey.Seed: return new SeedFormat();
|
|
1254
1261
|
}
|
|
1255
1262
|
}
|
|
1256
1263
|
|
|
@@ -1286,7 +1293,7 @@ function parseGroupSpec(value, previous) {
|
|
|
1286
1293
|
const spec = SSKRGroupSpec.parse(value);
|
|
1287
1294
|
return [...previous, spec];
|
|
1288
1295
|
}
|
|
1289
|
-
|
|
1296
|
+
function main() {
|
|
1290
1297
|
const program = new Command();
|
|
1291
1298
|
program.name("seedtool").description("A tool for generating and transforming cryptographic seeds.\n\nby Wolf McNally and Christopher Allen\n\nReport bugs to ChristopherA@BlockchainCommons.com.\n© 2024 Blockchain Commons.").version(VERSION).argument("[INPUT]", "The input to be transformed. If required and not present, it will be read from stdin.").option("-c, --count <COUNT>", "The number of output units (hex bytes, base-10 digits, etc.)", "16").addOption(new Option("-i, --in <INPUT_TYPE>", "The input format. If not specified, a new random seed is generated using a secure random number generator.").choices([
|
|
1292
1299
|
"random",
|
|
@@ -1333,21 +1340,21 @@ async function main() {
|
|
|
1333
1340
|
const args = program.args;
|
|
1334
1341
|
const cli = new Cli();
|
|
1335
1342
|
if (args.length > 0) cli.input = args[0];
|
|
1336
|
-
else if (
|
|
1343
|
+
else if (process.stdin.isTTY !== true) cli.input = fs.readFileSync(process.stdin.fd, "utf-8").trim();
|
|
1337
1344
|
cli.count = parseInt(options.count, 10);
|
|
1338
1345
|
cli.in = options.in;
|
|
1339
1346
|
cli.out = options.out;
|
|
1340
1347
|
cli.low = options.low;
|
|
1341
1348
|
cli.high = options.high;
|
|
1342
|
-
cli.name = options.name;
|
|
1343
|
-
cli.note = options.note;
|
|
1344
|
-
if (options.date) cli.date = parseCliDate(options.date);
|
|
1349
|
+
if (options.name !== void 0) cli.name = options.name;
|
|
1350
|
+
if (options.note !== void 0) cli.note = options.note;
|
|
1351
|
+
if (options.date !== void 0) cli.date = parseCliDate(options.date);
|
|
1345
1352
|
cli.maxFragmentLen = parseInt(options.maxFragmentLen, 10);
|
|
1346
1353
|
cli.additionalParts = parseInt(options.additionalParts, 10);
|
|
1347
1354
|
cli.groups = options.groups;
|
|
1348
1355
|
cli.groupThreshold = options.groupThreshold;
|
|
1349
1356
|
cli.sskrFormat = options.sskrFormat;
|
|
1350
|
-
if (options.deterministic) cli.rng = {
|
|
1357
|
+
if (options.deterministic !== void 0) cli.rng = {
|
|
1351
1358
|
type: "deterministic",
|
|
1352
1359
|
rng: DeterministicRandomNumberGenerator.newWithSeed(options.deterministic)
|
|
1353
1360
|
};
|
|
@@ -1359,16 +1366,19 @@ async function main() {
|
|
|
1359
1366
|
const outputFormat = selectOutputFormat(cli.out);
|
|
1360
1367
|
if (!outputFormat.roundTrippable() && inputFormat.name() !== "random") {
|
|
1361
1368
|
console.error(`Input for output form "${outputFormat.name()}" must be random.`);
|
|
1362
|
-
|
|
1369
|
+
throw new Error("Invalid input format for non-round-trippable output format");
|
|
1363
1370
|
}
|
|
1364
1371
|
const processedCli = inputFormat.processInput(cli);
|
|
1365
1372
|
const output = outputFormat.processOutput(processedCli);
|
|
1366
1373
|
console.log(output);
|
|
1367
1374
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1375
|
+
try {
|
|
1376
|
+
main();
|
|
1377
|
+
} catch (error) {
|
|
1378
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1379
|
+
console.error(message);
|
|
1370
1380
|
process.exit(1);
|
|
1371
|
-
}
|
|
1381
|
+
}
|
|
1372
1382
|
|
|
1373
1383
|
//#endregion
|
|
1374
1384
|
export { };
|