@substrate-system/crypto-stream 0.0.33

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.
Files changed (43) hide show
  1. package/LICENSE +99 -0
  2. package/README.md +366 -0
  3. package/dist/concat-streams.cjs +59 -0
  4. package/dist/ece.cjs +375 -0
  5. package/dist/extract-transformer.cjs +55 -0
  6. package/dist/index.cjs +28 -0
  7. package/dist/keychain.cjs +344 -0
  8. package/dist/slice-transformer.cjs +75 -0
  9. package/dist/src/concat-streams.d.ts +9 -0
  10. package/dist/src/concat-streams.d.ts.map +1 -0
  11. package/dist/src/concat-streams.js +46 -0
  12. package/dist/src/concat-streams.js.map +1 -0
  13. package/dist/src/ece.d.ts +66 -0
  14. package/dist/src/ece.d.ts.map +1 -0
  15. package/dist/src/ece.js +373 -0
  16. package/dist/src/ece.js.map +1 -0
  17. package/dist/src/extract-transformer.d.ts +18 -0
  18. package/dist/src/extract-transformer.d.ts.map +1 -0
  19. package/dist/src/extract-transformer.js +40 -0
  20. package/dist/src/extract-transformer.js.map +1 -0
  21. package/dist/src/index.d.ts +3 -0
  22. package/dist/src/index.d.ts.map +1 -0
  23. package/dist/src/index.js +3 -0
  24. package/dist/src/index.js.map +1 -0
  25. package/dist/src/keychain.d.ts +103 -0
  26. package/dist/src/keychain.d.ts.map +1 -0
  27. package/dist/src/keychain.js +267 -0
  28. package/dist/src/keychain.js.map +1 -0
  29. package/dist/src/slice-transformer.d.ts +19 -0
  30. package/dist/src/slice-transformer.d.ts.map +1 -0
  31. package/dist/src/slice-transformer.js +58 -0
  32. package/dist/src/slice-transformer.js.map +1 -0
  33. package/dist/src/transform-stream.d.ts +11 -0
  34. package/dist/src/transform-stream.d.ts.map +1 -0
  35. package/dist/src/transform-stream.js +136 -0
  36. package/dist/src/transform-stream.js.map +1 -0
  37. package/dist/src/util.d.ts +27 -0
  38. package/dist/src/util.d.ts.map +1 -0
  39. package/dist/src/util.js +49 -0
  40. package/dist/src/util.js.map +1 -0
  41. package/dist/transform-stream.cjs +159 -0
  42. package/dist/util.cjs +57 -0
  43. package/package.json +86 -0
package/dist/ece.cjs ADDED
@@ -0,0 +1,375 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var ece_exports = {};
21
+ __export(ece_exports, {
22
+ KEY_LENGTH: () => KEY_LENGTH,
23
+ TAG_LENGTH: () => TAG_LENGTH,
24
+ decryptStream: () => decryptStream,
25
+ decryptStreamRange: () => decryptStreamRange,
26
+ encryptStream: () => encryptStream,
27
+ encryptedSize: () => encryptedSize,
28
+ plaintextSize: () => plaintextSize
29
+ });
30
+ module.exports = __toCommonJS(ece_exports);
31
+ var import_concat_streams = require("./concat-streams.js");
32
+ var import_transform_stream = require("./transform-stream.js");
33
+ var import_slice_transformer = require("./slice-transformer.js");
34
+ var import_one_webcrypto = require("@substrate-system/one-webcrypto");
35
+ var import_extract_transformer = require("./extract-transformer.js");
36
+ var import_util = require("./util.js");
37
+ const MODE_ENCRYPT = "encrypt";
38
+ const MODE_DECRYPT = "decrypt";
39
+ const KEY_LENGTH = 16;
40
+ const TAG_LENGTH = 16;
41
+ const NONCE_LENGTH = 12;
42
+ const RECORD_SIZE = 64 * 1024;
43
+ const HEADER_LENGTH = KEY_LENGTH + 4 + 1;
44
+ const encoder = new TextEncoder();
45
+ class ECETransformer {
46
+ static {
47
+ __name(this, "ECETransformer");
48
+ }
49
+ mode;
50
+ secretKey;
51
+ rs;
52
+ salt;
53
+ seekOpts;
54
+ seq;
55
+ prevChunk;
56
+ nonceBase;
57
+ key;
58
+ constructor(mode, secretKey, rs, salt, seekOpts = {}) {
59
+ if (mode !== MODE_ENCRYPT && mode !== MODE_DECRYPT) {
60
+ throw new Error("mode must be either 'encrypt' or 'decrypt'");
61
+ }
62
+ checkSecretKey(secretKey);
63
+ if (salt != null && salt.byteLength !== KEY_LENGTH) {
64
+ throw new Error("Invalid salt length");
65
+ }
66
+ this.mode = mode;
67
+ this.secretKey = secretKey;
68
+ this.rs = rs;
69
+ this.salt = salt;
70
+ this.seekOpts = seekOpts;
71
+ this.seq = -1;
72
+ this.prevChunk = null;
73
+ this.nonceBase = null;
74
+ this.key = null;
75
+ }
76
+ async generateKey() {
77
+ return import_one_webcrypto.webcrypto.subtle.deriveKey(
78
+ {
79
+ name: "HKDF",
80
+ hash: "SHA-256",
81
+ salt: this.salt,
82
+ info: encoder.encode("Content-Encoding: aes128gcm\0")
83
+ },
84
+ this.secretKey,
85
+ {
86
+ name: "AES-GCM",
87
+ length: KEY_LENGTH * 8
88
+ },
89
+ false,
90
+ ["encrypt", "decrypt"]
91
+ );
92
+ }
93
+ async generateNonceBase() {
94
+ const nonceBaseBuf = await import_one_webcrypto.webcrypto.subtle.deriveBits(
95
+ {
96
+ name: "HKDF",
97
+ hash: "SHA-256",
98
+ salt: this.salt,
99
+ info: encoder.encode("Content-Encoding: nonce\0")
100
+ },
101
+ this.secretKey,
102
+ NONCE_LENGTH * 8
103
+ );
104
+ return new Uint8Array(nonceBaseBuf);
105
+ }
106
+ generateNonce(seq) {
107
+ if (seq > 4294967295) {
108
+ throw new Error("record sequence number exceeds limit");
109
+ }
110
+ if (!this.nonceBase) throw new Error("Not nonce base");
111
+ const nonce = this.nonceBase.slice();
112
+ const dv = new DataView(nonce.buffer, nonce.byteOffset, nonce.byteLength);
113
+ const m = dv.getUint32(nonce.byteLength - 4);
114
+ const xor = (m ^ seq) >>> 0;
115
+ dv.setUint32(nonce.byteLength - 4, xor);
116
+ return nonce;
117
+ }
118
+ pad(data, isLast) {
119
+ const len = data.byteLength;
120
+ if (len + TAG_LENGTH >= this.rs) {
121
+ throw new Error("data too large for record size");
122
+ }
123
+ let padding;
124
+ if (isLast) {
125
+ padding = Uint8Array.of(2);
126
+ } else {
127
+ padding = new Uint8Array(this.rs - len - TAG_LENGTH);
128
+ padding[0] = 1;
129
+ }
130
+ const result = new Uint8Array(data.byteLength + padding.byteLength);
131
+ result.set(data, 0);
132
+ result.set(padding, data.byteLength);
133
+ return result;
134
+ }
135
+ unpad(data, isLast) {
136
+ for (let i = data.byteLength - 1; i >= 0; i -= 1) {
137
+ if (data[i] !== 0) {
138
+ if (isLast) {
139
+ if (data[i] !== 2) {
140
+ throw new Error("delimiter of final record is not 2");
141
+ }
142
+ } else {
143
+ if (data[i] !== 1) {
144
+ throw new Error("delimiter of not final record is not 1");
145
+ }
146
+ }
147
+ return data.slice(0, i);
148
+ }
149
+ }
150
+ throw new Error("no delimiter found");
151
+ }
152
+ createHeader() {
153
+ if (!this.salt) throw new Error("Not salt");
154
+ const header = new Uint8Array(HEADER_LENGTH);
155
+ header.set(this.salt);
156
+ const dv = new DataView(header.buffer, header.byteOffset, header.byteLength);
157
+ dv.setUint32(KEY_LENGTH, this.rs);
158
+ return header;
159
+ }
160
+ readHeader(buffer) {
161
+ if (buffer.byteLength !== HEADER_LENGTH) {
162
+ throw new Error("chunk is not expected header length");
163
+ }
164
+ const header = { salt: null, rs: null };
165
+ const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
166
+ header.salt = buffer.slice(0, KEY_LENGTH);
167
+ header.rs = dv.getUint32(KEY_LENGTH);
168
+ const idlen = dv.getUint8(KEY_LENGTH + 4);
169
+ if (idlen !== 0) {
170
+ throw new Error("Implementation does not support non-zero idlen");
171
+ }
172
+ return header;
173
+ }
174
+ async encryptRecord(record, seq, isLast) {
175
+ const nonce = this.generateNonce(seq);
176
+ const paddedRecord = this.pad(record, isLast);
177
+ if (!this.key) throw new Error("not key");
178
+ const encryptedRecordBuf = await import_one_webcrypto.webcrypto.subtle.encrypt(
179
+ {
180
+ name: "AES-GCM",
181
+ iv: nonce,
182
+ tagLength: TAG_LENGTH * 8
183
+ },
184
+ this.key,
185
+ paddedRecord
186
+ );
187
+ return new Uint8Array(encryptedRecordBuf);
188
+ }
189
+ async decryptRecord(encryptedRecord, seq, isLast) {
190
+ if (!this.key) throw new Error("not key");
191
+ const nonce = this.generateNonce(seq);
192
+ const paddedRecordBuf = await import_one_webcrypto.webcrypto.subtle.decrypt(
193
+ {
194
+ name: "AES-GCM",
195
+ iv: nonce,
196
+ tagLength: TAG_LENGTH * 8
197
+ },
198
+ this.key,
199
+ (0, import_util.asBufferSource)(encryptedRecord)
200
+ );
201
+ const paddedRecord = new Uint8Array(paddedRecordBuf);
202
+ return this.unpad(paddedRecord, isLast);
203
+ }
204
+ async start(controller) {
205
+ if (this.mode === MODE_ENCRYPT) {
206
+ this.key = await this.generateKey();
207
+ this.nonceBase = await this.generateNonceBase();
208
+ controller.enqueue(this.createHeader());
209
+ this.seq += 1;
210
+ }
211
+ }
212
+ async transformPrevChunk(isLast, controller) {
213
+ if (!this.prevChunk) throw new Error("not this.prevChunk");
214
+ if (this.mode === MODE_ENCRYPT) {
215
+ controller.enqueue(
216
+ await this.encryptRecord(this.prevChunk, this.seq, isLast)
217
+ );
218
+ } else {
219
+ if (this.seq === -1) {
220
+ if (!this.prevChunk) throw new Error("not this.prevChunk");
221
+ const header = this.readHeader(this.prevChunk);
222
+ this.salt = (0, import_util.asBufferSource)(header.salt);
223
+ if (this.rs !== null && this.rs !== header.rs) {
224
+ throw new Error(
225
+ "Record size declared in constructor does not match record size in encrypted stream"
226
+ );
227
+ }
228
+ this.rs = header.rs;
229
+ this.key = await this.generateKey();
230
+ this.nonceBase = await this.generateNonceBase();
231
+ const startSeq = this.seekOpts.startSeq;
232
+ if (startSeq != null && startSeq > 0) {
233
+ this.seq += startSeq;
234
+ }
235
+ } else {
236
+ let expectEndPadding = false;
237
+ if (isLast) {
238
+ const endSeq = this.seekOpts.endSeq;
239
+ if (endSeq != null && endSeq !== this.seq + 1) {
240
+ throw new Error("Incorrect encrypted stream length");
241
+ }
242
+ expectEndPadding = !this.seekOpts.endsPrematurely;
243
+ }
244
+ controller.enqueue(
245
+ await this.decryptRecord(
246
+ this.prevChunk,
247
+ this.seq,
248
+ expectEndPadding
249
+ )
250
+ );
251
+ }
252
+ }
253
+ this.seq += 1;
254
+ }
255
+ async transform(chunk, controller) {
256
+ if (this.prevChunk) {
257
+ await this.transformPrevChunk(false, controller);
258
+ }
259
+ this.prevChunk = chunk;
260
+ }
261
+ async flush(controller) {
262
+ if (this.prevChunk) {
263
+ await this.transformPrevChunk(true, controller);
264
+ }
265
+ }
266
+ }
267
+ function encryptedSize(plaintextSize2, rs = RECORD_SIZE) {
268
+ if (!Number.isInteger(plaintextSize2)) {
269
+ throw new TypeError("plaintextSize");
270
+ }
271
+ if (!Number.isInteger(rs)) {
272
+ throw new TypeError("rs");
273
+ }
274
+ const chunkMetaLength = TAG_LENGTH + 1;
275
+ return HEADER_LENGTH + plaintextSize2 + chunkMetaLength * Math.ceil(plaintextSize2 / (rs - chunkMetaLength));
276
+ }
277
+ __name(encryptedSize, "encryptedSize");
278
+ function plaintextSize(encryptedSize2, rs = RECORD_SIZE) {
279
+ if (!Number.isInteger(encryptedSize2)) {
280
+ throw new TypeError("encryptedSize");
281
+ }
282
+ if (!Number.isInteger(rs)) {
283
+ throw new TypeError("rs");
284
+ }
285
+ const chunkMetaLength = TAG_LENGTH + 1;
286
+ const encryptedRecordsSize = encryptedSize2 - HEADER_LENGTH;
287
+ return encryptedRecordsSize - chunkMetaLength * Math.ceil(encryptedRecordsSize / rs);
288
+ }
289
+ __name(plaintextSize, "plaintextSize");
290
+ function encryptStream(input, secretKey, rs = RECORD_SIZE, salt = (0, import_util.generateSalt)(KEY_LENGTH)) {
291
+ const stream = (0, import_transform_stream.transformStream)(
292
+ input,
293
+ new import_slice_transformer.SliceTransformer(rs - TAG_LENGTH - 1)
294
+ ).readable;
295
+ return (0, import_transform_stream.transformStream)(
296
+ stream,
297
+ new ECETransformer(MODE_ENCRYPT, secretKey, rs, salt)
298
+ ).readable;
299
+ }
300
+ __name(encryptStream, "encryptStream");
301
+ function decryptStream(input, secretKey, rs = RECORD_SIZE) {
302
+ const stream = (0, import_transform_stream.transformStream)(
303
+ input,
304
+ new import_slice_transformer.SliceTransformer(HEADER_LENGTH, rs)
305
+ ).readable;
306
+ return (0, import_transform_stream.transformStream)(
307
+ stream,
308
+ new ECETransformer(MODE_DECRYPT, secretKey, rs, null)
309
+ ).readable;
310
+ }
311
+ __name(decryptStream, "decryptStream");
312
+ function decryptStreamRange(secretKey, offset, length, totalEncryptedLength, rs = RECORD_SIZE) {
313
+ if (!Number.isInteger(rs)) {
314
+ throw new TypeError("Missing record size (rs)");
315
+ }
316
+ const chunkMetaLength = TAG_LENGTH + 1;
317
+ const startRecord = Math.floor(offset / (rs - chunkMetaLength));
318
+ const offsetInStartRecord = offset % (rs - chunkMetaLength);
319
+ const endRecord = Math.ceil((offset + length) / (rs - chunkMetaLength));
320
+ const dataOffset = HEADER_LENGTH + startRecord * rs;
321
+ let dataEnd = HEADER_LENGTH + endRecord * rs;
322
+ const endsPrematurely = dataEnd < totalEncryptedLength;
323
+ if (!endsPrematurely) {
324
+ dataEnd = totalEncryptedLength;
325
+ }
326
+ return {
327
+ ranges: [
328
+ {
329
+ offset: 0,
330
+ length: HEADER_LENGTH
331
+ },
332
+ {
333
+ offset: dataOffset,
334
+ length: dataEnd - dataOffset
335
+ }
336
+ ],
337
+ decrypt: /* @__PURE__ */ __name((streams) => {
338
+ if (!streams.every((stream) => stream instanceof ReadableStream)) {
339
+ throw new TypeError("Stream is not a ReadableStream");
340
+ }
341
+ const encryptedStream = (0, import_transform_stream.transformStream)(
342
+ (0, import_concat_streams.concatStreams)(streams),
343
+ new import_slice_transformer.SliceTransformer(HEADER_LENGTH, rs)
344
+ ).readable;
345
+ const plaintextStream = (0, import_transform_stream.transformStream)(
346
+ encryptedStream,
347
+ new ECETransformer(MODE_DECRYPT, secretKey, rs, null, {
348
+ startSeq: startRecord,
349
+ endSeq: endRecord,
350
+ endsPrematurely
351
+ })
352
+ ).readable;
353
+ return (0, import_transform_stream.transformStream)(
354
+ plaintextStream,
355
+ new import_extract_transformer.ExtractTransformer(offsetInStartRecord, length)
356
+ ).readable;
357
+ }, "decrypt")
358
+ };
359
+ }
360
+ __name(decryptStreamRange, "decryptStreamRange");
361
+ function checkSecretKey(secretKey) {
362
+ if (secretKey.type !== "secret") {
363
+ throw new Error('Invalid key: type must be "secret"');
364
+ }
365
+ if (secretKey.algorithm.name !== "HKDF") {
366
+ throw new Error("Invalid key: algorithm must be HKDF");
367
+ }
368
+ if (!secretKey.usages.includes("deriveKey")) {
369
+ throw new Error("Invalid key: usages must include deriveKey");
370
+ }
371
+ if (!secretKey.usages.includes("deriveBits")) {
372
+ throw new Error("Invalid key: usages must include deriveBits");
373
+ }
374
+ }
375
+ __name(checkSecretKey, "checkSecretKey");
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var extract_transformer_exports = {};
21
+ __export(extract_transformer_exports, {
22
+ ExtractTransformer: () => ExtractTransformer
23
+ });
24
+ module.exports = __toCommonJS(extract_transformer_exports);
25
+ class ExtractTransformer {
26
+ static {
27
+ __name(this, "ExtractTransformer");
28
+ }
29
+ extractStart;
30
+ extractEnd;
31
+ offset;
32
+ constructor(offset, length) {
33
+ this.extractStart = offset;
34
+ this.extractEnd = offset + length;
35
+ this.offset = 0;
36
+ }
37
+ transform(chunk, controller) {
38
+ const chunkStart = this.offset;
39
+ const chunkEnd = this.offset + chunk.byteLength;
40
+ this.offset = chunkEnd;
41
+ const sliceStart = Math.max(this.extractStart - chunkStart, 0);
42
+ const sliceEnd = Math.min(this.extractEnd - chunkStart, chunk.byteLength);
43
+ if (sliceStart >= chunk.byteLength || sliceEnd <= 0) {
44
+ return;
45
+ }
46
+ controller.enqueue(chunk.subarray(sliceStart, sliceEnd));
47
+ }
48
+ flush(controller) {
49
+ if (this.offset < this.extractEnd) {
50
+ controller.error(
51
+ new Error("Stream passed through ExtractTransformer ended early")
52
+ );
53
+ }
54
+ }
55
+ }
package/dist/index.cjs ADDED
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var index_exports = {};
20
+ __export(index_exports, {
21
+ Keychain: () => import_keychain.Keychain,
22
+ encryptedSize: () => import_keychain.encryptedSize,
23
+ plaintextSize: () => import_keychain.plaintextSize,
24
+ transformStream: () => import_transform_stream.transformStream
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+ var import_keychain = require("./keychain.js");
28
+ var import_transform_stream = require("./transform-stream.js");