@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 CHANGED
@@ -1,5 +1,5 @@
1
1
  Copyright © 2025 Blockchain Commons, LLC
2
- Copyright © 2025 Leonardo Amoroso Custodio
2
+ Copyright © 2025-2026 Leonardo Amoroso Custodio
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without modification,
5
5
  are permitted provided that the following conditions are met:
package/README.md CHANGED
@@ -1,11 +1,80 @@
1
- # Blockchain Commons Known Values for TypeScript
1
+ # Blockchain Commons Seedtool CLI (TypeScript)
2
2
 
3
3
  > Disclaimer: This package is under active development and APIs may change.
4
4
 
5
5
  ## Introduction
6
6
 
7
- This package defines the [known values](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2023-002-known-value.md) used in Blockchain Commons projects.
7
+ `@bcts/seedtool-cli` is a command line tool for generating and transforming cryptographic seeds. It supports multiple input and output formats including BIP39 mnemonic phrases, hex, SSKR shares, and more.
8
+
9
+ Features:
10
+ - Generate random cryptographic seeds
11
+ - Convert between seed formats (hex, BIP39, SSKR, etc.)
12
+ - Split seeds using SSKR (Sharded Secret Key Reconstruction)
13
+ - Recover seeds from SSKR shares
14
+ - Support for deterministic seed generation
8
15
 
9
16
  ## Rust Reference Implementation
10
17
 
11
- This TypeScript implementation is based on [known-values-rust](https://github.com/BlockchainCommons/known-values-rust) **v0.13.0** ([commit](https://github.com/BlockchainCommons/known-values-rust/tree/75c0b893be3eebe0432749ef24b804569f6e1378)).
18
+ This TypeScript implementation is based on [seedtool-cli-rust](https://github.com/BlockchainCommons/seedtool-cli-rust).
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ # Install globally
24
+ bun add -g @bcts/seedtool-cli
25
+
26
+ # Or run directly with bunx
27
+ bunx @bcts/seedtool-cli --help
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ### Generate a Random Seed
33
+
34
+ ```bash
35
+ # Generate a 16-byte (128-bit) seed (default)
36
+ seedtool
37
+
38
+ # Generate a 32-byte (256-bit) seed
39
+ seedtool --count 32
40
+ ```
41
+
42
+ ### Input/Output Formats
43
+
44
+ ```bash
45
+ # Output as BIP39 mnemonic
46
+ seedtool --out bip39
47
+
48
+ # Output as hex
49
+ seedtool --out hex
50
+
51
+ # Input from hex, output as BIP39
52
+ seedtool --in hex --out bip39 7e31b2b14b895e75cdb82c22b013527c
53
+ ```
54
+
55
+ ### SSKR Shares
56
+
57
+ ```bash
58
+ # Split a seed into SSKR shares (2-of-3)
59
+ seedtool --out sskr --groups 2-of-3
60
+
61
+ # Recover from SSKR shares
62
+ seedtool --in sskr <share1> <share2>
63
+ ```
64
+
65
+ ## Command Line Reference
66
+
67
+ ```
68
+ Usage: seedtool [options] [input...]
69
+
70
+ Arguments:
71
+ input Input data (format specified by --in)
72
+
73
+ Options:
74
+ -c, --count <n> Number of bytes to generate (default: 16)
75
+ -i, --in <format> Input format: hex, bip39, sskr, random (default: random)
76
+ -o, --out <format> Output format: hex, bip39, sskr, ur (default: hex)
77
+ -g, --groups <spec> SSKR group specification (e.g., "2-of-3")
78
+ -h, --help Display help
79
+ -V, --version Display version
80
+ ```
package/dist/index.cjs CHANGED
@@ -25,16 +25,16 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
  }) : target, mod));
26
26
 
27
27
  //#endregion
28
- let readline = require("readline");
29
- readline = __toESM(readline);
28
+ let node_readline = require("node:readline");
29
+ node_readline = __toESM(node_readline);
30
30
  let _bcts_components = require("@bcts/components");
31
31
  let _bcts_envelope = require("@bcts/envelope");
32
32
  let _bcts_known_values = require("@bcts/known-values");
33
33
  let _bcts_dcbor = require("@bcts/dcbor");
34
- let _noble_hashes_sha256 = require("@noble/hashes/sha256");
35
- let _noble_hashes_hkdf = require("@noble/hashes/hkdf");
34
+ let _noble_hashes_sha2_js = require("@noble/hashes/sha2.js");
35
+ let _noble_hashes_hkdf_js = require("@noble/hashes/hkdf.js");
36
36
  let _scure_bip39 = require("@scure/bip39");
37
- let _scure_bip39_wordlists_english = require("@scure/bip39/wordlists/english");
37
+ let _scure_bip39_wordlists_english_js = require("@scure/bip39/wordlists/english.js");
38
38
  let _bcts_uniform_resources = require("@bcts/uniform-resources");
39
39
  let _bcts_tags = require("@bcts/tags");
40
40
 
@@ -143,8 +143,8 @@ function parseDate(s) {
143
143
  * Returns SSKRGroupSpec instance.
144
144
  */
145
145
  function parseGroupSpec(s) {
146
- const match = s.match(/^(\d+)-of-(\d+)$/i);
147
- if (!match) throw new Error(`Invalid group specification: ${s}. Use format 'M-of-N' (e.g., '2-of-3').`);
146
+ const match = /^(\d+)-of-(\d+)$/i.exec(s);
147
+ if (match === null) throw new Error(`Invalid group specification: ${s}. Use format 'M-of-N' (e.g., '2-of-3').`);
148
148
  const threshold = parseInt(match[1], 10);
149
149
  const count = parseInt(match[2], 10);
150
150
  return _bcts_components.SSKRGroupSpec.new(threshold, count);
@@ -204,13 +204,13 @@ var Cli = class Cli {
204
204
  if (this.input !== void 0) return this.input;
205
205
  return new Promise((resolve, reject) => {
206
206
  let data = "";
207
- const rl = readline.createInterface({
207
+ const rl = node_readline.createInterface({
208
208
  input: process.stdin,
209
209
  output: process.stdout,
210
210
  terminal: false
211
211
  });
212
212
  rl.on("line", (line) => {
213
- data += line + "\n";
213
+ data += `${line}\n`;
214
214
  });
215
215
  rl.on("close", () => {
216
216
  resolve(data.trim());
@@ -267,23 +267,23 @@ var Cli = class Cli {
267
267
  */
268
268
  clone() {
269
269
  const cli = new Cli();
270
- cli.input = this.input;
270
+ if (this.input !== void 0) cli.input = this.input;
271
271
  cli.count = this.count;
272
272
  cli.in = this.in;
273
273
  cli.out = this.out;
274
274
  cli.low = this.low;
275
275
  cli.high = this.high;
276
- cli.name = this.name;
277
- cli.note = this.note;
278
- cli.date = this.date;
276
+ if (this.name !== void 0) cli.name = this.name;
277
+ if (this.note !== void 0) cli.note = this.note;
278
+ if (this.date !== void 0) cli.date = this.date;
279
279
  cli.maxFragmentLen = this.maxFragmentLen;
280
280
  cli.additionalParts = this.additionalParts;
281
281
  cli.groups = [...this.groups];
282
282
  cli.groupThreshold = this.groupThreshold;
283
283
  cli.sskrFormat = this.sskrFormat;
284
- cli.deterministic = this.deterministic;
285
- cli.seed = this.seed?.clone();
286
- cli.rng = this.rng;
284
+ if (this.deterministic !== void 0) cli.deterministic = this.deterministic;
285
+ if (this.seed !== void 0) cli.seed = this.seed.clone();
286
+ if (this.rng !== void 0) cli.rng = this.rng;
287
287
  return cli;
288
288
  }
289
289
  };
@@ -405,7 +405,7 @@ var Seed = class Seed {
405
405
  * - Optional note assertion (if not empty)
406
406
  */
407
407
  toEnvelope() {
408
- let envelope = _bcts_envelope.Envelope.new((0, _bcts_dcbor.toByteString)(this._data));
408
+ let envelope = _bcts_envelope.Envelope.new(this._data);
409
409
  envelope = envelope.addType(_bcts_known_values.SEED_TYPE);
410
410
  if (this._creationDate !== void 0) {
411
411
  const cborDate = _bcts_dcbor.CborDate.fromDatetime(this._creationDate);
@@ -524,7 +524,7 @@ var DeterministicRandomNumberGenerator = class DeterministicRandomNumberGenerato
524
524
  * Matches Rust new_with_seed function.
525
525
  */
526
526
  static newWithSeed(seedString) {
527
- return new DeterministicRandomNumberGenerator((0, _noble_hashes_sha256.sha256)(new TextEncoder().encode(seedString)));
527
+ return new DeterministicRandomNumberGenerator((0, _noble_hashes_sha2_js.sha256)(new TextEncoder().encode(seedString)));
528
528
  }
529
529
  /**
530
530
  * Generate deterministic random data.
@@ -557,7 +557,7 @@ var DeterministicRandomNumberGenerator = class DeterministicRandomNumberGenerato
557
557
  * Matches Rust hkdf_hmac_sha256 function from bc-crypto.
558
558
  */
559
559
  function hkdfHmacSha256(ikm, salt, length) {
560
- return (0, _noble_hashes_hkdf.hkdf)(_noble_hashes_sha256.sha256, ikm, salt, new Uint8Array(0), length);
560
+ return (0, _noble_hashes_hkdf_js.hkdf)(_noble_hashes_sha2_js.sha256, ikm, salt, new Uint8Array(0), length);
561
561
  }
562
562
  /**
563
563
  * Generate deterministic random data from entropy using SHA256.
@@ -569,7 +569,7 @@ function hkdfHmacSha256(ikm, salt, length) {
569
569
  * @throws Error if n > 32
570
570
  */
571
571
  function sha256DeterministicRandom(entropy, n) {
572
- const seed = (0, _noble_hashes_sha256.sha256)(entropy);
572
+ const seed = (0, _noble_hashes_sha2_js.sha256)(entropy);
573
573
  if (n <= seed.length) return seed.slice(0, n);
574
574
  else throw new Error("Random number generator limits reached.");
575
575
  }
@@ -593,7 +593,7 @@ function sha256DeterministicRandomString(str, n) {
593
593
  * @param n - Number of bytes to return
594
594
  */
595
595
  function deterministicRandom(entropy, n) {
596
- return hkdfHmacSha256((0, _noble_hashes_sha256.sha256)(entropy), new Uint8Array(0), n);
596
+ return hkdfHmacSha256((0, _noble_hashes_sha2_js.sha256)(entropy), new Uint8Array(0), n);
597
597
  }
598
598
 
599
599
  //#endregion
@@ -744,19 +744,23 @@ var Bip39Format = class {
744
744
  }
745
745
  processInput(state) {
746
746
  const normalized = state.expectInput().toLowerCase().trim().replace(/\s+/g, " ");
747
- if (!(0, _scure_bip39.validateMnemonic)(normalized, _scure_bip39_wordlists_english.wordlist)) throw new Error("Invalid BIP39 mnemonic");
748
- const entropy = (0, _scure_bip39.mnemonicToEntropy)(normalized, _scure_bip39_wordlists_english.wordlist);
747
+ if (!(0, _scure_bip39.validateMnemonic)(normalized, _scure_bip39_wordlists_english_js.wordlist)) throw new Error("Invalid BIP39 mnemonic");
748
+ const entropy = (0, _scure_bip39.mnemonicToEntropy)(normalized, _scure_bip39_wordlists_english_js.wordlist);
749
749
  state.seed = Seed.new(entropy);
750
750
  return state;
751
751
  }
752
752
  processOutput(state) {
753
- return (0, _scure_bip39.entropyToMnemonic)(state.expectSeed().data(), _scure_bip39_wordlists_english.wordlist);
753
+ return (0, _scure_bip39.entropyToMnemonic)(state.expectSeed().data(), _scure_bip39_wordlists_english_js.wordlist);
754
754
  }
755
755
  };
756
756
 
757
757
  //#endregion
758
758
  //#region src/formats/sskr.ts
759
759
  /**
760
+ * SSKR format
761
+ * Ported from seedtool-cli-rust/src/formats/sskr.rs
762
+ */
763
+ /**
760
764
  * SSKR format handler.
761
765
  * Round-trippable: sskr shares → seed → sskr shares.
762
766
  * Supports multiple sub-formats: envelope, btw, btwm, btwu, ur.
@@ -781,16 +785,16 @@ var SSKRFormat = class {
781
785
  };
782
786
  function outputSskrSeed(seed, spec, format) {
783
787
  switch (format) {
784
- case "envelope": {
785
- const envelope = seed.toEnvelope();
788
+ case SSKRFormatKey.Envelope: {
789
+ const seedEnvelope = seed.toEnvelope();
786
790
  const contentKey = _bcts_envelope.SymmetricKey.new();
787
- return envelope.wrap().encryptSubject(contentKey).sskrSplitFlattened(spec, contentKey).map((envelope$1) => envelope$1.urString()).join("\n");
791
+ return seedEnvelope.wrap().encryptSubject(contentKey).sskrSplitFlattened(spec, contentKey).map((env) => env.urString()).join("\n");
788
792
  }
789
- case "btw": return makeBytewordsShares(spec, seed, _bcts_uniform_resources.BytewordsStyle.Standard);
790
- case "btwm": return makeBytewordsShares(spec, seed, _bcts_uniform_resources.BytewordsStyle.Minimal);
791
- case "btwu": return makeBytewordsShares(spec, seed, _bcts_uniform_resources.BytewordsStyle.Uri);
792
- case "ur": return makeShares(spec, seed).map((share) => {
793
- return _bcts_uniform_resources.UR.fromCbor("sskr", (0, _bcts_dcbor.toByteString)(share.asBytes())).toString();
793
+ case SSKRFormatKey.Btw: return makeBytewordsShares(spec, seed, _bcts_uniform_resources.BytewordsStyle.Standard);
794
+ case SSKRFormatKey.Btwm: return makeBytewordsShares(spec, seed, _bcts_uniform_resources.BytewordsStyle.Minimal);
795
+ case SSKRFormatKey.Btwu: return makeBytewordsShares(spec, seed, _bcts_uniform_resources.BytewordsStyle.Uri);
796
+ case SSKRFormatKey.Ur: return makeShares(spec, seed).map((share) => {
797
+ return _bcts_uniform_resources.UR.new("sskr", (0, _bcts_dcbor.toByteString)(share.asBytes())).toString();
794
798
  }).join("\n");
795
799
  }
796
800
  }
@@ -812,7 +816,8 @@ function parseEnvelopes(input) {
812
816
  shareEnvelopes.push(envelope);
813
817
  } catch {}
814
818
  if (shareEnvelopes.length === 0) return null;
815
- const recoveredEnvelope = _bcts_envelope.Envelope.sskrJoin(shareEnvelopes).unwrap();
819
+ const sskrJoin = _bcts_envelope.Envelope.sskrJoin;
820
+ const recoveredEnvelope = sskrJoin(shareEnvelopes).unwrap();
816
821
  return Seed.fromEnvelope(recoveredEnvelope);
817
822
  } catch {
818
823
  return null;
@@ -833,7 +838,7 @@ function fromTaggedCborShares(taggedCborDataShares) {
833
838
  const cbor = (0, _bcts_dcbor.decodeCbor)(data);
834
839
  const tagged = cbor;
835
840
  if (tagged.tag !== _bcts_tags.SSKR_SHARE.value && tagged.tag !== _bcts_tags.SSKR_SHARE_V1.value) return null;
836
- const bytes = (0, _bcts_dcbor.expectBytes)(tagged.value || cbor);
841
+ const bytes = (0, _bcts_dcbor.expectBytes)(tagged.value !== void 0 ? tagged.value : cbor);
837
842
  untaggedShares.push(bytes);
838
843
  }
839
844
  return fromUntaggedCborShares(untaggedShares);
@@ -867,7 +872,7 @@ function parseUr(input, expectedTagValue, allowTaggedCbor) {
867
872
  urs.push(ur);
868
873
  } catch {}
869
874
  if (urs.length === 0) return null;
870
- for (const ur of urs) if (ur.type() !== expectedType) return null;
875
+ for (const ur of urs) if (ur.urTypeStr() !== expectedType) return null;
871
876
  const untaggedCborShares = [];
872
877
  for (const ur of urs) {
873
878
  let cbor = ur.cbor();
@@ -892,9 +897,9 @@ function parseSskrSeed(input) {
892
897
  if (btwmResult !== null) return btwmResult;
893
898
  const btwuResult = parseBytewords(input, _bcts_uniform_resources.BytewordsStyle.Uri);
894
899
  if (btwuResult !== null) return btwuResult;
895
- const urResult = parseUr(input, _bcts_tags.SSKR_SHARE.value, false);
900
+ const urResult = parseUr(input, Number(_bcts_tags.SSKR_SHARE.value), false);
896
901
  if (urResult !== null) return urResult;
897
- const urLegacyResult = parseUr(input, _bcts_tags.SSKR_SHARE_V1.value, true);
902
+ const urLegacyResult = parseUr(input, Number(_bcts_tags.SSKR_SHARE_V1.value), true);
898
903
  if (urLegacyResult !== null) return urLegacyResult;
899
904
  throw new Error("Insufficient or invalid SSKR shares.");
900
905
  }
@@ -971,7 +976,7 @@ var MultipartFormat = class {
971
976
  }
972
977
  if (!decoder.isComplete()) throw new Error("Insufficient multipart shares");
973
978
  const ur = decoder.message();
974
- if (ur === void 0) throw new Error("Failed to decode multipart message");
979
+ if (ur === void 0 || ur === null) throw new Error("Failed to decode multipart message");
975
980
  const envelope = _bcts_envelope.Envelope.fromUR(ur);
976
981
  state.seed = Seed.fromEnvelope(envelope);
977
982
  return state;
@@ -1278,28 +1283,31 @@ var BytewordsUriFormat = class {
1278
1283
  //#endregion
1279
1284
  //#region src/formats/format.ts
1280
1285
  /**
1286
+ * Format traits and factory functions
1287
+ * Ported from seedtool-cli-rust/src/formats/format.rs
1288
+ */
1289
+ /**
1281
1290
  * Select input format by key.
1282
1291
  * Matches Rust select_input_format function.
1283
1292
  */
1284
1293
  function selectInputFormat(key) {
1285
1294
  switch (key) {
1286
- case "random": return new RandomFormat();
1287
- case "hex": return new HexFormat();
1288
- case "btw": return new BytewordsStandardFormat();
1289
- case "btwu": return new BytewordsUriFormat();
1290
- case "btwm": return new BytewordsMinimalFormat();
1291
- case "bits": return new BitsFormat();
1292
- case "cards": return new CardsFormat();
1293
- case "dice": return new DiceFormat();
1294
- case "base6": return new Base6Format();
1295
- case "base10": return new Base10Format();
1296
- case "ints": return new IntsFormat();
1297
- case "bip39": return new Bip39Format();
1298
- case "sskr": return new SSKRFormat();
1299
- case "envelope": return new EnvelopeFormat();
1300
- case "multipart": return new MultipartFormat();
1301
- case "seed": return new SeedFormat();
1302
- default: throw new Error(`Unknown input format: ${key}`);
1295
+ case InputFormatKey.Random: return new RandomFormat();
1296
+ case InputFormatKey.Hex: return new HexFormat();
1297
+ case InputFormatKey.Btw: return new BytewordsStandardFormat();
1298
+ case InputFormatKey.Btwu: return new BytewordsUriFormat();
1299
+ case InputFormatKey.Btwm: return new BytewordsMinimalFormat();
1300
+ case InputFormatKey.Bits: return new BitsFormat();
1301
+ case InputFormatKey.Cards: return new CardsFormat();
1302
+ case InputFormatKey.Dice: return new DiceFormat();
1303
+ case InputFormatKey.Base6: return new Base6Format();
1304
+ case InputFormatKey.Base10: return new Base10Format();
1305
+ case InputFormatKey.Ints: return new IntsFormat();
1306
+ case InputFormatKey.Bip39: return new Bip39Format();
1307
+ case InputFormatKey.Sskr: return new SSKRFormat();
1308
+ case InputFormatKey.Envelope: return new EnvelopeFormat();
1309
+ case InputFormatKey.Multipart: return new MultipartFormat();
1310
+ case InputFormatKey.Seed: return new SeedFormat();
1303
1311
  }
1304
1312
  }
1305
1313
  /**
@@ -1308,22 +1316,21 @@ function selectInputFormat(key) {
1308
1316
  */
1309
1317
  function selectOutputFormat(key) {
1310
1318
  switch (key) {
1311
- case "hex": return new HexFormat();
1312
- case "btw": return new BytewordsStandardFormat();
1313
- case "btwu": return new BytewordsUriFormat();
1314
- case "btwm": return new BytewordsMinimalFormat();
1315
- case "bits": return new BitsFormat();
1316
- case "cards": return new CardsFormat();
1317
- case "dice": return new DiceFormat();
1318
- case "base6": return new Base6Format();
1319
- case "base10": return new Base10Format();
1320
- case "ints": return new IntsFormat();
1321
- case "bip39": return new Bip39Format();
1322
- case "sskr": return new SSKRFormat();
1323
- case "envelope": return new EnvelopeFormat();
1324
- case "multipart": return new MultipartFormat();
1325
- case "seed": return new SeedFormat();
1326
- default: throw new Error(`Unknown output format: ${key}`);
1319
+ case OutputFormatKey.Hex: return new HexFormat();
1320
+ case OutputFormatKey.Btw: return new BytewordsStandardFormat();
1321
+ case OutputFormatKey.Btwu: return new BytewordsUriFormat();
1322
+ case OutputFormatKey.Btwm: return new BytewordsMinimalFormat();
1323
+ case OutputFormatKey.Bits: return new BitsFormat();
1324
+ case OutputFormatKey.Cards: return new CardsFormat();
1325
+ case OutputFormatKey.Dice: return new DiceFormat();
1326
+ case OutputFormatKey.Base6: return new Base6Format();
1327
+ case OutputFormatKey.Base10: return new Base10Format();
1328
+ case OutputFormatKey.Ints: return new IntsFormat();
1329
+ case OutputFormatKey.Bip39: return new Bip39Format();
1330
+ case OutputFormatKey.Sskr: return new SSKRFormat();
1331
+ case OutputFormatKey.Envelope: return new EnvelopeFormat();
1332
+ case OutputFormatKey.Multipart: return new MultipartFormat();
1333
+ case OutputFormatKey.Seed: return new SeedFormat();
1327
1334
  }
1328
1335
  }
1329
1336