@zip.js/zip.js 2.13.1 → 2.14.0
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/BENCHMARKS.md +46 -1
- package/deno.json +1 -1
- package/dist/zip-core-external.js +559 -924
- package/dist/zip-core-external.min.js +1 -1
- package/dist/zip-core.js +426 -897
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core-external.js +566 -927
- package/dist/zip-fs-core-external.min.js +1 -1
- package/dist/zip-fs-core.js +433 -900
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-external.js +566 -927
- package/dist/zip-fs-external.min.js +1 -1
- package/dist/zip-fs-native.js +435 -902
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +569 -930
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +428 -899
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-module.wasm +0 -0
- package/dist/zip-native.js +428 -899
- package/dist/zip-native.min.js +1 -1
- package/dist/zip-web-worker-native.js +1 -1
- package/dist/zip-web-worker.js +1 -1
- package/dist/zip.js +562 -927
- package/dist/zip.min.js +1 -1
- package/index-native.cjs +435 -902
- package/index-native.min.js +1 -1
- package/index.cjs +569 -930
- package/index.d.cts +52 -12
- package/index.d.ts +52 -12
- package/index.min.js +1 -1
- package/lib/core/codec-worker.js +33 -23
- package/lib/core/io.js +29 -21
- package/lib/core/streams/aes-crypto-stream.js +58 -99
- package/lib/core/streams/codecs/aes-hmac-sha1.js +348 -0
- package/lib/core/streams/zlib-wasm/aes-hmac-sha1-wasm.js +106 -0
- package/lib/core/streams/zlib-wasm/zlib-streams-loader.js +4 -0
- package/lib/core/streams/zlib-wasm/zlib-streams.wasm +0 -0
- package/lib/core/version.js +1 -1
- package/lib/core/web-worker-base.js +15 -9
- package/lib/core/web-worker-inline-native.js +1 -1
- package/lib/core/web-worker-inline-wasm.js +1 -1
- package/lib/core/web-worker-wasm.js +2 -0
- package/lib/core/zip-fs.js +7 -3
- package/lib/core/zip-writer.js +1 -2
- package/lib/core/zlib-streams-inline.js +1 -1
- package/lib/zip-module-wasm-base.js +3 -1
- package/package.json +1 -1
- package/lib/core/streams/codecs/sjcl.js +0 -795
package/lib/core/codec-worker.js
CHANGED
|
@@ -78,11 +78,18 @@ async function supportsDeflate(config) {
|
|
|
78
78
|
return true;
|
|
79
79
|
}
|
|
80
80
|
if (FallbackStream) {
|
|
81
|
+
return await loadModule(config);
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function loadModule(config) {
|
|
87
|
+
if (initModule) {
|
|
81
88
|
try {
|
|
82
89
|
await initModule(config);
|
|
83
90
|
return true;
|
|
84
91
|
} catch {
|
|
85
|
-
|
|
92
|
+
// ignored
|
|
86
93
|
}
|
|
87
94
|
}
|
|
88
95
|
return false;
|
|
@@ -125,26 +132,27 @@ class CodecWorker {
|
|
|
125
132
|
terminate() {
|
|
126
133
|
return new Promise(resolve => {
|
|
127
134
|
const { worker, busy } = workerData;
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
if (busy) {
|
|
136
|
+
workerData.resolveTerminated = resolve;
|
|
137
|
+
} else {
|
|
138
|
+
if (worker) {
|
|
132
139
|
worker.terminate();
|
|
133
|
-
|
|
140
|
+
workerData.worker = null;
|
|
134
141
|
}
|
|
135
|
-
workerData.interface = null;
|
|
136
|
-
} else {
|
|
137
142
|
resolve();
|
|
138
143
|
}
|
|
144
|
+
workerData.interface = null;
|
|
139
145
|
});
|
|
140
146
|
},
|
|
141
147
|
onTaskFinished() {
|
|
142
148
|
if (workerData.busy) {
|
|
143
|
-
const { resolveTerminated } = workerData;
|
|
149
|
+
const { resolveTerminated, worker } = workerData;
|
|
144
150
|
if (resolveTerminated) {
|
|
145
151
|
workerData.resolveTerminated = null;
|
|
146
|
-
|
|
147
|
-
|
|
152
|
+
if (worker) {
|
|
153
|
+
workerData.terminated = true;
|
|
154
|
+
worker.terminate();
|
|
155
|
+
}
|
|
148
156
|
resolveTerminated();
|
|
149
157
|
}
|
|
150
158
|
workerData.busy = false;
|
|
@@ -201,28 +209,23 @@ function createWorkerInterface(workerData, config) {
|
|
|
201
209
|
}
|
|
202
210
|
|
|
203
211
|
async function runWorker({ options, readable, writable, onTaskFinished, workerOptions }, config) {
|
|
204
|
-
let codecStream, chunkStream;
|
|
212
|
+
let codecStream, chunkStream, modulePromise;
|
|
205
213
|
try {
|
|
206
214
|
if (options.compressed && !options.format) {
|
|
207
215
|
const deflate = options.codecType.startsWith(CODEC_DEFLATE);
|
|
208
216
|
const FallbackStream = deflate ? config.CompressionStreamFallback : config.DecompressionStreamFallback;
|
|
209
217
|
const NativeStream = deflate ? config.CompressionStream : config.DecompressionStream;
|
|
210
218
|
if (!options.useCompressionStream) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
} catch {
|
|
214
|
-
if (!FallbackStream || FallbackStream.requiresModule) {
|
|
215
|
-
options.useCompressionStream = true;
|
|
216
|
-
}
|
|
219
|
+
if (!await moduleLoaded() && (!FallbackStream || FallbackStream.requiresModule)) {
|
|
220
|
+
options.useCompressionStream = true;
|
|
217
221
|
}
|
|
218
222
|
} else if (FallbackStream && FallbackStream.requiresModule && !supportsDeflateRaw(NativeStream)) {
|
|
219
|
-
|
|
220
|
-
await initModule(config);
|
|
221
|
-
} catch {
|
|
222
|
-
// ignored
|
|
223
|
-
}
|
|
223
|
+
await moduleLoaded();
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
|
+
if (options.encrypted && !options.zipCrypto) {
|
|
227
|
+
await moduleLoaded();
|
|
228
|
+
}
|
|
226
229
|
codecStream = new CodecStream(options, config);
|
|
227
230
|
chunkStream = new ChunkStream(getChunkSize(config));
|
|
228
231
|
await readable
|
|
@@ -255,4 +258,11 @@ async function runWorker({ options, readable, writable, onTaskFinished, workerOp
|
|
|
255
258
|
} finally {
|
|
256
259
|
onTaskFinished();
|
|
257
260
|
}
|
|
261
|
+
|
|
262
|
+
function moduleLoaded() {
|
|
263
|
+
if (!modulePromise) {
|
|
264
|
+
modulePromise = loadModule(config);
|
|
265
|
+
}
|
|
266
|
+
return modulePromise;
|
|
267
|
+
}
|
|
258
268
|
}
|
package/lib/core/io.js
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
FUNCTION_TYPE,
|
|
35
35
|
UNDEFINED_TYPE,
|
|
36
36
|
END_OF_CENTRAL_DIR_LENGTH,
|
|
37
|
+
MAX_16_BITS,
|
|
37
38
|
EMPTY_UINT8_ARRAY
|
|
38
39
|
} from "./constants.js";
|
|
39
40
|
import { getConfiguration, getChunkSize, normalizeChunkSize } from "./configuration.js";
|
|
@@ -63,6 +64,7 @@ const HTTP_METHOD_GET = "GET";
|
|
|
63
64
|
const HTTP_RANGE_UNIT = "bytes";
|
|
64
65
|
const DEFAULT_BUFFER_SIZE = 256 * 1024;
|
|
65
66
|
const DEFAULT_MAXIMUM_RANGE_SIZE = 16 * 1024 * 1024;
|
|
67
|
+
const END_OF_CENTRAL_DIR_SEARCH_LENGTH = END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS;
|
|
66
68
|
|
|
67
69
|
const PROPERTY_NAME_WRITABLE = "writable";
|
|
68
70
|
const DISK_BOUNDARY = Symbol();
|
|
@@ -425,16 +427,14 @@ async function initHttpReader(httpReader, sendRequest, getRequestData) {
|
|
|
425
427
|
combineSizeEocd
|
|
426
428
|
} = httpReader;
|
|
427
429
|
if (isHttpFamily(url) && (useRangeHeader || forceRangeRequests) && (typeof preventHeadRequest == UNDEFINED_TYPE || preventHeadRequest)) {
|
|
428
|
-
const response = await sendRequest(HTTP_METHOD_GET, httpReader, getRangeHeaders(httpReader, combineSizeEocd ? -
|
|
430
|
+
const response = await sendRequest(HTTP_METHOD_GET, httpReader, getRangeHeaders(httpReader, combineSizeEocd ? -END_OF_CENTRAL_DIR_SEARCH_LENGTH : undefined));
|
|
429
431
|
const acceptRanges = response.headers.get(HTTP_HEADER_ACCEPT_RANGES);
|
|
430
432
|
if (!forceRangeRequests && (!acceptRanges || acceptRanges.toLowerCase() != HTTP_RANGE_UNIT)) {
|
|
431
433
|
throw new Error(ERR_HTTP_RANGE);
|
|
432
434
|
} else {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
httpReader.eocdCache = eocdCache;
|
|
437
|
-
}
|
|
435
|
+
let eocdCache;
|
|
436
|
+
if (combineSizeEocd && response.status == 206) {
|
|
437
|
+
eocdCache = new Uint8Array(await response.arrayBuffer());
|
|
438
438
|
}
|
|
439
439
|
setResourceValidators(httpReader, response);
|
|
440
440
|
const contentSize = getContentRangeSize(response);
|
|
@@ -443,6 +443,9 @@ async function initHttpReader(httpReader, sendRequest, getRequestData) {
|
|
|
443
443
|
} else {
|
|
444
444
|
httpReader.size = contentSize;
|
|
445
445
|
}
|
|
446
|
+
if (eocdCache && eocdCache.length && getContentRangeOffset(response) === httpReader.size - eocdCache.length) {
|
|
447
|
+
httpReader.eocdCache = eocdCache;
|
|
448
|
+
}
|
|
446
449
|
}
|
|
447
450
|
} else {
|
|
448
451
|
await getContentLength(httpReader, sendRequest, getRequestData);
|
|
@@ -458,25 +461,23 @@ async function readUint8ArrayHttpReader(httpReader, index, length, sendRequest,
|
|
|
458
461
|
options
|
|
459
462
|
} = httpReader;
|
|
460
463
|
if (useRangeHeader || forceRangeRequests) {
|
|
461
|
-
if (eocdCache && index == size - END_OF_CENTRAL_DIR_LENGTH && length == END_OF_CENTRAL_DIR_LENGTH) {
|
|
462
|
-
return eocdCache;
|
|
463
|
-
}
|
|
464
464
|
if (index >= size || length === 0) {
|
|
465
465
|
return EMPTY_UINT8_ARRAY;
|
|
466
466
|
} else {
|
|
467
467
|
if (index + length > size) {
|
|
468
468
|
length = size - index;
|
|
469
469
|
}
|
|
470
|
+
if (eocdCache && index >= size - eocdCache.length) {
|
|
471
|
+
const cacheIndex = index - (size - eocdCache.length);
|
|
472
|
+
return eocdCache.slice(cacheIndex, cacheIndex + length);
|
|
473
|
+
}
|
|
470
474
|
const response = await sendRequest(HTTP_METHOD_GET, httpReader, getRangeHeaders(httpReader, index, length));
|
|
471
475
|
if (response.status != 206) {
|
|
472
476
|
throw new Error(ERR_HTTP_RANGE);
|
|
473
477
|
}
|
|
474
|
-
const
|
|
475
|
-
if (
|
|
476
|
-
|
|
477
|
-
if (!Number.isNaN(rangeStart) && rangeStart != index) {
|
|
478
|
-
throw new Error(ERR_HTTP_RANGE);
|
|
479
|
-
}
|
|
478
|
+
const rangeStart = getContentRangeOffset(response);
|
|
479
|
+
if (rangeStart !== UNDEFINED_VALUE && rangeStart != index) {
|
|
480
|
+
throw new Error(ERR_HTTP_RANGE);
|
|
480
481
|
}
|
|
481
482
|
checkResourceValidators(httpReader, response);
|
|
482
483
|
setResourceValidators(httpReader, response);
|
|
@@ -536,12 +537,9 @@ function createRangeReadable(httpReader, offset, size) {
|
|
|
536
537
|
if (response.status != 206) {
|
|
537
538
|
throw new Error(ERR_HTTP_RANGE);
|
|
538
539
|
}
|
|
539
|
-
const
|
|
540
|
-
if (
|
|
541
|
-
|
|
542
|
-
if (!Number.isNaN(rangeStart) && rangeStart != windowOffset) {
|
|
543
|
-
throw new Error(ERR_HTTP_RANGE);
|
|
544
|
-
}
|
|
540
|
+
const rangeStart = getContentRangeOffset(response);
|
|
541
|
+
if (rangeStart !== UNDEFINED_VALUE && rangeStart != windowOffset) {
|
|
542
|
+
throw new Error(ERR_HTTP_RANGE);
|
|
545
543
|
}
|
|
546
544
|
checkResourceValidators(httpReader, response);
|
|
547
545
|
setResourceValidators(httpReader, response);
|
|
@@ -557,6 +555,16 @@ function createRangeReadable(httpReader, offset, size) {
|
|
|
557
555
|
}
|
|
558
556
|
}
|
|
559
557
|
|
|
558
|
+
function getContentRangeOffset(response) {
|
|
559
|
+
const contentRangeHeader = response.headers.get(HTTP_HEADER_CONTENT_RANGE);
|
|
560
|
+
if (contentRangeHeader) {
|
|
561
|
+
const rangeStart = Number(contentRangeHeader.trim().split(/[\s-]+/)[1]);
|
|
562
|
+
if (!Number.isNaN(rangeStart)) {
|
|
563
|
+
return rangeStart;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
560
568
|
function getContentRangeSize(response) {
|
|
561
569
|
const contentRangeHeader = response.headers.get(HTTP_HEADER_CONTENT_RANGE);
|
|
562
570
|
if (contentRangeHeader) {
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
1. Redistributions of source code must retain the above copyright notice,
|
|
8
8
|
this list of conditions and the following disclaimer.
|
|
9
9
|
|
|
10
|
-
2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
-
notice, this list of conditions and the following disclaimer in
|
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
notice, this list of conditions and the following disclaimer in
|
|
12
12
|
the documentation and/or other materials provided with the distribution.
|
|
13
13
|
|
|
14
14
|
3. The names of the authors may not be used to endorse or promote products
|
|
@@ -32,12 +32,7 @@
|
|
|
32
32
|
import { UNDEFINED_VALUE, UNDEFINED_TYPE, FUNCTION_TYPE, EMPTY_UINT8_ARRAY } from "../constants.js";
|
|
33
33
|
import { encodeText } from "./../util/encode-text.js";
|
|
34
34
|
import { concat } from "./../util/array.js";
|
|
35
|
-
import {
|
|
36
|
-
cipher,
|
|
37
|
-
codec,
|
|
38
|
-
misc,
|
|
39
|
-
mode
|
|
40
|
-
} from "./codecs/sjcl.js";
|
|
35
|
+
import { createEngine as createDefaultEngine, pbkdf2 } from "./codecs/aes-hmac-sha1.js";
|
|
41
36
|
import {
|
|
42
37
|
ERR_INVALID_PASSWORD,
|
|
43
38
|
ERR_INVALID_AUTHENTICATION_CODE,
|
|
@@ -50,24 +45,21 @@ const RAW_FORMAT = "raw";
|
|
|
50
45
|
const PBKDF2_ALGORITHM = { name: "PBKDF2" };
|
|
51
46
|
const HASH_ALGORITHM = { name: "HMAC" };
|
|
52
47
|
const HASH_FUNCTION = "SHA-1";
|
|
48
|
+
const PBKDF2_ITERATIONS = 1000;
|
|
53
49
|
const BASE_KEY_ALGORITHM = Object.assign({ hash: HASH_ALGORITHM }, PBKDF2_ALGORITHM);
|
|
54
|
-
const DERIVED_BITS_ALGORITHM = Object.assign({ iterations:
|
|
50
|
+
const DERIVED_BITS_ALGORITHM = Object.assign({ iterations: PBKDF2_ITERATIONS, hash: { name: HASH_FUNCTION } }, PBKDF2_ALGORITHM);
|
|
55
51
|
const DERIVED_BITS_USAGE = ["deriveBits"];
|
|
56
52
|
const SALT_LENGTH = [8, 12, 16];
|
|
57
53
|
const KEY_LENGTH = [16, 24, 32];
|
|
58
54
|
const AUTHENTICATION_CODE_LENGTH = 10;
|
|
59
|
-
const
|
|
55
|
+
const PASSWORD_VERIFICATION_LENGTH = 2;
|
|
60
56
|
// deno-lint-ignore valid-typeof
|
|
61
57
|
const CRYPTO_API_SUPPORTED = typeof crypto != UNDEFINED_TYPE;
|
|
62
58
|
const subtle = CRYPTO_API_SUPPORTED && crypto.subtle;
|
|
63
59
|
const SUBTLE_API_SUPPORTED = CRYPTO_API_SUPPORTED && typeof subtle != UNDEFINED_TYPE;
|
|
64
|
-
const codecBytes = codec.bytes;
|
|
65
|
-
const Aes = cipher.aes;
|
|
66
|
-
const CtrGladman = mode.ctrGladman;
|
|
67
|
-
const HmacSha1 = misc.hmacSha1;
|
|
68
60
|
|
|
69
|
-
let
|
|
70
|
-
let
|
|
61
|
+
let DERIVE_BITS_SUPPORTED = SUBTLE_API_SUPPORTED && typeof subtle.importKey == FUNCTION_TYPE && typeof subtle.deriveBits == FUNCTION_TYPE;
|
|
62
|
+
let createEngine = createDefaultEngine;
|
|
71
63
|
|
|
72
64
|
class AESDecryptionStream extends TransformStream {
|
|
73
65
|
|
|
@@ -85,9 +77,10 @@ class AESDecryptionStream extends TransformStream {
|
|
|
85
77
|
ready
|
|
86
78
|
} = aesCrypto;
|
|
87
79
|
if (password) {
|
|
88
|
-
await createDecryptionKeys(aesCrypto, strength, password, subarray(chunk, 0, SALT_LENGTH[strength] +
|
|
89
|
-
chunk = subarray(chunk, SALT_LENGTH[strength] +
|
|
80
|
+
await createDecryptionKeys(aesCrypto, strength, password, subarray(chunk, 0, SALT_LENGTH[strength] + PASSWORD_VERIFICATION_LENGTH));
|
|
81
|
+
chunk = subarray(chunk, SALT_LENGTH[strength] + PASSWORD_VERIFICATION_LENGTH);
|
|
90
82
|
if (checkPasswordOnly) {
|
|
83
|
+
disposeEngine(aesCrypto);
|
|
91
84
|
controller.error(new Error(ERR_ABORT_CHECK_PASSWORD));
|
|
92
85
|
} else {
|
|
93
86
|
resolveReady();
|
|
@@ -100,23 +93,16 @@ class AESDecryptionStream extends TransformStream {
|
|
|
100
93
|
},
|
|
101
94
|
async flush(controller) {
|
|
102
95
|
const {
|
|
103
|
-
|
|
104
|
-
hmac,
|
|
96
|
+
engine,
|
|
105
97
|
pendingInput,
|
|
106
98
|
ready
|
|
107
99
|
} = this;
|
|
108
|
-
if (
|
|
100
|
+
if (engine) {
|
|
109
101
|
await ready;
|
|
110
|
-
const chunkToDecrypt = subarray(pendingInput, 0, pendingInput.length - AUTHENTICATION_CODE_LENGTH);
|
|
111
102
|
const originalAuthenticationCode = subarray(pendingInput, pendingInput.length - AUTHENTICATION_CODE_LENGTH);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
hmac.update(encryptedChunk);
|
|
116
|
-
const decryptedChunk = ctr.update(encryptedChunk);
|
|
117
|
-
decryptedChunkArray = fromBits(codecBytes, decryptedChunk);
|
|
118
|
-
}
|
|
119
|
-
const authenticationCode = subarray(fromBits(codecBytes, hmac.digest()), 0, AUTHENTICATION_CODE_LENGTH);
|
|
103
|
+
const decryptedChunkArray = new Uint8Array(subarray(pendingInput, 0, pendingInput.length - AUTHENTICATION_CODE_LENGTH));
|
|
104
|
+
engine.process(decryptedChunkArray, true);
|
|
105
|
+
const authenticationCode = engine.digest();
|
|
120
106
|
let invalidAuthenticationCode = pendingInput.length < AUTHENTICATION_CODE_LENGTH ? 1 : 0;
|
|
121
107
|
for (let indexByte = 0; indexByte < AUTHENTICATION_CODE_LENGTH; indexByte++) {
|
|
122
108
|
invalidAuthenticationCode |= authenticationCode[indexByte] ^ originalAuthenticationCode[indexByte];
|
|
@@ -126,6 +112,9 @@ class AESDecryptionStream extends TransformStream {
|
|
|
126
112
|
}
|
|
127
113
|
controller.enqueue(decryptedChunkArray);
|
|
128
114
|
}
|
|
115
|
+
},
|
|
116
|
+
cancel() {
|
|
117
|
+
disposeEngine(this);
|
|
129
118
|
}
|
|
130
119
|
});
|
|
131
120
|
}
|
|
@@ -155,26 +144,24 @@ class AESEncryptionStream extends TransformStream {
|
|
|
155
144
|
}
|
|
156
145
|
const output = new Uint8Array(preamble.length + chunk.length - (chunk.length % BLOCK_LENGTH));
|
|
157
146
|
output.set(preamble, 0);
|
|
158
|
-
controller.enqueue(append(aesCrypto, chunk, output, preamble.length, 0));
|
|
147
|
+
controller.enqueue(append(aesCrypto, chunk, output, preamble.length, 0, false));
|
|
159
148
|
},
|
|
160
149
|
async flush(controller) {
|
|
161
150
|
const {
|
|
162
|
-
|
|
163
|
-
hmac,
|
|
151
|
+
engine,
|
|
164
152
|
pendingInput,
|
|
165
153
|
ready
|
|
166
154
|
} = this;
|
|
167
|
-
if (
|
|
155
|
+
if (engine) {
|
|
168
156
|
await ready;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
hmac.update(encryptedChunk);
|
|
173
|
-
encryptedChunkArray = fromBits(codecBytes, encryptedChunk);
|
|
174
|
-
}
|
|
175
|
-
const authenticationCode = fromBits(codecBytes, hmac.digest()).slice(0, AUTHENTICATION_CODE_LENGTH);
|
|
157
|
+
const encryptedChunkArray = new Uint8Array(pendingInput);
|
|
158
|
+
engine.process(encryptedChunkArray, false);
|
|
159
|
+
const authenticationCode = subarray(engine.digest(), 0, AUTHENTICATION_CODE_LENGTH);
|
|
176
160
|
controller.enqueue(concat(encryptedChunkArray, authenticationCode));
|
|
177
161
|
}
|
|
162
|
+
},
|
|
163
|
+
cancel() {
|
|
164
|
+
disposeEngine(this);
|
|
178
165
|
}
|
|
179
166
|
});
|
|
180
167
|
}
|
|
@@ -182,9 +169,14 @@ class AESEncryptionStream extends TransformStream {
|
|
|
182
169
|
|
|
183
170
|
export {
|
|
184
171
|
AESDecryptionStream,
|
|
185
|
-
AESEncryptionStream
|
|
172
|
+
AESEncryptionStream,
|
|
173
|
+
setAESEngine
|
|
186
174
|
};
|
|
187
175
|
|
|
176
|
+
function setAESEngine(createEngineFunction) {
|
|
177
|
+
createEngine = createEngineFunction || createDefaultEngine;
|
|
178
|
+
}
|
|
179
|
+
|
|
188
180
|
function initAesCrypto(aesCrypto, password, rawPassword, encryptionStrength) {
|
|
189
181
|
Object.assign(aesCrypto, {
|
|
190
182
|
ready: new Promise(resolve => aesCrypto.resolveReady = resolve),
|
|
@@ -194,30 +186,23 @@ function initAesCrypto(aesCrypto, password, rawPassword, encryptionStrength) {
|
|
|
194
186
|
});
|
|
195
187
|
}
|
|
196
188
|
|
|
197
|
-
function append(aesCrypto, input, output, paddingStart, paddingEnd,
|
|
189
|
+
function append(aesCrypto, input, output, paddingStart, paddingEnd, decrypt) {
|
|
198
190
|
const {
|
|
199
|
-
|
|
200
|
-
hmac,
|
|
191
|
+
engine,
|
|
201
192
|
pendingInput
|
|
202
193
|
} = aesCrypto;
|
|
203
194
|
if (pendingInput.length) {
|
|
204
195
|
input = concat(pendingInput, input);
|
|
205
196
|
}
|
|
206
197
|
const inputLength = input.length - paddingEnd;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
const outputChunk = ctr.update(inputChunk);
|
|
215
|
-
if (!verifyAuthenticationCode) {
|
|
216
|
-
hmac.update(outputChunk);
|
|
217
|
-
}
|
|
218
|
-
output.set(fromBits(codecBytes, outputChunk), offset + paddingStart);
|
|
198
|
+
const alignedLength = inputLength - (inputLength % BLOCK_LENGTH);
|
|
199
|
+
output = expand(output, paddingStart + alignedLength);
|
|
200
|
+
if (alignedLength) {
|
|
201
|
+
const chunk = subarray(output, paddingStart, paddingStart + alignedLength);
|
|
202
|
+
chunk.set(subarray(input, 0, alignedLength));
|
|
203
|
+
engine.process(chunk, decrypt);
|
|
219
204
|
}
|
|
220
|
-
aesCrypto.pendingInput = subarray(input,
|
|
205
|
+
aesCrypto.pendingInput = subarray(input, alignedLength);
|
|
221
206
|
return output;
|
|
222
207
|
}
|
|
223
208
|
|
|
@@ -225,10 +210,17 @@ async function createDecryptionKeys(decrypt, strength, password, preamble) {
|
|
|
225
210
|
const passwordVerificationKey = await createKeys(decrypt, strength, password, subarray(preamble, 0, SALT_LENGTH[strength]));
|
|
226
211
|
const passwordVerification = subarray(preamble, SALT_LENGTH[strength]);
|
|
227
212
|
if (passwordVerificationKey[0] != passwordVerification[0] || passwordVerificationKey[1] != passwordVerification[1]) {
|
|
213
|
+
disposeEngine(decrypt);
|
|
228
214
|
throw new Error(ERR_INVALID_PASSWORD);
|
|
229
215
|
}
|
|
230
216
|
}
|
|
231
217
|
|
|
218
|
+
function disposeEngine({ engine }) {
|
|
219
|
+
if (engine && engine.dispose) {
|
|
220
|
+
engine.dispose();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
232
224
|
async function createEncryptionKeys(encrypt, strength, password) {
|
|
233
225
|
const salt = getRandomValues(new Uint8Array(SALT_LENGTH[strength]));
|
|
234
226
|
const passwordVerification = await createKeys(encrypt, strength, password, salt);
|
|
@@ -237,48 +229,22 @@ async function createEncryptionKeys(encrypt, strength, password) {
|
|
|
237
229
|
|
|
238
230
|
async function createKeys(aesCrypto, strength, password, salt) {
|
|
239
231
|
aesCrypto.password = null;
|
|
240
|
-
const
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const authentication = toBits(codecBytes, subarray(compositeKey, KEY_LENGTH[strength], KEY_LENGTH[strength] * 2));
|
|
245
|
-
const passwordVerification = subarray(compositeKey, KEY_LENGTH[strength] * 2);
|
|
246
|
-
Object.assign(aesCrypto, {
|
|
247
|
-
keys: {
|
|
248
|
-
key,
|
|
249
|
-
authentication,
|
|
250
|
-
passwordVerification
|
|
251
|
-
},
|
|
252
|
-
ctr: new CtrGladman(new Aes(key), Array.from(COUNTER_DEFAULT_VALUE)),
|
|
253
|
-
hmac: new HmacSha1(authentication)
|
|
254
|
-
});
|
|
255
|
-
return passwordVerification;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
async function importKey(format, password, algorithm, extractable, keyUsages) {
|
|
259
|
-
if (IMPORT_KEY_SUPPORTED) {
|
|
260
|
-
try {
|
|
261
|
-
return await subtle.importKey(format, password, algorithm, extractable, keyUsages);
|
|
262
|
-
} catch {
|
|
263
|
-
IMPORT_KEY_SUPPORTED = false;
|
|
264
|
-
return misc.importKey(password);
|
|
265
|
-
}
|
|
266
|
-
} else {
|
|
267
|
-
return misc.importKey(password);
|
|
268
|
-
}
|
|
232
|
+
const keyLength = KEY_LENGTH[strength];
|
|
233
|
+
const compositeKey = await deriveKey(password, salt, keyLength * 2 + PASSWORD_VERIFICATION_LENGTH);
|
|
234
|
+
aesCrypto.engine = createEngine(subarray(compositeKey, 0, keyLength), subarray(compositeKey, keyLength, keyLength * 2));
|
|
235
|
+
return subarray(compositeKey, keyLength * 2);
|
|
269
236
|
}
|
|
270
237
|
|
|
271
|
-
async function
|
|
238
|
+
async function deriveKey(password, salt, length) {
|
|
272
239
|
if (DERIVE_BITS_SUPPORTED) {
|
|
273
240
|
try {
|
|
274
|
-
|
|
241
|
+
const baseKey = await subtle.importKey(RAW_FORMAT, password, BASE_KEY_ALGORITHM, false, DERIVED_BITS_USAGE);
|
|
242
|
+
return new Uint8Array(await subtle.deriveBits(Object.assign({ salt }, DERIVED_BITS_ALGORITHM), baseKey, length * 8));
|
|
275
243
|
} catch {
|
|
276
244
|
DERIVE_BITS_SUPPORTED = false;
|
|
277
|
-
return misc.pbkdf2(baseKey, algorithm.salt, DERIVED_BITS_ALGORITHM.iterations, length);
|
|
278
245
|
}
|
|
279
|
-
} else {
|
|
280
|
-
return misc.pbkdf2(baseKey, algorithm.salt, DERIVED_BITS_ALGORITHM.iterations, length);
|
|
281
246
|
}
|
|
247
|
+
return pbkdf2(password, salt, PBKDF2_ITERATIONS, length);
|
|
282
248
|
}
|
|
283
249
|
|
|
284
250
|
function encodePassword(password, rawPassword) {
|
|
@@ -301,10 +267,3 @@ function expand(inputArray, length) {
|
|
|
301
267
|
function subarray(array, begin, end) {
|
|
302
268
|
return array.subarray(begin, end);
|
|
303
269
|
}
|
|
304
|
-
|
|
305
|
-
function fromBits(codecBytes, chunk) {
|
|
306
|
-
return codecBytes.fromBits(chunk);
|
|
307
|
-
}
|
|
308
|
-
function toBits(codecBytes, chunk) {
|
|
309
|
-
return codecBytes.toBits(chunk);
|
|
310
|
-
}
|