@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/src/cli.ts
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* Ported from seedtool-cli-rust/src/cli.rs
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import * as readline from "readline";
|
|
6
|
+
import * as readline from "node:readline";
|
|
7
7
|
import { SSKRGroupSpec, SSKRSpec } from "@bcts/components";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import type { SecureRandomNumberGenerator } from "@bcts/rand";
|
|
9
|
+
import type { Envelope } from "@bcts/envelope";
|
|
10
10
|
import type { Seed } from "./seed.js";
|
|
11
11
|
import type { DeterministicRandomNumberGenerator } from "./random.js";
|
|
12
12
|
|
|
@@ -144,8 +144,9 @@ export function parseDate(s: string): Date {
|
|
|
144
144
|
* Returns SSKRGroupSpec instance.
|
|
145
145
|
*/
|
|
146
146
|
export function parseGroupSpec(s: string): SSKRGroupSpec {
|
|
147
|
-
const
|
|
148
|
-
|
|
147
|
+
const regex = /^(\d+)-of-(\d+)$/i;
|
|
148
|
+
const match = regex.exec(s);
|
|
149
|
+
if (match === null) {
|
|
149
150
|
throw new Error(`Invalid group specification: ${s}. Use format 'M-of-N' (e.g., '2-of-3').`);
|
|
150
151
|
}
|
|
151
152
|
const threshold = parseInt(match[1], 10);
|
|
@@ -166,19 +167,19 @@ export class Cli {
|
|
|
166
167
|
input?: string;
|
|
167
168
|
|
|
168
169
|
/** The number of output units (hex bytes, base-10 digits, etc.) */
|
|
169
|
-
count
|
|
170
|
+
count = 16;
|
|
170
171
|
|
|
171
172
|
/** The input format. Default: Random */
|
|
172
|
-
in
|
|
173
|
+
in = InputFormatKey.Random;
|
|
173
174
|
|
|
174
175
|
/** The output format. Default: Hex */
|
|
175
|
-
out
|
|
176
|
+
out = OutputFormatKey.Hex;
|
|
176
177
|
|
|
177
178
|
/** The lowest int returned (0-254). Default: 0 */
|
|
178
|
-
low
|
|
179
|
+
low = 0;
|
|
179
180
|
|
|
180
181
|
/** The highest int returned (1-255), low < high. Default: 9 */
|
|
181
|
-
high
|
|
182
|
+
high = 9;
|
|
182
183
|
|
|
183
184
|
/** The name of the seed. */
|
|
184
185
|
name?: string;
|
|
@@ -190,19 +191,19 @@ export class Cli {
|
|
|
190
191
|
date?: Date;
|
|
191
192
|
|
|
192
193
|
/** For `multipart` output, max fragment length. Default: 500 */
|
|
193
|
-
maxFragmentLen
|
|
194
|
+
maxFragmentLen = 500;
|
|
194
195
|
|
|
195
196
|
/** For `multipart` output, additional parts for fountain encoding. Default: 0 */
|
|
196
|
-
additionalParts
|
|
197
|
+
additionalParts = 0;
|
|
197
198
|
|
|
198
199
|
/** Group specifications for SSKR. */
|
|
199
200
|
groups: SSKRGroupSpec[] = [];
|
|
200
201
|
|
|
201
202
|
/** The number of groups that must meet their threshold. Default: 1 */
|
|
202
|
-
groupThreshold
|
|
203
|
+
groupThreshold = 1;
|
|
203
204
|
|
|
204
205
|
/** SSKR output format. Default: Envelope */
|
|
205
|
-
sskrFormat
|
|
206
|
+
sskrFormat = SSKRFormatKey.Envelope;
|
|
206
207
|
|
|
207
208
|
/** Deterministic RNG seed string. */
|
|
208
209
|
deterministic?: string;
|
|
@@ -244,15 +245,15 @@ export class Cli {
|
|
|
244
245
|
terminal: false,
|
|
245
246
|
});
|
|
246
247
|
|
|
247
|
-
rl.on("line", (line) => {
|
|
248
|
-
data += line
|
|
248
|
+
rl.on("line", (line: string) => {
|
|
249
|
+
data += `${line}\n`;
|
|
249
250
|
});
|
|
250
251
|
|
|
251
252
|
rl.on("close", () => {
|
|
252
253
|
resolve(data.trim());
|
|
253
254
|
});
|
|
254
255
|
|
|
255
|
-
rl.on("error", (err) => {
|
|
256
|
+
rl.on("error", (err: Error) => {
|
|
256
257
|
reject(err);
|
|
257
258
|
});
|
|
258
259
|
});
|
|
@@ -323,23 +324,23 @@ export class Cli {
|
|
|
323
324
|
*/
|
|
324
325
|
clone(): Cli {
|
|
325
326
|
const cli = new Cli();
|
|
326
|
-
cli.input = this.input;
|
|
327
|
+
if (this.input !== undefined) cli.input = this.input;
|
|
327
328
|
cli.count = this.count;
|
|
328
329
|
cli.in = this.in;
|
|
329
330
|
cli.out = this.out;
|
|
330
331
|
cli.low = this.low;
|
|
331
332
|
cli.high = this.high;
|
|
332
|
-
cli.name = this.name;
|
|
333
|
-
cli.note = this.note;
|
|
334
|
-
cli.date = this.date;
|
|
333
|
+
if (this.name !== undefined) cli.name = this.name;
|
|
334
|
+
if (this.note !== undefined) cli.note = this.note;
|
|
335
|
+
if (this.date !== undefined) cli.date = this.date;
|
|
335
336
|
cli.maxFragmentLen = this.maxFragmentLen;
|
|
336
337
|
cli.additionalParts = this.additionalParts;
|
|
337
338
|
cli.groups = [...this.groups];
|
|
338
339
|
cli.groupThreshold = this.groupThreshold;
|
|
339
340
|
cli.sskrFormat = this.sskrFormat;
|
|
340
|
-
cli.deterministic = this.deterministic;
|
|
341
|
-
cli.seed = this.seed
|
|
342
|
-
cli.rng = this.rng;
|
|
341
|
+
if (this.deterministic !== undefined) cli.deterministic = this.deterministic;
|
|
342
|
+
if (this.seed !== undefined) cli.seed = this.seed.clone();
|
|
343
|
+
if (this.rng !== undefined) cli.rng = this.rng;
|
|
343
344
|
return cli;
|
|
344
345
|
}
|
|
345
346
|
}
|
package/src/formats/bip39.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { Cli } from "../cli.js";
|
|
|
7
7
|
import type { InputFormat, OutputFormat } from "./format.js";
|
|
8
8
|
import { Seed } from "../seed.js";
|
|
9
9
|
import { mnemonicToEntropy, entropyToMnemonic, validateMnemonic } from "@scure/bip39";
|
|
10
|
-
import { wordlist } from "@scure/bip39/wordlists/english";
|
|
10
|
+
import { wordlist } from "@scure/bip39/wordlists/english.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* BIP39 mnemonic format handler.
|
package/src/formats/format.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Ported from seedtool-cli-rust/src/formats/format.rs
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import { InputFormatKey, OutputFormatKey, type Cli } from "../cli.js";
|
|
7
7
|
|
|
8
8
|
// ============================================================================
|
|
9
9
|
// Format Interfaces (Traits)
|
|
@@ -66,40 +66,38 @@ import { BytewordsUriFormat } from "./bytewords-uri.js";
|
|
|
66
66
|
*/
|
|
67
67
|
export function selectInputFormat(key: InputFormatKey): InputFormat {
|
|
68
68
|
switch (key) {
|
|
69
|
-
case
|
|
69
|
+
case InputFormatKey.Random:
|
|
70
70
|
return new RandomFormat();
|
|
71
|
-
case
|
|
71
|
+
case InputFormatKey.Hex:
|
|
72
72
|
return new HexFormat();
|
|
73
|
-
case
|
|
73
|
+
case InputFormatKey.Btw:
|
|
74
74
|
return new BytewordsStandardFormat();
|
|
75
|
-
case
|
|
75
|
+
case InputFormatKey.Btwu:
|
|
76
76
|
return new BytewordsUriFormat();
|
|
77
|
-
case
|
|
77
|
+
case InputFormatKey.Btwm:
|
|
78
78
|
return new BytewordsMinimalFormat();
|
|
79
|
-
case
|
|
79
|
+
case InputFormatKey.Bits:
|
|
80
80
|
return new BitsFormat();
|
|
81
|
-
case
|
|
81
|
+
case InputFormatKey.Cards:
|
|
82
82
|
return new CardsFormat();
|
|
83
|
-
case
|
|
83
|
+
case InputFormatKey.Dice:
|
|
84
84
|
return new DiceFormat();
|
|
85
|
-
case
|
|
85
|
+
case InputFormatKey.Base6:
|
|
86
86
|
return new Base6Format();
|
|
87
|
-
case
|
|
87
|
+
case InputFormatKey.Base10:
|
|
88
88
|
return new Base10Format();
|
|
89
|
-
case
|
|
89
|
+
case InputFormatKey.Ints:
|
|
90
90
|
return new IntsFormat();
|
|
91
|
-
case
|
|
91
|
+
case InputFormatKey.Bip39:
|
|
92
92
|
return new Bip39Format();
|
|
93
|
-
case
|
|
93
|
+
case InputFormatKey.Sskr:
|
|
94
94
|
return new SSKRFormat();
|
|
95
|
-
case
|
|
95
|
+
case InputFormatKey.Envelope:
|
|
96
96
|
return new EnvelopeFormat();
|
|
97
|
-
case
|
|
97
|
+
case InputFormatKey.Multipart:
|
|
98
98
|
return new MultipartFormat();
|
|
99
|
-
case
|
|
99
|
+
case InputFormatKey.Seed:
|
|
100
100
|
return new SeedFormat();
|
|
101
|
-
default:
|
|
102
|
-
throw new Error(`Unknown input format: ${key}`);
|
|
103
101
|
}
|
|
104
102
|
}
|
|
105
103
|
|
|
@@ -109,37 +107,35 @@ export function selectInputFormat(key: InputFormatKey): InputFormat {
|
|
|
109
107
|
*/
|
|
110
108
|
export function selectOutputFormat(key: OutputFormatKey): OutputFormat {
|
|
111
109
|
switch (key) {
|
|
112
|
-
case
|
|
110
|
+
case OutputFormatKey.Hex:
|
|
113
111
|
return new HexFormat();
|
|
114
|
-
case
|
|
112
|
+
case OutputFormatKey.Btw:
|
|
115
113
|
return new BytewordsStandardFormat();
|
|
116
|
-
case
|
|
114
|
+
case OutputFormatKey.Btwu:
|
|
117
115
|
return new BytewordsUriFormat();
|
|
118
|
-
case
|
|
116
|
+
case OutputFormatKey.Btwm:
|
|
119
117
|
return new BytewordsMinimalFormat();
|
|
120
|
-
case
|
|
118
|
+
case OutputFormatKey.Bits:
|
|
121
119
|
return new BitsFormat();
|
|
122
|
-
case
|
|
120
|
+
case OutputFormatKey.Cards:
|
|
123
121
|
return new CardsFormat();
|
|
124
|
-
case
|
|
122
|
+
case OutputFormatKey.Dice:
|
|
125
123
|
return new DiceFormat();
|
|
126
|
-
case
|
|
124
|
+
case OutputFormatKey.Base6:
|
|
127
125
|
return new Base6Format();
|
|
128
|
-
case
|
|
126
|
+
case OutputFormatKey.Base10:
|
|
129
127
|
return new Base10Format();
|
|
130
|
-
case
|
|
128
|
+
case OutputFormatKey.Ints:
|
|
131
129
|
return new IntsFormat();
|
|
132
|
-
case
|
|
130
|
+
case OutputFormatKey.Bip39:
|
|
133
131
|
return new Bip39Format();
|
|
134
|
-
case
|
|
132
|
+
case OutputFormatKey.Sskr:
|
|
135
133
|
return new SSKRFormat();
|
|
136
|
-
case
|
|
134
|
+
case OutputFormatKey.Envelope:
|
|
137
135
|
return new EnvelopeFormat();
|
|
138
|
-
case
|
|
136
|
+
case OutputFormatKey.Multipart:
|
|
139
137
|
return new MultipartFormat();
|
|
140
|
-
case
|
|
138
|
+
case OutputFormatKey.Seed:
|
|
141
139
|
return new SeedFormat();
|
|
142
|
-
default:
|
|
143
|
-
throw new Error(`Unknown output format: ${key}`);
|
|
144
140
|
}
|
|
145
141
|
}
|
package/src/formats/multipart.ts
CHANGED
package/src/formats/sskr.ts
CHANGED
|
@@ -3,15 +3,21 @@
|
|
|
3
3
|
* Ported from seedtool-cli-rust/src/formats/sskr.rs
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import { SSKRFormatKey, type Cli } from "../cli.js";
|
|
7
7
|
import type { InputFormat, OutputFormat } from "./format.js";
|
|
8
8
|
import { Seed } from "../seed.js";
|
|
9
|
-
import { Envelope } from "@bcts/envelope";
|
|
10
|
-
import {
|
|
11
|
-
|
|
9
|
+
import { Envelope, SymmetricKey } from "@bcts/envelope";
|
|
10
|
+
import {
|
|
11
|
+
SSKRShare,
|
|
12
|
+
SSKRSecret,
|
|
13
|
+
sskrGenerate,
|
|
14
|
+
sskrCombine,
|
|
15
|
+
type SSKRShareCbor,
|
|
16
|
+
} from "@bcts/components";
|
|
12
17
|
import { encodeBytewords, decodeBytewords, BytewordsStyle, UR } from "@bcts/uniform-resources";
|
|
13
18
|
import { SSKR_SHARE, SSKR_SHARE_V1 } from "@bcts/tags";
|
|
14
19
|
import { toByteString, toTaggedValue, decodeCbor, expectBytes } from "@bcts/dcbor";
|
|
20
|
+
import type { Spec } from "@bcts/sskr";
|
|
15
21
|
|
|
16
22
|
/**
|
|
17
23
|
* SSKR format handler.
|
|
@@ -45,33 +51,35 @@ export class SSKRFormat implements InputFormat, OutputFormat {
|
|
|
45
51
|
// Output Helpers
|
|
46
52
|
//
|
|
47
53
|
|
|
48
|
-
function outputSskrSeed(
|
|
49
|
-
seed: Seed,
|
|
50
|
-
spec: import("@bcts/sskr").Spec,
|
|
51
|
-
format: SSKRFormatKey,
|
|
52
|
-
): string {
|
|
54
|
+
function outputSskrSeed(seed: Seed, spec: Spec, format: SSKRFormatKey): string {
|
|
53
55
|
switch (format) {
|
|
54
|
-
case
|
|
55
|
-
const
|
|
56
|
+
case SSKRFormatKey.Envelope: {
|
|
57
|
+
const seedEnvelope = seed.toEnvelope();
|
|
56
58
|
const contentKey = SymmetricKey.new();
|
|
57
|
-
const encryptedEnvelope =
|
|
58
|
-
|
|
59
|
-
const
|
|
59
|
+
const encryptedEnvelope = seedEnvelope.wrap().encryptSubject(contentKey);
|
|
60
|
+
// Use type assertion for prototype extension method
|
|
61
|
+
const shareEnvelopes: Envelope[] = (
|
|
62
|
+
encryptedEnvelope as unknown as {
|
|
63
|
+
sskrSplitFlattened: (spec: Spec, key: SymmetricKey) => Envelope[];
|
|
64
|
+
}
|
|
65
|
+
).sskrSplitFlattened(spec, contentKey);
|
|
66
|
+
|
|
67
|
+
const shareEnvelopesStrings: string[] = shareEnvelopes.map((env: Envelope) => env.urString());
|
|
60
68
|
return shareEnvelopesStrings.join("\n");
|
|
61
69
|
}
|
|
62
|
-
case
|
|
70
|
+
case SSKRFormatKey.Btw: {
|
|
63
71
|
return makeBytewordsShares(spec, seed, BytewordsStyle.Standard);
|
|
64
72
|
}
|
|
65
|
-
case
|
|
73
|
+
case SSKRFormatKey.Btwm: {
|
|
66
74
|
return makeBytewordsShares(spec, seed, BytewordsStyle.Minimal);
|
|
67
75
|
}
|
|
68
|
-
case
|
|
76
|
+
case SSKRFormatKey.Btwu: {
|
|
69
77
|
return makeBytewordsShares(spec, seed, BytewordsStyle.Uri);
|
|
70
78
|
}
|
|
71
|
-
case
|
|
79
|
+
case SSKRFormatKey.Ur: {
|
|
72
80
|
const shares = makeShares(spec, seed);
|
|
73
|
-
const urStrings = shares.map((share) => {
|
|
74
|
-
const ur = UR.
|
|
81
|
+
const urStrings: string[] = shares.map((share) => {
|
|
82
|
+
const ur = UR.new("sskr", toByteString(share.asBytes()));
|
|
75
83
|
return ur.toString();
|
|
76
84
|
});
|
|
77
85
|
return urStrings.join("\n");
|
|
@@ -79,7 +87,7 @@ function outputSskrSeed(
|
|
|
79
87
|
}
|
|
80
88
|
}
|
|
81
89
|
|
|
82
|
-
function makeShares(spec:
|
|
90
|
+
function makeShares(spec: Spec, seed: Seed): SSKRShareCbor[] {
|
|
83
91
|
const secret = SSKRSecret.new(seed.data());
|
|
84
92
|
const shareGroups = sskrGenerate(spec, secret);
|
|
85
93
|
const flatShares: SSKRShareCbor[] = [];
|
|
@@ -91,11 +99,7 @@ function makeShares(spec: import("@bcts/sskr").Spec, seed: Seed): SSKRShareCbor[
|
|
|
91
99
|
return flatShares;
|
|
92
100
|
}
|
|
93
101
|
|
|
94
|
-
function makeBytewordsShares(
|
|
95
|
-
spec: import("@bcts/sskr").Spec,
|
|
96
|
-
seed: Seed,
|
|
97
|
-
style: BytewordsStyle,
|
|
98
|
-
): string {
|
|
102
|
+
function makeBytewordsShares(spec: Spec, seed: Seed, style: BytewordsStyle): string {
|
|
99
103
|
const shares = makeShares(spec, seed);
|
|
100
104
|
const cborShares = shares.map((share) =>
|
|
101
105
|
toTaggedValue(SSKR_SHARE.value, toByteString(share.asBytes())),
|
|
@@ -125,7 +129,11 @@ function parseEnvelopes(input: string): Seed | null {
|
|
|
125
129
|
return null;
|
|
126
130
|
}
|
|
127
131
|
|
|
128
|
-
|
|
132
|
+
// Use type assertion for static prototype extension method
|
|
133
|
+
const sskrJoin = (Envelope as unknown as { sskrJoin: (envelopes: Envelope[]) => Envelope })
|
|
134
|
+
.sskrJoin;
|
|
135
|
+
const recoveredEnvelope: Envelope = sskrJoin(shareEnvelopes).unwrap();
|
|
136
|
+
|
|
129
137
|
return Seed.fromEnvelope(recoveredEnvelope);
|
|
130
138
|
} catch {
|
|
131
139
|
return null;
|
|
@@ -151,8 +159,9 @@ function fromTaggedCborShares(taggedCborDataShares: Uint8Array[]): Seed | null {
|
|
|
151
159
|
if (tagged.tag !== SSKR_SHARE.value && tagged.tag !== SSKR_SHARE_V1.value) {
|
|
152
160
|
return null;
|
|
153
161
|
}
|
|
154
|
-
const content =
|
|
155
|
-
|
|
162
|
+
const content =
|
|
163
|
+
tagged.value !== undefined ? (tagged.value as ReturnType<typeof decodeCbor>) : cbor;
|
|
164
|
+
const bytes = expectBytes(content);
|
|
156
165
|
untaggedShares.push(bytes);
|
|
157
166
|
}
|
|
158
167
|
return fromUntaggedCborShares(untaggedShares);
|
|
@@ -214,7 +223,7 @@ function parseUr(input: string, expectedTagValue: number, allowTaggedCbor: boole
|
|
|
214
223
|
|
|
215
224
|
// Ensure every UR is of the expected type
|
|
216
225
|
for (const ur of urs) {
|
|
217
|
-
if (ur.
|
|
226
|
+
if (ur.urTypeStr() !== expectedType) {
|
|
218
227
|
return null;
|
|
219
228
|
}
|
|
220
229
|
}
|
|
@@ -275,13 +284,13 @@ function parseSskrSeed(input: string): Seed {
|
|
|
275
284
|
}
|
|
276
285
|
|
|
277
286
|
// Try UR format (current tag)
|
|
278
|
-
const urResult = parseUr(input, SSKR_SHARE.value, false);
|
|
287
|
+
const urResult = parseUr(input, Number(SSKR_SHARE.value), false);
|
|
279
288
|
if (urResult !== null) {
|
|
280
289
|
return urResult;
|
|
281
290
|
}
|
|
282
291
|
|
|
283
292
|
// Try legacy UR format (v1 tag, allow tagged cbor)
|
|
284
|
-
const urLegacyResult = parseUr(input, SSKR_SHARE_V1.value, true);
|
|
293
|
+
const urLegacyResult = parseUr(input, Number(SSKR_SHARE_V1.value), true);
|
|
285
294
|
if (urLegacyResult !== null) {
|
|
286
295
|
return urLegacyResult;
|
|
287
296
|
}
|
package/src/main.ts
CHANGED
|
@@ -9,10 +9,30 @@ import { SSKRGroupSpec } from "@bcts/components";
|
|
|
9
9
|
import { Cli, type InputFormatKey, type OutputFormatKey, type SSKRFormatKey } from "./cli.js";
|
|
10
10
|
import { selectInputFormat, selectOutputFormat } from "./formats/index.js";
|
|
11
11
|
import { DeterministicRandomNumberGenerator } from "./random.js";
|
|
12
|
-
import
|
|
12
|
+
import fs from "node:fs";
|
|
13
13
|
|
|
14
14
|
const VERSION = "0.4.0";
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* CLI options parsed from commander.
|
|
18
|
+
*/
|
|
19
|
+
interface CliOptions {
|
|
20
|
+
count: string;
|
|
21
|
+
in: InputFormatKey;
|
|
22
|
+
out: OutputFormatKey;
|
|
23
|
+
low: number;
|
|
24
|
+
high: number;
|
|
25
|
+
name?: string;
|
|
26
|
+
note?: string;
|
|
27
|
+
date?: string;
|
|
28
|
+
maxFragmentLen: string;
|
|
29
|
+
additionalParts: string;
|
|
30
|
+
groups: SSKRGroupSpec[];
|
|
31
|
+
groupThreshold: number;
|
|
32
|
+
sskrFormat: SSKRFormatKey;
|
|
33
|
+
deterministic?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
16
36
|
function parseLowInt(value: string): number {
|
|
17
37
|
const num = parseInt(value, 10);
|
|
18
38
|
if (isNaN(num) || num < 0 || num > 254) {
|
|
@@ -53,7 +73,7 @@ function parseGroupSpec(value: string, previous: SSKRGroupSpec[]): SSKRGroupSpec
|
|
|
53
73
|
return [...previous, spec];
|
|
54
74
|
}
|
|
55
75
|
|
|
56
|
-
|
|
76
|
+
function main(): void {
|
|
57
77
|
const program = new Command();
|
|
58
78
|
|
|
59
79
|
program
|
|
@@ -159,7 +179,7 @@ async function main(): Promise<void> {
|
|
|
159
179
|
|
|
160
180
|
program.parse();
|
|
161
181
|
|
|
162
|
-
const options = program.opts();
|
|
182
|
+
const options = program.opts<CliOptions>();
|
|
163
183
|
const args = program.args;
|
|
164
184
|
|
|
165
185
|
// Create the CLI state
|
|
@@ -168,30 +188,30 @@ async function main(): Promise<void> {
|
|
|
168
188
|
// Set input from argument or stdin
|
|
169
189
|
if (args.length > 0) {
|
|
170
190
|
cli.input = args[0];
|
|
171
|
-
} else if (
|
|
191
|
+
} else if (process.stdin.isTTY !== true) {
|
|
172
192
|
// Read from stdin if it's piped
|
|
173
|
-
cli.input = fs.readFileSync(
|
|
193
|
+
cli.input = fs.readFileSync(process.stdin.fd, "utf-8").trim();
|
|
174
194
|
}
|
|
175
195
|
|
|
176
196
|
// Set options
|
|
177
197
|
cli.count = parseInt(options.count, 10);
|
|
178
|
-
cli.in = options.in
|
|
179
|
-
cli.out = options.out
|
|
198
|
+
cli.in = options.in;
|
|
199
|
+
cli.out = options.out;
|
|
180
200
|
cli.low = options.low;
|
|
181
201
|
cli.high = options.high;
|
|
182
|
-
cli.name = options.name;
|
|
183
|
-
cli.note = options.note;
|
|
184
|
-
if (options.date) {
|
|
202
|
+
if (options.name !== undefined) cli.name = options.name;
|
|
203
|
+
if (options.note !== undefined) cli.note = options.note;
|
|
204
|
+
if (options.date !== undefined) {
|
|
185
205
|
cli.date = parseCliDate(options.date);
|
|
186
206
|
}
|
|
187
207
|
cli.maxFragmentLen = parseInt(options.maxFragmentLen, 10);
|
|
188
208
|
cli.additionalParts = parseInt(options.additionalParts, 10);
|
|
189
209
|
cli.groups = options.groups;
|
|
190
210
|
cli.groupThreshold = options.groupThreshold;
|
|
191
|
-
cli.sskrFormat = options.sskrFormat
|
|
211
|
+
cli.sskrFormat = options.sskrFormat;
|
|
192
212
|
|
|
193
213
|
// Set up RNG
|
|
194
|
-
if (options.deterministic) {
|
|
214
|
+
if (options.deterministic !== undefined) {
|
|
195
215
|
cli.rng = {
|
|
196
216
|
type: "deterministic",
|
|
197
217
|
rng: DeterministicRandomNumberGenerator.newWithSeed(options.deterministic),
|
|
@@ -210,7 +230,7 @@ async function main(): Promise<void> {
|
|
|
210
230
|
// Validate round-trippability
|
|
211
231
|
if (!outputFormat.roundTrippable() && inputFormat.name() !== "random") {
|
|
212
232
|
console.error(`Input for output form "${outputFormat.name()}" must be random.`);
|
|
213
|
-
|
|
233
|
+
throw new Error("Invalid input format for non-round-trippable output format");
|
|
214
234
|
}
|
|
215
235
|
|
|
216
236
|
// Process input
|
|
@@ -221,7 +241,10 @@ async function main(): Promise<void> {
|
|
|
221
241
|
console.log(output);
|
|
222
242
|
}
|
|
223
243
|
|
|
224
|
-
|
|
225
|
-
|
|
244
|
+
try {
|
|
245
|
+
main();
|
|
246
|
+
} catch (error: unknown) {
|
|
247
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
248
|
+
console.error(message);
|
|
226
249
|
process.exit(1);
|
|
227
|
-
}
|
|
250
|
+
}
|
package/src/random.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Ported from seedtool-cli-rust/src/random.rs
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { sha256 } from "@noble/hashes/
|
|
7
|
-
import { hkdf } from "@noble/hashes/hkdf";
|
|
6
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
7
|
+
import { hkdf } from "@noble/hashes/hkdf.js";
|
|
8
8
|
|
|
9
9
|
/** SHA256 output size in bytes */
|
|
10
10
|
const SHA256_SIZE = 32;
|
|
@@ -17,7 +17,7 @@ const SHA256_SIZE = 32;
|
|
|
17
17
|
* from a seed, with an incrementing salt for each call.
|
|
18
18
|
*/
|
|
19
19
|
export class DeterministicRandomNumberGenerator {
|
|
20
|
-
private seed: Uint8Array;
|
|
20
|
+
private readonly seed: Uint8Array;
|
|
21
21
|
private salt: bigint;
|
|
22
22
|
|
|
23
23
|
/**
|
package/src/seed.ts
CHANGED
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
import { Envelope } from "@bcts/envelope";
|
|
12
12
|
import { Seed as ComponentsSeed } from "@bcts/components";
|
|
13
13
|
import { NAME, NOTE, DATE, SEED_TYPE } from "@bcts/known-values";
|
|
14
|
-
import {
|
|
14
|
+
import { CborDate } from "@bcts/dcbor";
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Seed with optional metadata.
|
|
18
18
|
* Matches Rust Seed struct in seed.rs.
|
|
19
19
|
*/
|
|
20
20
|
export class Seed {
|
|
21
|
-
private _data: Uint8Array;
|
|
21
|
+
private readonly _data: Uint8Array;
|
|
22
22
|
private _name: string;
|
|
23
23
|
private _note: string;
|
|
24
|
-
private _creationDate
|
|
24
|
+
private _creationDate: Date | undefined;
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Create a new Seed with the given data.
|
|
@@ -144,7 +144,7 @@ export class Seed {
|
|
|
144
144
|
*/
|
|
145
145
|
toEnvelope(): Envelope {
|
|
146
146
|
// Create envelope with seed data as byte string subject
|
|
147
|
-
let envelope = Envelope.new(
|
|
147
|
+
let envelope = Envelope.new(this._data);
|
|
148
148
|
|
|
149
149
|
// Add type assertion
|
|
150
150
|
envelope = envelope.addType(SEED_TYPE);
|
package/src/styles.ts
CHANGED