@bcts/provenance-mark 1.0.0-alpha.10
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 +48 -0
- package/README.md +15 -0
- package/dist/index.cjs +1703 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +711 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +711 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.iife.js +1700 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.mjs +1657 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +78 -0
- package/src/crypto-utils.ts +75 -0
- package/src/date.ts +216 -0
- package/src/error.ts +141 -0
- package/src/generator.ts +182 -0
- package/src/index.ts +90 -0
- package/src/mark-info.ts +126 -0
- package/src/mark.ts +597 -0
- package/src/resolution.ts +294 -0
- package/src/rng-state.ts +68 -0
- package/src/seed.ts +98 -0
- package/src/utils.ts +103 -0
- package/src/validate.ts +449 -0
- package/src/xoshiro256starstar.ts +150 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1657 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { cbor, cborData, decodeCbor, expectArray, expectBytes, expectUnsigned } from "@bcts/dcbor";
|
|
3
|
+
import { sha256 as sha256$1 } from "@noble/hashes/sha2.js";
|
|
4
|
+
import { hkdf } from "@noble/hashes/hkdf.js";
|
|
5
|
+
import { chacha20 } from "@noble/ciphers/chacha.js";
|
|
6
|
+
import { randomData } from "@bcts/rand";
|
|
7
|
+
import { PROVENANCE_MARK } from "@bcts/tags";
|
|
8
|
+
import { BytewordsStyle, UR, decodeBytewords, encodeBytemojisIdentifier, encodeBytewords, encodeBytewordsIdentifier } from "@bcts/uniform-resources";
|
|
9
|
+
|
|
10
|
+
//#region rolldown:runtime
|
|
11
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/error.ts
|
|
15
|
+
/**
|
|
16
|
+
* Error types for Provenance Mark operations.
|
|
17
|
+
*/
|
|
18
|
+
let ProvenanceMarkErrorType = /* @__PURE__ */ function(ProvenanceMarkErrorType$1) {
|
|
19
|
+
/** Invalid Seed length */
|
|
20
|
+
ProvenanceMarkErrorType$1["InvalidSeedLength"] = "InvalidSeedLength";
|
|
21
|
+
/** Duplicate key */
|
|
22
|
+
ProvenanceMarkErrorType$1["DuplicateKey"] = "DuplicateKey";
|
|
23
|
+
/** Missing key */
|
|
24
|
+
ProvenanceMarkErrorType$1["MissingKey"] = "MissingKey";
|
|
25
|
+
/** Invalid key */
|
|
26
|
+
ProvenanceMarkErrorType$1["InvalidKey"] = "InvalidKey";
|
|
27
|
+
/** Extra keys */
|
|
28
|
+
ProvenanceMarkErrorType$1["ExtraKeys"] = "ExtraKeys";
|
|
29
|
+
/** Invalid key length for the given resolution */
|
|
30
|
+
ProvenanceMarkErrorType$1["InvalidKeyLength"] = "InvalidKeyLength";
|
|
31
|
+
/** Invalid next key length for the given resolution */
|
|
32
|
+
ProvenanceMarkErrorType$1["InvalidNextKeyLength"] = "InvalidNextKeyLength";
|
|
33
|
+
/** Invalid chain ID length for the given resolution */
|
|
34
|
+
ProvenanceMarkErrorType$1["InvalidChainIdLength"] = "InvalidChainIdLength";
|
|
35
|
+
/** Invalid message length for the given resolution */
|
|
36
|
+
ProvenanceMarkErrorType$1["InvalidMessageLength"] = "InvalidMessageLength";
|
|
37
|
+
/** Invalid CBOR data in info field */
|
|
38
|
+
ProvenanceMarkErrorType$1["InvalidInfoCbor"] = "InvalidInfoCbor";
|
|
39
|
+
/** Date out of range for serialization */
|
|
40
|
+
ProvenanceMarkErrorType$1["DateOutOfRange"] = "DateOutOfRange";
|
|
41
|
+
/** Invalid date components */
|
|
42
|
+
ProvenanceMarkErrorType$1["InvalidDate"] = "InvalidDate";
|
|
43
|
+
/** Missing required URL parameter */
|
|
44
|
+
ProvenanceMarkErrorType$1["MissingUrlParameter"] = "MissingUrlParameter";
|
|
45
|
+
/** Year out of range for 2-byte serialization */
|
|
46
|
+
ProvenanceMarkErrorType$1["YearOutOfRange"] = "YearOutOfRange";
|
|
47
|
+
/** Invalid month or day */
|
|
48
|
+
ProvenanceMarkErrorType$1["InvalidMonthOrDay"] = "InvalidMonthOrDay";
|
|
49
|
+
/** Resolution serialization error */
|
|
50
|
+
ProvenanceMarkErrorType$1["ResolutionError"] = "ResolutionError";
|
|
51
|
+
/** Bytewords encoding/decoding error */
|
|
52
|
+
ProvenanceMarkErrorType$1["BytewordsError"] = "BytewordsError";
|
|
53
|
+
/** CBOR encoding/decoding error */
|
|
54
|
+
ProvenanceMarkErrorType$1["CborError"] = "CborError";
|
|
55
|
+
/** URL parsing error */
|
|
56
|
+
ProvenanceMarkErrorType$1["UrlError"] = "UrlError";
|
|
57
|
+
/** Base64 decoding error */
|
|
58
|
+
ProvenanceMarkErrorType$1["Base64Error"] = "Base64Error";
|
|
59
|
+
/** JSON serialization error */
|
|
60
|
+
ProvenanceMarkErrorType$1["JsonError"] = "JsonError";
|
|
61
|
+
/** Integer conversion error */
|
|
62
|
+
ProvenanceMarkErrorType$1["IntegerConversionError"] = "IntegerConversionError";
|
|
63
|
+
/** Validation error */
|
|
64
|
+
ProvenanceMarkErrorType$1["ValidationError"] = "ValidationError";
|
|
65
|
+
return ProvenanceMarkErrorType$1;
|
|
66
|
+
}({});
|
|
67
|
+
/**
|
|
68
|
+
* Error class for Provenance Mark operations.
|
|
69
|
+
*/
|
|
70
|
+
var ProvenanceMarkError = class ProvenanceMarkError extends Error {
|
|
71
|
+
type;
|
|
72
|
+
details;
|
|
73
|
+
constructor(type, message, details) {
|
|
74
|
+
const fullMessage = message !== void 0 && message !== "" ? `${type}: ${message}` : ProvenanceMarkError.defaultMessage(type, details);
|
|
75
|
+
super(fullMessage);
|
|
76
|
+
this.name = "ProvenanceMarkError";
|
|
77
|
+
this.type = type;
|
|
78
|
+
this.details = details;
|
|
79
|
+
}
|
|
80
|
+
static defaultMessage(type, details) {
|
|
81
|
+
const d = (key) => {
|
|
82
|
+
const value = details?.[key];
|
|
83
|
+
if (value === void 0 || value === null) return "?";
|
|
84
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return String(value);
|
|
85
|
+
return JSON.stringify(value);
|
|
86
|
+
};
|
|
87
|
+
switch (type) {
|
|
88
|
+
case ProvenanceMarkErrorType.InvalidSeedLength: return `invalid seed length: expected 32 bytes, got ${d("actual")} bytes`;
|
|
89
|
+
case ProvenanceMarkErrorType.DuplicateKey: return `duplicate key: ${d("key")}`;
|
|
90
|
+
case ProvenanceMarkErrorType.MissingKey: return `missing key: ${d("key")}`;
|
|
91
|
+
case ProvenanceMarkErrorType.InvalidKey: return `invalid key: ${d("key")}`;
|
|
92
|
+
case ProvenanceMarkErrorType.ExtraKeys: return `wrong number of keys: expected ${d("expected")}, got ${d("actual")}`;
|
|
93
|
+
case ProvenanceMarkErrorType.InvalidKeyLength: return `invalid key length: expected ${d("expected")}, got ${d("actual")}`;
|
|
94
|
+
case ProvenanceMarkErrorType.InvalidNextKeyLength: return `invalid next key length: expected ${d("expected")}, got ${d("actual")}`;
|
|
95
|
+
case ProvenanceMarkErrorType.InvalidChainIdLength: return `invalid chain ID length: expected ${d("expected")}, got ${d("actual")}`;
|
|
96
|
+
case ProvenanceMarkErrorType.InvalidMessageLength: return `invalid message length: expected at least ${d("expected")}, got ${d("actual")}`;
|
|
97
|
+
case ProvenanceMarkErrorType.InvalidInfoCbor: return "invalid CBOR data in info field";
|
|
98
|
+
case ProvenanceMarkErrorType.DateOutOfRange: return `date out of range: ${d("details")}`;
|
|
99
|
+
case ProvenanceMarkErrorType.InvalidDate: return `invalid date: ${d("details")}`;
|
|
100
|
+
case ProvenanceMarkErrorType.MissingUrlParameter: return `missing required URL parameter: ${d("parameter")}`;
|
|
101
|
+
case ProvenanceMarkErrorType.YearOutOfRange: return `year out of range for 2-byte serialization: must be between 2023-2150, got ${d("year")}`;
|
|
102
|
+
case ProvenanceMarkErrorType.InvalidMonthOrDay: return `invalid month (${d("month")}) or day (${d("day")}) for year ${d("year")}`;
|
|
103
|
+
case ProvenanceMarkErrorType.ResolutionError: return `resolution serialization error: ${d("details")}`;
|
|
104
|
+
case ProvenanceMarkErrorType.BytewordsError: return `bytewords error: ${d("message")}`;
|
|
105
|
+
case ProvenanceMarkErrorType.CborError: return `CBOR error: ${d("message")}`;
|
|
106
|
+
case ProvenanceMarkErrorType.UrlError: return `URL parsing error: ${d("message")}`;
|
|
107
|
+
case ProvenanceMarkErrorType.Base64Error: return `base64 decoding error: ${d("message")}`;
|
|
108
|
+
case ProvenanceMarkErrorType.JsonError: return `JSON error: ${d("message")}`;
|
|
109
|
+
case ProvenanceMarkErrorType.IntegerConversionError: return `integer conversion error: ${d("message")}`;
|
|
110
|
+
case ProvenanceMarkErrorType.ValidationError: return `validation error: ${d("message")}`;
|
|
111
|
+
default: return type;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/date.ts
|
|
118
|
+
/**
|
|
119
|
+
* Reference date for 4-byte and 6-byte serialization (2001-01-01T00:00:00Z).
|
|
120
|
+
*/
|
|
121
|
+
const REFERENCE_DATE = Date.UTC(2001, 0, 1, 0, 0, 0, 0);
|
|
122
|
+
/**
|
|
123
|
+
* Maximum value for 6-byte millisecond representation.
|
|
124
|
+
*/
|
|
125
|
+
const MAX_6_BYTE_VALUE = 0xe5940a78a7ff;
|
|
126
|
+
/**
|
|
127
|
+
* Get the number of days in a month.
|
|
128
|
+
*/
|
|
129
|
+
function daysInMonth(year, month) {
|
|
130
|
+
return new Date(year, month, 0).getDate();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Check if a day is valid for a given year and month.
|
|
134
|
+
*/
|
|
135
|
+
function isValidDay(year, month, day) {
|
|
136
|
+
if (day < 1) return false;
|
|
137
|
+
return day <= daysInMonth(year, month);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Serialize a date to 2 bytes (year + month + day only, day precision).
|
|
141
|
+
* Year range: 2023-2150 (128 years)
|
|
142
|
+
* Format: YYYYYYY MMMM DDDDD (7 bits year offset, 4 bits month, 5 bits day)
|
|
143
|
+
*/
|
|
144
|
+
function serialize2Bytes(date) {
|
|
145
|
+
const year = date.getUTCFullYear();
|
|
146
|
+
const month = date.getUTCMonth() + 1;
|
|
147
|
+
const day = date.getUTCDate();
|
|
148
|
+
const yy = year - 2023;
|
|
149
|
+
if (yy < 0 || yy >= 128) throw new ProvenanceMarkError(ProvenanceMarkErrorType.YearOutOfRange, void 0, { year });
|
|
150
|
+
if (month < 1 || month > 12 || day < 1 || day > 31) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidMonthOrDay, void 0, {
|
|
151
|
+
year,
|
|
152
|
+
month,
|
|
153
|
+
day
|
|
154
|
+
});
|
|
155
|
+
const value = yy << 9 | month << 5 | day;
|
|
156
|
+
const buf = new Uint8Array(2);
|
|
157
|
+
buf[0] = value >> 8 & 255;
|
|
158
|
+
buf[1] = value & 255;
|
|
159
|
+
return buf;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Deserialize 2 bytes to a date.
|
|
163
|
+
*/
|
|
164
|
+
function deserialize2Bytes(bytes) {
|
|
165
|
+
if (bytes.length !== 2) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidDate, void 0, { details: `expected 2 bytes, got ${bytes.length}` });
|
|
166
|
+
const value = bytes[0] << 8 | bytes[1];
|
|
167
|
+
const day = value & 31;
|
|
168
|
+
const month = value >> 5 & 15;
|
|
169
|
+
const year = (value >> 9 & 127) + 2023;
|
|
170
|
+
if (month < 1 || month > 12 || !isValidDay(year, month, day)) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidMonthOrDay, void 0, {
|
|
171
|
+
year,
|
|
172
|
+
month,
|
|
173
|
+
day
|
|
174
|
+
});
|
|
175
|
+
return new Date(Date.UTC(year, month - 1, day, 0, 0, 0, 0));
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Serialize a date to 4 bytes (seconds since 2001-01-01).
|
|
179
|
+
*/
|
|
180
|
+
function serialize4Bytes(date) {
|
|
181
|
+
const duration = date.getTime() - REFERENCE_DATE;
|
|
182
|
+
const seconds = Math.floor(duration / 1e3);
|
|
183
|
+
if (seconds < 0 || seconds > 4294967295) throw new ProvenanceMarkError(ProvenanceMarkErrorType.DateOutOfRange, void 0, { details: "seconds value out of range for u32" });
|
|
184
|
+
const buf = new Uint8Array(4);
|
|
185
|
+
buf[0] = seconds >> 24 & 255;
|
|
186
|
+
buf[1] = seconds >> 16 & 255;
|
|
187
|
+
buf[2] = seconds >> 8 & 255;
|
|
188
|
+
buf[3] = seconds & 255;
|
|
189
|
+
return buf;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Deserialize 4 bytes to a date.
|
|
193
|
+
*/
|
|
194
|
+
function deserialize4Bytes(bytes) {
|
|
195
|
+
if (bytes.length !== 4) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidDate, void 0, { details: `expected 4 bytes, got ${bytes.length}` });
|
|
196
|
+
const seconds = (bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]) >>> 0;
|
|
197
|
+
return new Date(REFERENCE_DATE + seconds * 1e3);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Serialize a date to 6 bytes (milliseconds since 2001-01-01).
|
|
201
|
+
*/
|
|
202
|
+
function serialize6Bytes(date) {
|
|
203
|
+
const duration = date.getTime() - REFERENCE_DATE;
|
|
204
|
+
const milliseconds = BigInt(duration);
|
|
205
|
+
if (milliseconds < 0n || milliseconds > BigInt(MAX_6_BYTE_VALUE)) throw new ProvenanceMarkError(ProvenanceMarkErrorType.DateOutOfRange, void 0, { details: "date exceeds maximum representable value" });
|
|
206
|
+
const buf = new Uint8Array(6);
|
|
207
|
+
buf[0] = Number(milliseconds >> 40n & 255n);
|
|
208
|
+
buf[1] = Number(milliseconds >> 32n & 255n);
|
|
209
|
+
buf[2] = Number(milliseconds >> 24n & 255n);
|
|
210
|
+
buf[3] = Number(milliseconds >> 16n & 255n);
|
|
211
|
+
buf[4] = Number(milliseconds >> 8n & 255n);
|
|
212
|
+
buf[5] = Number(milliseconds & 255n);
|
|
213
|
+
return buf;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Deserialize 6 bytes to a date.
|
|
217
|
+
*/
|
|
218
|
+
function deserialize6Bytes(bytes) {
|
|
219
|
+
if (bytes.length !== 6) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidDate, void 0, { details: `expected 6 bytes, got ${bytes.length}` });
|
|
220
|
+
const milliseconds = BigInt(bytes[0]) << 40n | BigInt(bytes[1]) << 32n | BigInt(bytes[2]) << 24n | BigInt(bytes[3]) << 16n | BigInt(bytes[4]) << 8n | BigInt(bytes[5]);
|
|
221
|
+
if (milliseconds > BigInt(MAX_6_BYTE_VALUE)) throw new ProvenanceMarkError(ProvenanceMarkErrorType.DateOutOfRange, void 0, { details: "date exceeds maximum representable value" });
|
|
222
|
+
return new Date(REFERENCE_DATE + Number(milliseconds));
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Get the range of valid days in a month.
|
|
226
|
+
*/
|
|
227
|
+
function rangeOfDaysInMonth(year, month) {
|
|
228
|
+
return {
|
|
229
|
+
min: 1,
|
|
230
|
+
max: daysInMonth(year, month)
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Format a date as ISO8601 string.
|
|
235
|
+
*/
|
|
236
|
+
function dateToIso8601(date) {
|
|
237
|
+
return date.toISOString();
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Parse an ISO8601 string to a Date.
|
|
241
|
+
*/
|
|
242
|
+
function dateFromIso8601(str) {
|
|
243
|
+
const date = new Date(str);
|
|
244
|
+
if (isNaN(date.getTime())) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidDate, void 0, { details: `cannot parse date: ${str}` });
|
|
245
|
+
return date;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Format a date as a simple date string (YYYY-MM-DD).
|
|
249
|
+
*/
|
|
250
|
+
function dateToDateString(date) {
|
|
251
|
+
return `${date.getUTCFullYear()}-${(date.getUTCMonth() + 1).toString().padStart(2, "0")}-${date.getUTCDate().toString().padStart(2, "0")}`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
//#endregion
|
|
255
|
+
//#region src/resolution.ts
|
|
256
|
+
/**
|
|
257
|
+
* Resolution levels for provenance marks.
|
|
258
|
+
* Higher resolution provides more security but larger mark sizes.
|
|
259
|
+
*/
|
|
260
|
+
let ProvenanceMarkResolution = /* @__PURE__ */ function(ProvenanceMarkResolution$1) {
|
|
261
|
+
ProvenanceMarkResolution$1[ProvenanceMarkResolution$1["Low"] = 0] = "Low";
|
|
262
|
+
ProvenanceMarkResolution$1[ProvenanceMarkResolution$1["Medium"] = 1] = "Medium";
|
|
263
|
+
ProvenanceMarkResolution$1[ProvenanceMarkResolution$1["Quartile"] = 2] = "Quartile";
|
|
264
|
+
ProvenanceMarkResolution$1[ProvenanceMarkResolution$1["High"] = 3] = "High";
|
|
265
|
+
return ProvenanceMarkResolution$1;
|
|
266
|
+
}({});
|
|
267
|
+
/**
|
|
268
|
+
* Convert a resolution to its numeric value.
|
|
269
|
+
*/
|
|
270
|
+
function resolutionToNumber(res) {
|
|
271
|
+
return res;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Create a resolution from a numeric value.
|
|
275
|
+
*/
|
|
276
|
+
function resolutionFromNumber(value) {
|
|
277
|
+
switch (value) {
|
|
278
|
+
case 0: return ProvenanceMarkResolution.Low;
|
|
279
|
+
case 1: return ProvenanceMarkResolution.Medium;
|
|
280
|
+
case 2: return ProvenanceMarkResolution.Quartile;
|
|
281
|
+
case 3: return ProvenanceMarkResolution.High;
|
|
282
|
+
default: throw new ProvenanceMarkError(ProvenanceMarkErrorType.ResolutionError, void 0, { details: `invalid provenance mark resolution value: ${value}` });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get the link length (key/hash/chainID length) for a resolution.
|
|
287
|
+
*/
|
|
288
|
+
function linkLength(res) {
|
|
289
|
+
switch (res) {
|
|
290
|
+
case ProvenanceMarkResolution.Low: return 4;
|
|
291
|
+
case ProvenanceMarkResolution.Medium: return 8;
|
|
292
|
+
case ProvenanceMarkResolution.Quartile: return 16;
|
|
293
|
+
case ProvenanceMarkResolution.High: return 32;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Get the sequence bytes length for a resolution.
|
|
298
|
+
*/
|
|
299
|
+
function seqBytesLength(res) {
|
|
300
|
+
switch (res) {
|
|
301
|
+
case ProvenanceMarkResolution.Low: return 2;
|
|
302
|
+
case ProvenanceMarkResolution.Medium:
|
|
303
|
+
case ProvenanceMarkResolution.Quartile:
|
|
304
|
+
case ProvenanceMarkResolution.High: return 4;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Get the date bytes length for a resolution.
|
|
309
|
+
*/
|
|
310
|
+
function dateBytesLength(res) {
|
|
311
|
+
switch (res) {
|
|
312
|
+
case ProvenanceMarkResolution.Low: return 2;
|
|
313
|
+
case ProvenanceMarkResolution.Medium: return 4;
|
|
314
|
+
case ProvenanceMarkResolution.Quartile:
|
|
315
|
+
case ProvenanceMarkResolution.High: return 6;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Get the fixed-length portion size for a resolution.
|
|
320
|
+
*/
|
|
321
|
+
function fixedLength(res) {
|
|
322
|
+
return linkLength(res) * 3 + seqBytesLength(res) + dateBytesLength(res);
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Get the key range for a resolution.
|
|
326
|
+
*/
|
|
327
|
+
function keyRange(res) {
|
|
328
|
+
return {
|
|
329
|
+
start: 0,
|
|
330
|
+
end: linkLength(res)
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Get the chain ID range for a resolution.
|
|
335
|
+
*/
|
|
336
|
+
function chainIdRange(res) {
|
|
337
|
+
return {
|
|
338
|
+
start: 0,
|
|
339
|
+
end: linkLength(res)
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Get the hash range for a resolution.
|
|
344
|
+
*/
|
|
345
|
+
function hashRange(res) {
|
|
346
|
+
const chainIdEnd = chainIdRange(res).end;
|
|
347
|
+
return {
|
|
348
|
+
start: chainIdEnd,
|
|
349
|
+
end: chainIdEnd + linkLength(res)
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Get the sequence bytes range for a resolution.
|
|
354
|
+
*/
|
|
355
|
+
function seqBytesRange(res) {
|
|
356
|
+
const hashEnd = hashRange(res).end;
|
|
357
|
+
return {
|
|
358
|
+
start: hashEnd,
|
|
359
|
+
end: hashEnd + seqBytesLength(res)
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Get the date bytes range for a resolution.
|
|
364
|
+
*/
|
|
365
|
+
function dateBytesRange(res) {
|
|
366
|
+
const seqEnd = seqBytesRange(res).end;
|
|
367
|
+
return {
|
|
368
|
+
start: seqEnd,
|
|
369
|
+
end: seqEnd + dateBytesLength(res)
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Get the info range start for a resolution.
|
|
374
|
+
*/
|
|
375
|
+
function infoRangeStart(res) {
|
|
376
|
+
return dateBytesRange(res).end;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Serialize a Date into bytes based on the resolution.
|
|
380
|
+
*/
|
|
381
|
+
function serializeDate(res, date) {
|
|
382
|
+
switch (res) {
|
|
383
|
+
case ProvenanceMarkResolution.Low: return serialize2Bytes(date);
|
|
384
|
+
case ProvenanceMarkResolution.Medium: return serialize4Bytes(date);
|
|
385
|
+
case ProvenanceMarkResolution.Quartile:
|
|
386
|
+
case ProvenanceMarkResolution.High: return serialize6Bytes(date);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Deserialize bytes into a Date based on the resolution.
|
|
391
|
+
*/
|
|
392
|
+
function deserializeDate(res, data) {
|
|
393
|
+
switch (res) {
|
|
394
|
+
case ProvenanceMarkResolution.Low:
|
|
395
|
+
if (data.length !== 2) throw new ProvenanceMarkError(ProvenanceMarkErrorType.ResolutionError, void 0, { details: `invalid date length: expected 2 bytes, got ${data.length}` });
|
|
396
|
+
return deserialize2Bytes(data);
|
|
397
|
+
case ProvenanceMarkResolution.Medium:
|
|
398
|
+
if (data.length !== 4) throw new ProvenanceMarkError(ProvenanceMarkErrorType.ResolutionError, void 0, { details: `invalid date length: expected 4 bytes, got ${data.length}` });
|
|
399
|
+
return deserialize4Bytes(data);
|
|
400
|
+
case ProvenanceMarkResolution.Quartile:
|
|
401
|
+
case ProvenanceMarkResolution.High:
|
|
402
|
+
if (data.length !== 6) throw new ProvenanceMarkError(ProvenanceMarkErrorType.ResolutionError, void 0, { details: `invalid date length: expected 6 bytes, got ${data.length}` });
|
|
403
|
+
return deserialize6Bytes(data);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Serialize a sequence number into bytes based on the resolution.
|
|
408
|
+
*/
|
|
409
|
+
function serializeSeq(res, seq) {
|
|
410
|
+
if (seqBytesLength(res) === 2) {
|
|
411
|
+
if (seq > 65535) throw new ProvenanceMarkError(ProvenanceMarkErrorType.ResolutionError, void 0, { details: `sequence number ${seq} out of range for 2-byte format (max 65535)` });
|
|
412
|
+
const buf = new Uint8Array(2);
|
|
413
|
+
buf[0] = seq >> 8 & 255;
|
|
414
|
+
buf[1] = seq & 255;
|
|
415
|
+
return buf;
|
|
416
|
+
} else {
|
|
417
|
+
const buf = new Uint8Array(4);
|
|
418
|
+
buf[0] = seq >> 24 & 255;
|
|
419
|
+
buf[1] = seq >> 16 & 255;
|
|
420
|
+
buf[2] = seq >> 8 & 255;
|
|
421
|
+
buf[3] = seq & 255;
|
|
422
|
+
return buf;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Deserialize bytes into a sequence number based on the resolution.
|
|
427
|
+
*/
|
|
428
|
+
function deserializeSeq(res, data) {
|
|
429
|
+
if (seqBytesLength(res) === 2) {
|
|
430
|
+
if (data.length !== 2) throw new ProvenanceMarkError(ProvenanceMarkErrorType.ResolutionError, void 0, { details: `invalid sequence number length: expected 2 bytes, got ${data.length}` });
|
|
431
|
+
return data[0] << 8 | data[1];
|
|
432
|
+
} else {
|
|
433
|
+
if (data.length !== 4) throw new ProvenanceMarkError(ProvenanceMarkErrorType.ResolutionError, void 0, { details: `invalid sequence number length: expected 4 bytes, got ${data.length}` });
|
|
434
|
+
return (data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]) >>> 0;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Get the string representation of a resolution.
|
|
439
|
+
*/
|
|
440
|
+
function resolutionToString(res) {
|
|
441
|
+
switch (res) {
|
|
442
|
+
case ProvenanceMarkResolution.Low: return "low";
|
|
443
|
+
case ProvenanceMarkResolution.Medium: return "medium";
|
|
444
|
+
case ProvenanceMarkResolution.Quartile: return "quartile";
|
|
445
|
+
case ProvenanceMarkResolution.High: return "high";
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Convert a resolution to CBOR.
|
|
450
|
+
*/
|
|
451
|
+
function resolutionToCbor(res) {
|
|
452
|
+
return cbor(res);
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Create a resolution from CBOR.
|
|
456
|
+
*/
|
|
457
|
+
function resolutionFromCbor(cborValue) {
|
|
458
|
+
const value = expectUnsigned(cborValue);
|
|
459
|
+
return resolutionFromNumber(Number(value));
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
//#endregion
|
|
463
|
+
//#region src/crypto-utils.ts
|
|
464
|
+
const SHA256_SIZE = 32;
|
|
465
|
+
/**
|
|
466
|
+
* Compute SHA-256 hash of data.
|
|
467
|
+
*/
|
|
468
|
+
function sha256(data) {
|
|
469
|
+
return sha256$1(data);
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Compute SHA-256 hash and return a prefix of the given length.
|
|
473
|
+
*/
|
|
474
|
+
function sha256Prefix(data, prefix) {
|
|
475
|
+
return sha256(data).slice(0, prefix);
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Extend a key to 32 bytes using HKDF-HMAC-SHA-256.
|
|
479
|
+
*/
|
|
480
|
+
function extendKey(data) {
|
|
481
|
+
return hkdfHmacSha256(data, new Uint8Array(0), 32);
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Compute HKDF-HMAC-SHA-256 for the given key material.
|
|
485
|
+
*/
|
|
486
|
+
function hkdfHmacSha256(keyMaterial, salt, keyLen) {
|
|
487
|
+
return hkdf(sha256$1, keyMaterial, salt.length > 0 ? salt : void 0, new Uint8Array(0), keyLen);
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Obfuscate (or deobfuscate) a message using ChaCha20.
|
|
491
|
+
* The function is symmetric - applying it twice returns the original message.
|
|
492
|
+
*/
|
|
493
|
+
function obfuscate(key, message) {
|
|
494
|
+
if (message.length === 0) return new Uint8Array(0);
|
|
495
|
+
const extendedKey = extendKey(key instanceof Uint8Array ? key : new Uint8Array(key));
|
|
496
|
+
const iv = new Uint8Array(12);
|
|
497
|
+
for (let i = 0; i < 12; i++) iv[i] = extendedKey[31 - i];
|
|
498
|
+
return chacha20(extendedKey, iv, message instanceof Uint8Array ? message : new Uint8Array(message));
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
//#endregion
|
|
502
|
+
//#region src/xoshiro256starstar.ts
|
|
503
|
+
/**
|
|
504
|
+
* Xoshiro256** PRNG implementation.
|
|
505
|
+
* A fast, high-quality pseudorandom number generator.
|
|
506
|
+
*/
|
|
507
|
+
var Xoshiro256StarStar = class Xoshiro256StarStar {
|
|
508
|
+
s;
|
|
509
|
+
constructor(s) {
|
|
510
|
+
this.s = s;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Get the internal state as an array of 4 u64 values.
|
|
514
|
+
*/
|
|
515
|
+
toState() {
|
|
516
|
+
return [...this.s];
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Create a new PRNG from a state array.
|
|
520
|
+
*/
|
|
521
|
+
static fromState(state) {
|
|
522
|
+
return new Xoshiro256StarStar([...state]);
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Serialize the state to 32 bytes (little-endian).
|
|
526
|
+
*/
|
|
527
|
+
toData() {
|
|
528
|
+
const data = new Uint8Array(32);
|
|
529
|
+
for (let i = 0; i < 4; i++) {
|
|
530
|
+
const val = this.s[i];
|
|
531
|
+
for (let j = 0; j < 8; j++) data[i * 8 + j] = Number(val >> BigInt(j * 8) & 255n);
|
|
532
|
+
}
|
|
533
|
+
return data;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Create a new PRNG from 32 bytes of seed data (little-endian).
|
|
537
|
+
*/
|
|
538
|
+
static fromData(data) {
|
|
539
|
+
if (data.length !== 32) throw new Error(`expected 32 bytes, got ${data.length}`);
|
|
540
|
+
const s = [
|
|
541
|
+
0n,
|
|
542
|
+
0n,
|
|
543
|
+
0n,
|
|
544
|
+
0n
|
|
545
|
+
];
|
|
546
|
+
for (let i = 0; i < 4; i++) {
|
|
547
|
+
let val = 0n;
|
|
548
|
+
for (let j = 0; j < 8; j++) val |= BigInt(data[i * 8 + j]) << BigInt(j * 8);
|
|
549
|
+
s[i] = val;
|
|
550
|
+
}
|
|
551
|
+
return new Xoshiro256StarStar(s);
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Generate the next u64 value.
|
|
555
|
+
*/
|
|
556
|
+
nextU64() {
|
|
557
|
+
const result = this.starstarU64(this.s[1]);
|
|
558
|
+
this.advance();
|
|
559
|
+
return result;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Generate the next u32 value (upper bits of u64 for better quality).
|
|
563
|
+
*/
|
|
564
|
+
nextU32() {
|
|
565
|
+
return Number(this.nextU64() >> 32n & 4294967295n);
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Generate the next byte.
|
|
569
|
+
*/
|
|
570
|
+
nextByte() {
|
|
571
|
+
return Number(this.nextU64() & 255n);
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Generate the next n bytes.
|
|
575
|
+
*/
|
|
576
|
+
nextBytes(len) {
|
|
577
|
+
const bytes = new Uint8Array(len);
|
|
578
|
+
for (let i = 0; i < len; i++) bytes[i] = this.nextByte();
|
|
579
|
+
return bytes;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Fill a buffer with random bytes.
|
|
583
|
+
*/
|
|
584
|
+
fillBytes(dest) {
|
|
585
|
+
let i = 0;
|
|
586
|
+
while (i + 8 <= dest.length) {
|
|
587
|
+
const val = this.nextU64();
|
|
588
|
+
for (let j = 0; j < 8; j++) dest[i + j] = Number(val >> BigInt(j * 8) & 255n);
|
|
589
|
+
i += 8;
|
|
590
|
+
}
|
|
591
|
+
if (i < dest.length) {
|
|
592
|
+
const val = this.nextU64();
|
|
593
|
+
for (let j = 0; i < dest.length; i++, j++) dest[i] = Number(val >> BigInt(j * 8) & 255n);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* The starstar transformation: x * 5, rotate left 7, * 9
|
|
598
|
+
*/
|
|
599
|
+
starstarU64(x) {
|
|
600
|
+
const mask64 = 18446744073709551615n;
|
|
601
|
+
const mul5 = x * 5n & mask64;
|
|
602
|
+
return this.rotateLeft64(mul5, 7n) * 9n & mask64;
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Rotate a 64-bit value left by n bits.
|
|
606
|
+
*/
|
|
607
|
+
rotateLeft64(x, n) {
|
|
608
|
+
return (x << n | x >> 64n - n) & 18446744073709551615n;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Advance the PRNG state.
|
|
612
|
+
*/
|
|
613
|
+
advance() {
|
|
614
|
+
const t = this.s[1] << 17n & 18446744073709551615n;
|
|
615
|
+
this.s[2] ^= this.s[0];
|
|
616
|
+
this.s[3] ^= this.s[1];
|
|
617
|
+
this.s[1] ^= this.s[2];
|
|
618
|
+
this.s[0] ^= this.s[3];
|
|
619
|
+
this.s[2] ^= t;
|
|
620
|
+
this.s[3] = this.rotateLeft64(this.s[3], 45n);
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
//#endregion
|
|
625
|
+
//#region src/rng-state.ts
|
|
626
|
+
const RNG_STATE_LENGTH = 32;
|
|
627
|
+
/**
|
|
628
|
+
* RNG state for provenance marks (32 bytes).
|
|
629
|
+
*/
|
|
630
|
+
var RngState = class RngState {
|
|
631
|
+
data;
|
|
632
|
+
constructor(data) {
|
|
633
|
+
this.data = data;
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Get the raw bytes.
|
|
637
|
+
*/
|
|
638
|
+
toBytes() {
|
|
639
|
+
return new Uint8Array(this.data);
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Create from a 32-byte array.
|
|
643
|
+
*/
|
|
644
|
+
static fromBytes(bytes) {
|
|
645
|
+
if (bytes.length !== RNG_STATE_LENGTH) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidSeedLength, void 0, { actual: bytes.length });
|
|
646
|
+
return new RngState(new Uint8Array(bytes));
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Create from a slice (validates length).
|
|
650
|
+
*/
|
|
651
|
+
static fromSlice(bytes) {
|
|
652
|
+
return RngState.fromBytes(bytes);
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Get the hex representation.
|
|
656
|
+
*/
|
|
657
|
+
hex() {
|
|
658
|
+
return Array.from(this.data).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Convert to CBOR (byte string).
|
|
662
|
+
*/
|
|
663
|
+
toCbor() {
|
|
664
|
+
return cbor(this.data);
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Create from CBOR (byte string).
|
|
668
|
+
*/
|
|
669
|
+
static fromCbor(cborValue) {
|
|
670
|
+
const bytes = expectBytes(cborValue);
|
|
671
|
+
return RngState.fromBytes(bytes);
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
//#endregion
|
|
676
|
+
//#region src/seed.ts
|
|
677
|
+
const PROVENANCE_SEED_LENGTH = 32;
|
|
678
|
+
/**
|
|
679
|
+
* A seed for generating provenance marks.
|
|
680
|
+
*/
|
|
681
|
+
var ProvenanceSeed = class ProvenanceSeed {
|
|
682
|
+
data;
|
|
683
|
+
constructor(data) {
|
|
684
|
+
this.data = data;
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Create a new random seed using secure random number generation.
|
|
688
|
+
*/
|
|
689
|
+
static new() {
|
|
690
|
+
const data = randomData(PROVENANCE_SEED_LENGTH);
|
|
691
|
+
return ProvenanceSeed.fromBytes(data);
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Create a new seed using custom random data.
|
|
695
|
+
*/
|
|
696
|
+
static newUsing(randomData$1) {
|
|
697
|
+
if (randomData$1.length < PROVENANCE_SEED_LENGTH) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidSeedLength, void 0, { actual: randomData$1.length });
|
|
698
|
+
return ProvenanceSeed.fromBytes(randomData$1.slice(0, PROVENANCE_SEED_LENGTH));
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Create a new seed from a passphrase.
|
|
702
|
+
*/
|
|
703
|
+
static newWithPassphrase(passphrase) {
|
|
704
|
+
const seedData = extendKey(new TextEncoder().encode(passphrase));
|
|
705
|
+
return ProvenanceSeed.fromBytes(seedData);
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Get the raw bytes.
|
|
709
|
+
*/
|
|
710
|
+
toBytes() {
|
|
711
|
+
return new Uint8Array(this.data);
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Create from a 32-byte array.
|
|
715
|
+
*/
|
|
716
|
+
static fromBytes(bytes) {
|
|
717
|
+
if (bytes.length !== PROVENANCE_SEED_LENGTH) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidSeedLength, void 0, { actual: bytes.length });
|
|
718
|
+
return new ProvenanceSeed(new Uint8Array(bytes));
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Create from a slice (validates length).
|
|
722
|
+
*/
|
|
723
|
+
static fromSlice(bytes) {
|
|
724
|
+
return ProvenanceSeed.fromBytes(bytes);
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Get the hex representation.
|
|
728
|
+
*/
|
|
729
|
+
hex() {
|
|
730
|
+
return Array.from(this.data).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Convert to CBOR (byte string).
|
|
734
|
+
*/
|
|
735
|
+
toCbor() {
|
|
736
|
+
return cbor(this.data);
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Create from CBOR (byte string).
|
|
740
|
+
*/
|
|
741
|
+
static fromCbor(cborValue) {
|
|
742
|
+
const bytes = expectBytes(cborValue);
|
|
743
|
+
return ProvenanceSeed.fromBytes(bytes);
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
//#endregion
|
|
748
|
+
//#region src/utils.ts
|
|
749
|
+
/**
|
|
750
|
+
* Convert a Uint8Array to a lowercase hexadecimal string.
|
|
751
|
+
*
|
|
752
|
+
* @param data - The byte array to convert
|
|
753
|
+
* @returns A lowercase hex string representation (2 characters per byte)
|
|
754
|
+
*/
|
|
755
|
+
function bytesToHex(data) {
|
|
756
|
+
return Array.from(data).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Convert a Uint8Array to a base64-encoded string.
|
|
760
|
+
*
|
|
761
|
+
* This function works in both browser and Node.js environments.
|
|
762
|
+
*
|
|
763
|
+
* @param data - The byte array to encode
|
|
764
|
+
* @returns A base64-encoded string
|
|
765
|
+
*/
|
|
766
|
+
function toBase64(data) {
|
|
767
|
+
const globalBtoa = globalThis.btoa;
|
|
768
|
+
if (typeof globalBtoa === "function") {
|
|
769
|
+
let binary = "";
|
|
770
|
+
for (const byte of data) binary += String.fromCharCode(byte);
|
|
771
|
+
return globalBtoa(binary);
|
|
772
|
+
}
|
|
773
|
+
const requireFn = __require;
|
|
774
|
+
if (typeof requireFn === "function") {
|
|
775
|
+
const { Buffer: NodeBuffer } = requireFn("buffer");
|
|
776
|
+
return NodeBuffer.from(data).toString("base64");
|
|
777
|
+
}
|
|
778
|
+
throw new Error("btoa not available and require is not defined");
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Convert a base64-encoded string to a Uint8Array.
|
|
782
|
+
*
|
|
783
|
+
* This function works in both browser and Node.js environments.
|
|
784
|
+
*
|
|
785
|
+
* @param base64 - A base64-encoded string
|
|
786
|
+
* @returns The decoded byte array
|
|
787
|
+
*/
|
|
788
|
+
function fromBase64(base64) {
|
|
789
|
+
const globalAtob = globalThis.atob;
|
|
790
|
+
if (typeof globalAtob === "function") {
|
|
791
|
+
const binary = globalAtob(base64);
|
|
792
|
+
const bytes = new Uint8Array(binary.length);
|
|
793
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
794
|
+
return bytes;
|
|
795
|
+
}
|
|
796
|
+
const requireFn = __require;
|
|
797
|
+
if (typeof requireFn === "function") {
|
|
798
|
+
const { Buffer: NodeBuffer } = requireFn("buffer");
|
|
799
|
+
return new Uint8Array(NodeBuffer.from(base64, "base64"));
|
|
800
|
+
}
|
|
801
|
+
throw new Error("atob not available and require is not defined");
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
//#endregion
|
|
805
|
+
//#region src/mark.ts
|
|
806
|
+
/**
|
|
807
|
+
* A cryptographically-secured provenance mark.
|
|
808
|
+
*/
|
|
809
|
+
var ProvenanceMark = class ProvenanceMark {
|
|
810
|
+
_res;
|
|
811
|
+
_key;
|
|
812
|
+
_hash;
|
|
813
|
+
_chainId;
|
|
814
|
+
_seqBytes;
|
|
815
|
+
_dateBytes;
|
|
816
|
+
_infoBytes;
|
|
817
|
+
_seq;
|
|
818
|
+
_date;
|
|
819
|
+
constructor(res, key, hash, chainId, seqBytes, dateBytes, infoBytes, seq, date) {
|
|
820
|
+
this._res = res;
|
|
821
|
+
this._key = key;
|
|
822
|
+
this._hash = hash;
|
|
823
|
+
this._chainId = chainId;
|
|
824
|
+
this._seqBytes = seqBytes;
|
|
825
|
+
this._dateBytes = dateBytes;
|
|
826
|
+
this._infoBytes = infoBytes;
|
|
827
|
+
this._seq = seq;
|
|
828
|
+
this._date = date;
|
|
829
|
+
}
|
|
830
|
+
res() {
|
|
831
|
+
return this._res;
|
|
832
|
+
}
|
|
833
|
+
key() {
|
|
834
|
+
return new Uint8Array(this._key);
|
|
835
|
+
}
|
|
836
|
+
hash() {
|
|
837
|
+
return new Uint8Array(this._hash);
|
|
838
|
+
}
|
|
839
|
+
chainId() {
|
|
840
|
+
return new Uint8Array(this._chainId);
|
|
841
|
+
}
|
|
842
|
+
seqBytes() {
|
|
843
|
+
return new Uint8Array(this._seqBytes);
|
|
844
|
+
}
|
|
845
|
+
dateBytes() {
|
|
846
|
+
return new Uint8Array(this._dateBytes);
|
|
847
|
+
}
|
|
848
|
+
seq() {
|
|
849
|
+
return this._seq;
|
|
850
|
+
}
|
|
851
|
+
date() {
|
|
852
|
+
return this._date;
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Get the message (serialized bytes) of this mark.
|
|
856
|
+
*/
|
|
857
|
+
message() {
|
|
858
|
+
const payload = new Uint8Array([
|
|
859
|
+
...this._chainId,
|
|
860
|
+
...this._hash,
|
|
861
|
+
...this._seqBytes,
|
|
862
|
+
...this._dateBytes,
|
|
863
|
+
...this._infoBytes
|
|
864
|
+
]);
|
|
865
|
+
const obfuscated = obfuscate(this._key, payload);
|
|
866
|
+
return new Uint8Array([...this._key, ...obfuscated]);
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Get the info field as CBOR, if present.
|
|
870
|
+
*/
|
|
871
|
+
info() {
|
|
872
|
+
if (this._infoBytes.length === 0) return;
|
|
873
|
+
return decodeCbor(this._infoBytes);
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Create a new provenance mark.
|
|
877
|
+
*/
|
|
878
|
+
static new(res, key, nextKey, chainId, seq, date, info) {
|
|
879
|
+
const linkLen = linkLength(res);
|
|
880
|
+
if (key.length !== linkLen) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidKeyLength, void 0, {
|
|
881
|
+
expected: linkLen,
|
|
882
|
+
actual: key.length
|
|
883
|
+
});
|
|
884
|
+
if (nextKey.length !== linkLen) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidNextKeyLength, void 0, {
|
|
885
|
+
expected: linkLen,
|
|
886
|
+
actual: nextKey.length
|
|
887
|
+
});
|
|
888
|
+
if (chainId.length !== linkLen) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidChainIdLength, void 0, {
|
|
889
|
+
expected: linkLen,
|
|
890
|
+
actual: chainId.length
|
|
891
|
+
});
|
|
892
|
+
const dateBytes = serializeDate(res, date);
|
|
893
|
+
const seqBytes = serializeSeq(res, seq);
|
|
894
|
+
const normalizedDate = deserializeDate(res, dateBytes);
|
|
895
|
+
const infoBytes = info !== void 0 ? cborData(info) : new Uint8Array(0);
|
|
896
|
+
const hash = ProvenanceMark.makeHash(res, key, nextKey, chainId, seqBytes, dateBytes, infoBytes);
|
|
897
|
+
return new ProvenanceMark(res, new Uint8Array(key), hash, new Uint8Array(chainId), seqBytes, dateBytes, infoBytes, seq, normalizedDate);
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Create a provenance mark from a serialized message.
|
|
901
|
+
*/
|
|
902
|
+
static fromMessage(res, message) {
|
|
903
|
+
const minLen = fixedLength(res);
|
|
904
|
+
if (message.length < minLen) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidMessageLength, void 0, {
|
|
905
|
+
expected: minLen,
|
|
906
|
+
actual: message.length
|
|
907
|
+
});
|
|
908
|
+
const linkLen = linkLength(res);
|
|
909
|
+
const keyRng = keyRange(res);
|
|
910
|
+
const key = message.slice(keyRng.start, keyRng.end);
|
|
911
|
+
const payload = obfuscate(key, message.slice(linkLen));
|
|
912
|
+
const chainIdRng = chainIdRange(res);
|
|
913
|
+
const chainId = payload.slice(chainIdRng.start, chainIdRng.end);
|
|
914
|
+
const hashRng = hashRange(res);
|
|
915
|
+
const hash = payload.slice(hashRng.start, hashRng.end);
|
|
916
|
+
const seqRng = seqBytesRange(res);
|
|
917
|
+
const seqBytes = payload.slice(seqRng.start, seqRng.end);
|
|
918
|
+
const seq = deserializeSeq(res, seqBytes);
|
|
919
|
+
const dateRng = dateBytesRange(res);
|
|
920
|
+
const dateBytes = payload.slice(dateRng.start, dateRng.end);
|
|
921
|
+
const date = deserializeDate(res, dateBytes);
|
|
922
|
+
const infoStart = infoRangeStart(res);
|
|
923
|
+
const infoBytes = payload.slice(infoStart);
|
|
924
|
+
if (infoBytes.length > 0) try {
|
|
925
|
+
decodeCbor(infoBytes);
|
|
926
|
+
} catch {
|
|
927
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidInfoCbor);
|
|
928
|
+
}
|
|
929
|
+
return new ProvenanceMark(res, new Uint8Array(key), new Uint8Array(hash), new Uint8Array(chainId), new Uint8Array(seqBytes), new Uint8Array(dateBytes), new Uint8Array(infoBytes), seq, date);
|
|
930
|
+
}
|
|
931
|
+
static makeHash(res, key, nextKey, chainId, seqBytes, dateBytes, infoBytes) {
|
|
932
|
+
return sha256Prefix(new Uint8Array([
|
|
933
|
+
...key,
|
|
934
|
+
...nextKey,
|
|
935
|
+
...chainId,
|
|
936
|
+
...seqBytes,
|
|
937
|
+
...dateBytes,
|
|
938
|
+
...infoBytes
|
|
939
|
+
]), linkLength(res));
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Get the first four bytes of the hash as a hex string identifier.
|
|
943
|
+
*/
|
|
944
|
+
identifier() {
|
|
945
|
+
return Array.from(this._hash.slice(0, 4)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Get the first four bytes of the hash as upper-case ByteWords.
|
|
949
|
+
*/
|
|
950
|
+
bytewordsIdentifier(prefix) {
|
|
951
|
+
const s = encodeBytewordsIdentifier(this._hash.slice(0, 4)).toUpperCase();
|
|
952
|
+
return prefix ? `\u{1F151} ${s}` : s;
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Get the first four bytes of the hash as Bytemoji.
|
|
956
|
+
*/
|
|
957
|
+
bytemojiIdentifier(prefix) {
|
|
958
|
+
const s = encodeBytemojisIdentifier(this._hash.slice(0, 4)).toUpperCase();
|
|
959
|
+
return prefix ? `\u{1F151} ${s}` : s;
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Check if this mark precedes another mark in the chain.
|
|
963
|
+
*/
|
|
964
|
+
precedes(next) {
|
|
965
|
+
try {
|
|
966
|
+
this.precedesOpt(next);
|
|
967
|
+
return true;
|
|
968
|
+
} catch {
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Check if this mark precedes another mark, throwing on validation errors.
|
|
974
|
+
*/
|
|
975
|
+
precedesOpt(next) {
|
|
976
|
+
if (next._seq === 0) throw new ProvenanceMarkError(ProvenanceMarkErrorType.ValidationError, void 0, { message: "non-genesis mark at sequence 0" });
|
|
977
|
+
if (arraysEqual(next._key, next._chainId)) throw new ProvenanceMarkError(ProvenanceMarkErrorType.ValidationError, void 0, { message: "genesis mark must have key equal to chain_id" });
|
|
978
|
+
if (this._seq !== next._seq - 1) throw new ProvenanceMarkError(ProvenanceMarkErrorType.ValidationError, void 0, { message: `sequence gap: expected ${this._seq + 1}, got ${next._seq}` });
|
|
979
|
+
if (this._date > next._date) throw new ProvenanceMarkError(ProvenanceMarkErrorType.ValidationError, void 0, { message: `date ordering: ${this._date.toISOString()} > ${next._date.toISOString()}` });
|
|
980
|
+
const expectedHash = ProvenanceMark.makeHash(this._res, this._key, next._key, this._chainId, this._seqBytes, this._dateBytes, this._infoBytes);
|
|
981
|
+
if (!arraysEqual(this._hash, expectedHash)) throw new ProvenanceMarkError(ProvenanceMarkErrorType.ValidationError, void 0, {
|
|
982
|
+
message: "hash mismatch",
|
|
983
|
+
expected: Array.from(expectedHash).map((b) => b.toString(16).padStart(2, "0")).join(""),
|
|
984
|
+
actual: Array.from(this._hash).map((b) => b.toString(16).padStart(2, "0")).join("")
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Check if a sequence of marks is valid.
|
|
989
|
+
*/
|
|
990
|
+
static isSequenceValid(marks) {
|
|
991
|
+
if (marks.length < 2) return false;
|
|
992
|
+
if (marks[0]._seq === 0 && !marks[0].isGenesis()) return false;
|
|
993
|
+
for (let i = 0; i < marks.length - 1; i++) if (!marks[i].precedes(marks[i + 1])) return false;
|
|
994
|
+
return true;
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Check if this is a genesis mark (seq 0 and key equals chain_id).
|
|
998
|
+
*/
|
|
999
|
+
isGenesis() {
|
|
1000
|
+
return this._seq === 0 && arraysEqual(this._key, this._chainId);
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Encode as bytewords with the given style.
|
|
1004
|
+
*/
|
|
1005
|
+
toBytewordsWithStyle(style) {
|
|
1006
|
+
return encodeBytewords(this.message(), style);
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Encode as standard bytewords.
|
|
1010
|
+
*/
|
|
1011
|
+
toBytewords() {
|
|
1012
|
+
return this.toBytewordsWithStyle(BytewordsStyle.Standard);
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Decode from bytewords.
|
|
1016
|
+
*/
|
|
1017
|
+
static fromBytewords(res, bytewords) {
|
|
1018
|
+
const message = decodeBytewords(bytewords, BytewordsStyle.Standard);
|
|
1019
|
+
return ProvenanceMark.fromMessage(res, message);
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Encode for URL (minimal bytewords of CBOR).
|
|
1023
|
+
*/
|
|
1024
|
+
toUrlEncoding() {
|
|
1025
|
+
return encodeBytewords(this.toCborData(), BytewordsStyle.Minimal);
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Decode from URL encoding.
|
|
1029
|
+
*/
|
|
1030
|
+
static fromUrlEncoding(urlEncoding) {
|
|
1031
|
+
const cborValue = decodeCbor(decodeBytewords(urlEncoding, BytewordsStyle.Minimal));
|
|
1032
|
+
return ProvenanceMark.fromTaggedCbor(cborValue);
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Build a URL with this mark as a query parameter.
|
|
1036
|
+
*/
|
|
1037
|
+
toUrl(base) {
|
|
1038
|
+
const url = new URL(base);
|
|
1039
|
+
url.searchParams.set("provenance", this.toUrlEncoding());
|
|
1040
|
+
return url;
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Parse a provenance mark from a URL.
|
|
1044
|
+
*/
|
|
1045
|
+
static fromUrl(url) {
|
|
1046
|
+
const param = url.searchParams.get("provenance");
|
|
1047
|
+
if (param === null || param === "") throw new ProvenanceMarkError(ProvenanceMarkErrorType.MissingUrlParameter, void 0, { parameter: "provenance" });
|
|
1048
|
+
return ProvenanceMark.fromUrlEncoding(param);
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Get the untagged CBOR representation.
|
|
1052
|
+
*/
|
|
1053
|
+
untaggedCbor() {
|
|
1054
|
+
return cbor([resolutionToCbor(this._res), cbor(this.message())]);
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Get the tagged CBOR representation.
|
|
1058
|
+
*/
|
|
1059
|
+
taggedCbor() {
|
|
1060
|
+
return cbor({
|
|
1061
|
+
tag: PROVENANCE_MARK.value,
|
|
1062
|
+
value: this.untaggedCbor()
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Serialize to CBOR bytes (tagged).
|
|
1067
|
+
*/
|
|
1068
|
+
toCborData() {
|
|
1069
|
+
return cborData(this.taggedCbor());
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Create from untagged CBOR.
|
|
1073
|
+
*/
|
|
1074
|
+
static fromUntaggedCbor(cborValue) {
|
|
1075
|
+
const arr = expectArray(cborValue);
|
|
1076
|
+
if (arr.length !== 2) throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, void 0, { message: "Invalid provenance mark length" });
|
|
1077
|
+
const res = resolutionFromCbor(arr[0]);
|
|
1078
|
+
const message = expectBytes(arr[1]);
|
|
1079
|
+
return ProvenanceMark.fromMessage(res, message);
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Create from tagged CBOR.
|
|
1083
|
+
*/
|
|
1084
|
+
static fromTaggedCbor(cborValue) {
|
|
1085
|
+
const cborObj = cborValue;
|
|
1086
|
+
if (cborObj.tag !== PROVENANCE_MARK.value) throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, void 0, { message: `Expected tag ${PROVENANCE_MARK.value}, got ${String(cborObj.tag)}` });
|
|
1087
|
+
if (cborObj.value === void 0) throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, void 0, { message: "Tagged CBOR value is missing" });
|
|
1088
|
+
return ProvenanceMark.fromUntaggedCbor(cborObj.value);
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Create from CBOR bytes.
|
|
1092
|
+
*/
|
|
1093
|
+
static fromCborData(data) {
|
|
1094
|
+
const cborValue = decodeCbor(data);
|
|
1095
|
+
return ProvenanceMark.fromTaggedCbor(cborValue);
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Get the fingerprint (SHA-256 of CBOR data).
|
|
1099
|
+
*/
|
|
1100
|
+
fingerprint() {
|
|
1101
|
+
return sha256(this.toCborData());
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Debug string representation.
|
|
1105
|
+
*/
|
|
1106
|
+
toString() {
|
|
1107
|
+
return `ProvenanceMark(${this.identifier()})`;
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Detailed debug representation.
|
|
1111
|
+
*/
|
|
1112
|
+
toDebugString() {
|
|
1113
|
+
const components = [
|
|
1114
|
+
`key: ${bytesToHex(this._key)}`,
|
|
1115
|
+
`hash: ${bytesToHex(this._hash)}`,
|
|
1116
|
+
`chainID: ${bytesToHex(this._chainId)}`,
|
|
1117
|
+
`seq: ${this._seq}`,
|
|
1118
|
+
`date: ${this._date.toISOString()}`
|
|
1119
|
+
];
|
|
1120
|
+
const info = this.info();
|
|
1121
|
+
if (info !== void 0) components.push(`info: ${JSON.stringify(info)}`);
|
|
1122
|
+
return `ProvenanceMark(${components.join(", ")})`;
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Check equality with another mark.
|
|
1126
|
+
*/
|
|
1127
|
+
equals(other) {
|
|
1128
|
+
return this._res === other._res && arraysEqual(this.message(), other.message());
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* JSON serialization.
|
|
1132
|
+
*/
|
|
1133
|
+
toJSON() {
|
|
1134
|
+
const result = {
|
|
1135
|
+
res: this._res,
|
|
1136
|
+
key: toBase64(this._key),
|
|
1137
|
+
hash: toBase64(this._hash),
|
|
1138
|
+
chainID: toBase64(this._chainId),
|
|
1139
|
+
seq: this._seq,
|
|
1140
|
+
date: this._date.toISOString()
|
|
1141
|
+
};
|
|
1142
|
+
if (this._infoBytes.length > 0) result["info_bytes"] = toBase64(this._infoBytes);
|
|
1143
|
+
return result;
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Create from JSON object.
|
|
1147
|
+
*/
|
|
1148
|
+
static fromJSON(json) {
|
|
1149
|
+
const res = json["res"];
|
|
1150
|
+
const key = fromBase64(json["key"]);
|
|
1151
|
+
const hash = fromBase64(json["hash"]);
|
|
1152
|
+
const chainId = fromBase64(json["chainID"]);
|
|
1153
|
+
const seq = json["seq"];
|
|
1154
|
+
const dateStr = json["date"];
|
|
1155
|
+
const date = new Date(dateStr);
|
|
1156
|
+
const seqBytes = serializeSeq(res, seq);
|
|
1157
|
+
const dateBytes = serializeDate(res, date);
|
|
1158
|
+
let infoBytes = new Uint8Array(0);
|
|
1159
|
+
if (typeof json["info_bytes"] === "string") infoBytes = fromBase64(json["info_bytes"]);
|
|
1160
|
+
return new ProvenanceMark(res, key, hash, chainId, seqBytes, dateBytes, infoBytes, seq, date);
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
/**
|
|
1164
|
+
* Helper function to compare two Uint8Arrays.
|
|
1165
|
+
*/
|
|
1166
|
+
function arraysEqual(a, b) {
|
|
1167
|
+
if (a.length !== b.length) return false;
|
|
1168
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
1169
|
+
return true;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
//#endregion
|
|
1173
|
+
//#region src/generator.ts
|
|
1174
|
+
/**
|
|
1175
|
+
* Generator for creating provenance mark chains.
|
|
1176
|
+
*/
|
|
1177
|
+
var ProvenanceMarkGenerator = class ProvenanceMarkGenerator {
|
|
1178
|
+
_res;
|
|
1179
|
+
_seed;
|
|
1180
|
+
_chainId;
|
|
1181
|
+
_nextSeq;
|
|
1182
|
+
_rngState;
|
|
1183
|
+
constructor(res, seed, chainId, nextSeq, rngState) {
|
|
1184
|
+
this._res = res;
|
|
1185
|
+
this._seed = seed;
|
|
1186
|
+
this._chainId = chainId;
|
|
1187
|
+
this._nextSeq = nextSeq;
|
|
1188
|
+
this._rngState = rngState;
|
|
1189
|
+
}
|
|
1190
|
+
res() {
|
|
1191
|
+
return this._res;
|
|
1192
|
+
}
|
|
1193
|
+
seed() {
|
|
1194
|
+
return this._seed;
|
|
1195
|
+
}
|
|
1196
|
+
chainId() {
|
|
1197
|
+
return new Uint8Array(this._chainId);
|
|
1198
|
+
}
|
|
1199
|
+
nextSeq() {
|
|
1200
|
+
return this._nextSeq;
|
|
1201
|
+
}
|
|
1202
|
+
rngState() {
|
|
1203
|
+
return this._rngState;
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Create a new generator with a seed.
|
|
1207
|
+
*/
|
|
1208
|
+
static newWithSeed(res, seed) {
|
|
1209
|
+
const digest1 = sha256(seed.toBytes());
|
|
1210
|
+
const chainId = digest1.slice(0, linkLength(res));
|
|
1211
|
+
const digest2 = sha256(digest1);
|
|
1212
|
+
return ProvenanceMarkGenerator.new(res, seed, chainId, 0, RngState.fromBytes(digest2));
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Create a new generator with a passphrase.
|
|
1216
|
+
*/
|
|
1217
|
+
static newWithPassphrase(res, passphrase) {
|
|
1218
|
+
const seed = ProvenanceSeed.newWithPassphrase(passphrase);
|
|
1219
|
+
return ProvenanceMarkGenerator.newWithSeed(res, seed);
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* Create a new generator with custom random data.
|
|
1223
|
+
*/
|
|
1224
|
+
static newUsing(res, randomData$1) {
|
|
1225
|
+
const seed = ProvenanceSeed.newUsing(randomData$1);
|
|
1226
|
+
return ProvenanceMarkGenerator.newWithSeed(res, seed);
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Create a new generator with random seed.
|
|
1230
|
+
*/
|
|
1231
|
+
static newRandom(res) {
|
|
1232
|
+
const seed = ProvenanceSeed.new();
|
|
1233
|
+
return ProvenanceMarkGenerator.newWithSeed(res, seed);
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Create a new generator with all parameters.
|
|
1237
|
+
*/
|
|
1238
|
+
static new(res, seed, chainId, nextSeq, rngState) {
|
|
1239
|
+
const linkLen = linkLength(res);
|
|
1240
|
+
if (chainId.length !== linkLen) throw new ProvenanceMarkError(ProvenanceMarkErrorType.InvalidChainIdLength, void 0, {
|
|
1241
|
+
expected: linkLen,
|
|
1242
|
+
actual: chainId.length
|
|
1243
|
+
});
|
|
1244
|
+
return new ProvenanceMarkGenerator(res, seed, chainId, nextSeq, rngState);
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Generate the next provenance mark in the chain.
|
|
1248
|
+
*/
|
|
1249
|
+
next(date, info) {
|
|
1250
|
+
const data = this._rngState.toBytes();
|
|
1251
|
+
const rng = Xoshiro256StarStar.fromData(data);
|
|
1252
|
+
const seq = this._nextSeq;
|
|
1253
|
+
this._nextSeq += 1;
|
|
1254
|
+
let key;
|
|
1255
|
+
if (seq === 0) key = new Uint8Array(this._chainId);
|
|
1256
|
+
else {
|
|
1257
|
+
key = rng.nextBytes(linkLength(this._res));
|
|
1258
|
+
this._rngState = RngState.fromBytes(rng.toData());
|
|
1259
|
+
}
|
|
1260
|
+
const nextRngData = rng.toData();
|
|
1261
|
+
const nextKey = Xoshiro256StarStar.fromData(nextRngData).nextBytes(linkLength(this._res));
|
|
1262
|
+
return ProvenanceMark.new(this._res, key, nextKey, new Uint8Array(this._chainId), seq, date, info);
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* String representation.
|
|
1266
|
+
*/
|
|
1267
|
+
toString() {
|
|
1268
|
+
return `ProvenanceMarkGenerator(chainID: ${bytesToHex(this._chainId)}, res: ${this._res}, seed: ${this._seed.hex()}, nextSeq: ${this._nextSeq})`;
|
|
1269
|
+
}
|
|
1270
|
+
/**
|
|
1271
|
+
* JSON serialization.
|
|
1272
|
+
*/
|
|
1273
|
+
toJSON() {
|
|
1274
|
+
return {
|
|
1275
|
+
res: this._res,
|
|
1276
|
+
seed: toBase64(this._seed.toBytes()),
|
|
1277
|
+
chainID: toBase64(this._chainId),
|
|
1278
|
+
nextSeq: this._nextSeq,
|
|
1279
|
+
rngState: toBase64(this._rngState.toBytes())
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Create from JSON object.
|
|
1284
|
+
*/
|
|
1285
|
+
static fromJSON(json) {
|
|
1286
|
+
const res = json["res"];
|
|
1287
|
+
const seed = ProvenanceSeed.fromBytes(fromBase64(json["seed"]));
|
|
1288
|
+
const chainId = fromBase64(json["chainID"]);
|
|
1289
|
+
const nextSeq = json["nextSeq"];
|
|
1290
|
+
const rngState = RngState.fromBytes(fromBase64(json["rngState"]));
|
|
1291
|
+
return ProvenanceMarkGenerator.new(res, seed, chainId, nextSeq, rngState);
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
|
|
1295
|
+
//#endregion
|
|
1296
|
+
//#region src/validate.ts
|
|
1297
|
+
/**
|
|
1298
|
+
* Format for validation report output.
|
|
1299
|
+
*/
|
|
1300
|
+
let ValidationReportFormat = /* @__PURE__ */ function(ValidationReportFormat$1) {
|
|
1301
|
+
/** Human-readable text format */
|
|
1302
|
+
ValidationReportFormat$1["Text"] = "text";
|
|
1303
|
+
/** Compact JSON format (no whitespace) */
|
|
1304
|
+
ValidationReportFormat$1["JsonCompact"] = "json-compact";
|
|
1305
|
+
/** Pretty-printed JSON format (with indentation) */
|
|
1306
|
+
ValidationReportFormat$1["JsonPretty"] = "json-pretty";
|
|
1307
|
+
return ValidationReportFormat$1;
|
|
1308
|
+
}({});
|
|
1309
|
+
/**
|
|
1310
|
+
* Format a validation issue as a string.
|
|
1311
|
+
*/
|
|
1312
|
+
function formatValidationIssue(issue) {
|
|
1313
|
+
switch (issue.type) {
|
|
1314
|
+
case "HashMismatch": return `hash mismatch: expected ${issue.expected}, got ${issue.actual}`;
|
|
1315
|
+
case "KeyMismatch": return "key mismatch: current hash was not generated from next key";
|
|
1316
|
+
case "SequenceGap": return `sequence number gap: expected ${issue.expected}, got ${issue.actual}`;
|
|
1317
|
+
case "DateOrdering": return `date must be equal or later: previous is ${issue.previous}, next is ${issue.next}`;
|
|
1318
|
+
case "NonGenesisAtZero": return "non-genesis mark at sequence 0";
|
|
1319
|
+
case "InvalidGenesisKey": return "genesis mark must have key equal to chain_id";
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Get the chain ID as a hex string for display.
|
|
1324
|
+
*/
|
|
1325
|
+
function chainIdHex(report) {
|
|
1326
|
+
return hexEncode(report.chainId);
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Check if the validation report has any issues.
|
|
1330
|
+
*/
|
|
1331
|
+
function hasIssues(report) {
|
|
1332
|
+
for (const chain of report.chains) if (!chain.hasGenesis) return true;
|
|
1333
|
+
for (const chain of report.chains) for (const seq of chain.sequences) for (const mark of seq.marks) if (mark.issues.length > 0) return true;
|
|
1334
|
+
if (report.chains.length > 1) return true;
|
|
1335
|
+
if (report.chains.length === 1 && report.chains[0].sequences.length > 1) return true;
|
|
1336
|
+
return false;
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Check if the validation report contains interesting information.
|
|
1340
|
+
*/
|
|
1341
|
+
function isInteresting(report) {
|
|
1342
|
+
if (report.chains.length === 0) return false;
|
|
1343
|
+
for (const chain of report.chains) if (!chain.hasGenesis) return true;
|
|
1344
|
+
if (report.chains.length === 1) {
|
|
1345
|
+
const chain = report.chains[0];
|
|
1346
|
+
if (chain.sequences.length === 1) {
|
|
1347
|
+
if (chain.sequences[0].marks.every((m) => m.issues.length === 0)) return false;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return true;
|
|
1351
|
+
}
|
|
1352
|
+
/**
|
|
1353
|
+
* Format the validation report as human-readable text.
|
|
1354
|
+
*/
|
|
1355
|
+
function formatText(report) {
|
|
1356
|
+
if (!isInteresting(report)) return "";
|
|
1357
|
+
const lines = [];
|
|
1358
|
+
lines.push(`Total marks: ${report.marks.length}`);
|
|
1359
|
+
lines.push(`Chains: ${report.chains.length}`);
|
|
1360
|
+
lines.push("");
|
|
1361
|
+
for (let chainIdx = 0; chainIdx < report.chains.length; chainIdx++) {
|
|
1362
|
+
const chain = report.chains[chainIdx];
|
|
1363
|
+
const chainIdStr = chainIdHex(chain);
|
|
1364
|
+
const shortChainId = chainIdStr.length > 8 ? chainIdStr.slice(0, 8) : chainIdStr;
|
|
1365
|
+
lines.push(`Chain ${chainIdx + 1}: ${shortChainId}`);
|
|
1366
|
+
if (!chain.hasGenesis) lines.push(" Warning: No genesis mark found");
|
|
1367
|
+
for (const seq of chain.sequences) for (const flaggedMark of seq.marks) {
|
|
1368
|
+
const mark = flaggedMark.mark;
|
|
1369
|
+
const shortId = mark.identifier();
|
|
1370
|
+
const seqNum = mark.seq();
|
|
1371
|
+
const annotations = [];
|
|
1372
|
+
if (mark.isGenesis()) annotations.push("genesis mark");
|
|
1373
|
+
for (const issue of flaggedMark.issues) {
|
|
1374
|
+
let issueStr;
|
|
1375
|
+
switch (issue.type) {
|
|
1376
|
+
case "SequenceGap":
|
|
1377
|
+
issueStr = `gap: ${issue.expected} missing`;
|
|
1378
|
+
break;
|
|
1379
|
+
case "DateOrdering":
|
|
1380
|
+
issueStr = `date ${issue.previous} < ${issue.next}`;
|
|
1381
|
+
break;
|
|
1382
|
+
case "HashMismatch":
|
|
1383
|
+
issueStr = "hash mismatch";
|
|
1384
|
+
break;
|
|
1385
|
+
case "KeyMismatch":
|
|
1386
|
+
issueStr = "key mismatch";
|
|
1387
|
+
break;
|
|
1388
|
+
case "NonGenesisAtZero":
|
|
1389
|
+
issueStr = "non-genesis at seq 0";
|
|
1390
|
+
break;
|
|
1391
|
+
case "InvalidGenesisKey":
|
|
1392
|
+
issueStr = "invalid genesis key";
|
|
1393
|
+
break;
|
|
1394
|
+
}
|
|
1395
|
+
annotations.push(issueStr);
|
|
1396
|
+
}
|
|
1397
|
+
if (annotations.length === 0) lines.push(` ${seqNum}: ${shortId}`);
|
|
1398
|
+
else lines.push(` ${seqNum}: ${shortId} (${annotations.join(", ")})`);
|
|
1399
|
+
}
|
|
1400
|
+
lines.push("");
|
|
1401
|
+
}
|
|
1402
|
+
return lines.join("\n").trimEnd();
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Format the validation report.
|
|
1406
|
+
*/
|
|
1407
|
+
function formatReport(report, format) {
|
|
1408
|
+
switch (format) {
|
|
1409
|
+
case ValidationReportFormat.Text: return formatText(report);
|
|
1410
|
+
case ValidationReportFormat.JsonCompact: return JSON.stringify(reportToJSON(report));
|
|
1411
|
+
case ValidationReportFormat.JsonPretty: return JSON.stringify(reportToJSON(report), null, 2);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* Convert a report to a JSON-serializable object.
|
|
1416
|
+
*/
|
|
1417
|
+
function reportToJSON(report) {
|
|
1418
|
+
return {
|
|
1419
|
+
marks: report.marks.map((m) => m.toUrlEncoding()),
|
|
1420
|
+
chains: report.chains.map((chain) => ({
|
|
1421
|
+
chain_id: hexEncode(chain.chainId),
|
|
1422
|
+
has_genesis: chain.hasGenesis,
|
|
1423
|
+
marks: chain.marks.map((m) => m.toUrlEncoding()),
|
|
1424
|
+
sequences: chain.sequences.map((seq) => ({
|
|
1425
|
+
start_seq: seq.startSeq,
|
|
1426
|
+
end_seq: seq.endSeq,
|
|
1427
|
+
marks: seq.marks.map((fm) => ({
|
|
1428
|
+
mark: fm.mark.toUrlEncoding(),
|
|
1429
|
+
issues: fm.issues
|
|
1430
|
+
}))
|
|
1431
|
+
}))
|
|
1432
|
+
}))
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Build sequence bins for a chain.
|
|
1437
|
+
*/
|
|
1438
|
+
function buildSequenceBins(marks) {
|
|
1439
|
+
const sequences = [];
|
|
1440
|
+
let currentSequence = [];
|
|
1441
|
+
for (let i = 0; i < marks.length; i++) {
|
|
1442
|
+
const mark = marks[i];
|
|
1443
|
+
if (i === 0) currentSequence.push({
|
|
1444
|
+
mark,
|
|
1445
|
+
issues: []
|
|
1446
|
+
});
|
|
1447
|
+
else {
|
|
1448
|
+
const prev = marks[i - 1];
|
|
1449
|
+
try {
|
|
1450
|
+
prev.precedesOpt(mark);
|
|
1451
|
+
currentSequence.push({
|
|
1452
|
+
mark,
|
|
1453
|
+
issues: []
|
|
1454
|
+
});
|
|
1455
|
+
} catch (e) {
|
|
1456
|
+
if (currentSequence.length > 0) sequences.push(createSequenceReport(currentSequence));
|
|
1457
|
+
currentSequence = [{
|
|
1458
|
+
mark,
|
|
1459
|
+
issues: [parseValidationError(e, prev, mark)]
|
|
1460
|
+
}];
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
if (currentSequence.length > 0) sequences.push(createSequenceReport(currentSequence));
|
|
1465
|
+
return sequences;
|
|
1466
|
+
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Parse a validation error into a ValidationIssue.
|
|
1469
|
+
*/
|
|
1470
|
+
function parseValidationError(e, prev, next) {
|
|
1471
|
+
const message = e instanceof Error ? e.message : "";
|
|
1472
|
+
if (message !== "" && message.includes("non-genesis mark at sequence 0")) return { type: "NonGenesisAtZero" };
|
|
1473
|
+
if (message !== "" && message.includes("genesis mark must have key equal to chain_id")) return { type: "InvalidGenesisKey" };
|
|
1474
|
+
if (message !== "" && message.includes("sequence gap")) {
|
|
1475
|
+
const match = /expected (\d+), got (\d+)/.exec(message);
|
|
1476
|
+
if (match !== null) return {
|
|
1477
|
+
type: "SequenceGap",
|
|
1478
|
+
expected: parseInt(match[1], 10),
|
|
1479
|
+
actual: parseInt(match[2], 10)
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
if (message !== "" && message.includes("date ordering")) return {
|
|
1483
|
+
type: "DateOrdering",
|
|
1484
|
+
previous: prev.date().toISOString(),
|
|
1485
|
+
next: next.date().toISOString()
|
|
1486
|
+
};
|
|
1487
|
+
if (message !== "" && message.includes("hash mismatch")) {
|
|
1488
|
+
const match = /expected: (\w+), actual: (\w+)/.exec(message);
|
|
1489
|
+
if (match !== null) return {
|
|
1490
|
+
type: "HashMismatch",
|
|
1491
|
+
expected: match[1],
|
|
1492
|
+
actual: match[2]
|
|
1493
|
+
};
|
|
1494
|
+
return {
|
|
1495
|
+
type: "HashMismatch",
|
|
1496
|
+
expected: "",
|
|
1497
|
+
actual: ""
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
return { type: "KeyMismatch" };
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Create a sequence report from flagged marks.
|
|
1504
|
+
*/
|
|
1505
|
+
function createSequenceReport(marks) {
|
|
1506
|
+
return {
|
|
1507
|
+
startSeq: marks.length > 0 ? marks[0].mark.seq() : 0,
|
|
1508
|
+
endSeq: marks.length > 0 ? marks[marks.length - 1].mark.seq() : 0,
|
|
1509
|
+
marks
|
|
1510
|
+
};
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Validate a collection of provenance marks.
|
|
1514
|
+
*/
|
|
1515
|
+
function validate(marks) {
|
|
1516
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1517
|
+
const deduplicatedMarks = [];
|
|
1518
|
+
for (const mark of marks) {
|
|
1519
|
+
const key = mark.toUrlEncoding();
|
|
1520
|
+
if (!seen.has(key)) {
|
|
1521
|
+
seen.add(key);
|
|
1522
|
+
deduplicatedMarks.push(mark);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
const chainBins = /* @__PURE__ */ new Map();
|
|
1526
|
+
for (const mark of deduplicatedMarks) {
|
|
1527
|
+
const chainIdKey = hexEncode(mark.chainId());
|
|
1528
|
+
const bin = chainBins.get(chainIdKey);
|
|
1529
|
+
if (bin !== void 0) bin.push(mark);
|
|
1530
|
+
else chainBins.set(chainIdKey, [mark]);
|
|
1531
|
+
}
|
|
1532
|
+
const chains = [];
|
|
1533
|
+
for (const [chainIdKey, chainMarks] of chainBins) {
|
|
1534
|
+
chainMarks.sort((a, b) => a.seq() - b.seq());
|
|
1535
|
+
const hasGenesis = chainMarks.length > 0 && chainMarks[0].seq() === 0 && chainMarks[0].isGenesis();
|
|
1536
|
+
const sequences = buildSequenceBins(chainMarks);
|
|
1537
|
+
chains.push({
|
|
1538
|
+
chainId: hexDecode(chainIdKey),
|
|
1539
|
+
hasGenesis,
|
|
1540
|
+
marks: chainMarks,
|
|
1541
|
+
sequences
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1544
|
+
chains.sort((a, b) => hexEncode(a.chainId).localeCompare(hexEncode(b.chainId)));
|
|
1545
|
+
return {
|
|
1546
|
+
marks: deduplicatedMarks,
|
|
1547
|
+
chains
|
|
1548
|
+
};
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Helper function to encode bytes as hex.
|
|
1552
|
+
*/
|
|
1553
|
+
function hexEncode(bytes) {
|
|
1554
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1555
|
+
}
|
|
1556
|
+
/**
|
|
1557
|
+
* Helper function to decode hex to bytes.
|
|
1558
|
+
*/
|
|
1559
|
+
function hexDecode(hex) {
|
|
1560
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
1561
|
+
for (let i = 0; i < hex.length; i += 2) bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
|
|
1562
|
+
return bytes;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
//#endregion
|
|
1566
|
+
//#region src/mark-info.ts
|
|
1567
|
+
/**
|
|
1568
|
+
* Wrapper for a provenance mark with additional display information.
|
|
1569
|
+
*/
|
|
1570
|
+
var ProvenanceMarkInfo = class ProvenanceMarkInfo {
|
|
1571
|
+
_mark;
|
|
1572
|
+
_ur;
|
|
1573
|
+
_bytewords;
|
|
1574
|
+
_bytemoji;
|
|
1575
|
+
_comment;
|
|
1576
|
+
constructor(mark, ur, bytewords, bytemoji, comment) {
|
|
1577
|
+
this._mark = mark;
|
|
1578
|
+
this._ur = ur;
|
|
1579
|
+
this._bytewords = bytewords;
|
|
1580
|
+
this._bytemoji = bytemoji;
|
|
1581
|
+
this._comment = comment;
|
|
1582
|
+
}
|
|
1583
|
+
/**
|
|
1584
|
+
* Create a new ProvenanceMarkInfo from a mark.
|
|
1585
|
+
*/
|
|
1586
|
+
static new(mark, comment = "") {
|
|
1587
|
+
const tagName = PROVENANCE_MARK.name;
|
|
1588
|
+
if (tagName === void 0) throw new Error("PROVENANCE_MARK tag has no name");
|
|
1589
|
+
const cborValue = decodeCbor(mark.toCborData());
|
|
1590
|
+
return new ProvenanceMarkInfo(mark, UR.new(tagName, cborValue), mark.bytewordsIdentifier(true), mark.bytemojiIdentifier(true), comment);
|
|
1591
|
+
}
|
|
1592
|
+
mark() {
|
|
1593
|
+
return this._mark;
|
|
1594
|
+
}
|
|
1595
|
+
ur() {
|
|
1596
|
+
return this._ur;
|
|
1597
|
+
}
|
|
1598
|
+
bytewords() {
|
|
1599
|
+
return this._bytewords;
|
|
1600
|
+
}
|
|
1601
|
+
bytemoji() {
|
|
1602
|
+
return this._bytemoji;
|
|
1603
|
+
}
|
|
1604
|
+
comment() {
|
|
1605
|
+
return this._comment;
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Generate a markdown summary of the mark.
|
|
1609
|
+
*/
|
|
1610
|
+
markdownSummary() {
|
|
1611
|
+
const lines = [];
|
|
1612
|
+
lines.push("---");
|
|
1613
|
+
lines.push("");
|
|
1614
|
+
lines.push(this._mark.date().toISOString());
|
|
1615
|
+
lines.push("");
|
|
1616
|
+
lines.push(`#### ${this._ur.toString()}`);
|
|
1617
|
+
lines.push("");
|
|
1618
|
+
lines.push(`#### \`${this._bytewords}\``);
|
|
1619
|
+
lines.push("");
|
|
1620
|
+
lines.push(this._bytemoji);
|
|
1621
|
+
lines.push("");
|
|
1622
|
+
if (this._comment.length > 0) {
|
|
1623
|
+
lines.push(this._comment);
|
|
1624
|
+
lines.push("");
|
|
1625
|
+
}
|
|
1626
|
+
return lines.join("\n");
|
|
1627
|
+
}
|
|
1628
|
+
/**
|
|
1629
|
+
* JSON serialization.
|
|
1630
|
+
*/
|
|
1631
|
+
toJSON() {
|
|
1632
|
+
const result = {
|
|
1633
|
+
ur: this._ur.toString(),
|
|
1634
|
+
bytewords: this._bytewords,
|
|
1635
|
+
bytemoji: this._bytemoji,
|
|
1636
|
+
mark: this._mark.toJSON()
|
|
1637
|
+
};
|
|
1638
|
+
if (this._comment.length > 0) result["comment"] = this._comment;
|
|
1639
|
+
return result;
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* Create from JSON object.
|
|
1643
|
+
*/
|
|
1644
|
+
static fromJSON(json) {
|
|
1645
|
+
const urString = json["ur"];
|
|
1646
|
+
const ur = UR.fromURString(urString);
|
|
1647
|
+
const cborBytes = cborData(ur.cbor());
|
|
1648
|
+
const mark = ProvenanceMark.fromCborData(cborBytes);
|
|
1649
|
+
const bytewords = json["bytewords"];
|
|
1650
|
+
const bytemoji = json["bytemoji"];
|
|
1651
|
+
return new ProvenanceMarkInfo(mark, ur, bytewords, bytemoji, typeof json["comment"] === "string" ? json["comment"] : "");
|
|
1652
|
+
}
|
|
1653
|
+
};
|
|
1654
|
+
|
|
1655
|
+
//#endregion
|
|
1656
|
+
export { PROVENANCE_SEED_LENGTH, ProvenanceMark, ProvenanceMarkError, ProvenanceMarkErrorType, ProvenanceMarkGenerator, ProvenanceMarkInfo, ProvenanceMarkResolution, ProvenanceSeed, RNG_STATE_LENGTH, RngState, SHA256_SIZE, ValidationReportFormat, Xoshiro256StarStar, chainIdHex, chainIdRange, dateBytesLength, dateBytesRange, dateFromIso8601, dateToDateString, dateToIso8601, deserialize2Bytes, deserialize4Bytes, deserialize6Bytes, deserializeDate, deserializeSeq, extendKey, fixedLength, formatReport, formatValidationIssue, hasIssues, hashRange, hkdfHmacSha256, infoRangeStart, keyRange, linkLength, obfuscate, rangeOfDaysInMonth, resolutionFromCbor, resolutionFromNumber, resolutionToCbor, resolutionToNumber, resolutionToString, seqBytesLength, seqBytesRange, serialize2Bytes, serialize4Bytes, serialize6Bytes, serializeDate, serializeSeq, sha256, sha256Prefix, validate };
|
|
1657
|
+
//# sourceMappingURL=index.mjs.map
|