@zip.js/zip.js 2.5.2 → 2.5.5
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/dist/z-worker-fflate.js +1 -1
- package/dist/z-worker-pako.js +1 -1
- package/dist/z-worker.js +1 -1
- package/dist/zip-fs-full.js +460 -482
- package/dist/zip-fs-full.min.js +1 -1
- package/dist/zip-fs.js +460 -482
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-full.js +460 -482
- package/dist/zip-full.min.js +1 -1
- package/dist/zip-no-worker-deflate.min.js +1 -1
- package/dist/zip-no-worker-inflate.min.js +1 -1
- package/dist/zip-no-worker.min.js +1 -1
- package/dist/zip.js +460 -482
- package/dist/zip.min.js +1 -1
- package/index-fflate.js +1 -1
- package/index.d.ts +1 -1
- package/index.js +3 -3
- package/lib/core/codec-interface.js +89 -0
- package/lib/core/{codecs/codec-pool-worker.js → codec-pool-worker.js} +55 -29
- package/lib/core/{codecs/codec-pool.js → codec-pool.js} +28 -9
- package/lib/core/configuration.js +10 -1
- package/lib/core/io.js +102 -100
- package/lib/core/{codecs → streams}/aes-crypto-stream.js +68 -54
- package/lib/core/{codecs → streams}/codec-stream.js +3 -6
- package/lib/core/{codecs → streams/codecs}/crc32.js +0 -0
- package/lib/core/{codecs → streams/codecs}/deflate.js +0 -0
- package/lib/core/{codecs → streams/codecs}/inflate.js +0 -0
- package/lib/core/{codecs → streams/codecs}/sjcl.js +0 -0
- package/lib/core/{codecs → streams}/crc32-stream.js +3 -4
- package/lib/core/{codecs → streams}/zip-crypto-stream.js +28 -32
- package/lib/core/streams/zip-entry-stream.js +178 -0
- package/lib/core/z-worker-core.js +30 -15
- package/lib/core/zip-fs-core.js +1 -1
- package/lib/core/zip-reader.js +21 -12
- package/lib/core/zip-writer.js +33 -22
- package/lib/z-worker-fflate.js +4 -1
- package/lib/z-worker-inline.js +1 -1
- package/lib/z-worker.js +2 -2
- package/lib/zip-fflate-shim.js +4 -1
- package/lib/zip-fs.js +6 -2
- package/lib/zip-full-fflate.js +6 -3
- package/lib/zip-full.js +8 -5
- package/lib/zip-no-worker-deflate.js +7 -3
- package/lib/zip-no-worker-fflate-deflate.js +6 -2
- package/lib/zip-no-worker-fflate-inflate.js +6 -3
- package/lib/zip-no-worker-fflate.js +6 -3
- package/lib/zip-no-worker-inflate.js +7 -4
- package/lib/zip-no-worker.js +8 -5
- package/lib/zip.js +6 -3
- package/package.json +2 -8
- package/tests/all/test-safe-closing.js +1 -1
- package/tests/node/package.json +2 -6
- package/tests/node/test-node.js +7 -11
- package/lib/core/codecs/codec.js +0 -230
- package/lib/core/engine.js +0 -89
|
@@ -30,7 +30,13 @@
|
|
|
30
30
|
// deno-lint-ignore-file no-this-alias
|
|
31
31
|
|
|
32
32
|
import encodeText from "./../util/encode-text.js";
|
|
33
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
cipher,
|
|
35
|
+
codec,
|
|
36
|
+
misc,
|
|
37
|
+
mode,
|
|
38
|
+
random
|
|
39
|
+
} from "./codecs/sjcl.js";
|
|
34
40
|
|
|
35
41
|
const ERR_INVALID_PASSWORD = "Invalid pasword";
|
|
36
42
|
const BLOCK_LENGTH = 16;
|
|
@@ -45,14 +51,19 @@ const SALT_LENGTH = [8, 12, 16];
|
|
|
45
51
|
const KEY_LENGTH = [16, 24, 32];
|
|
46
52
|
const SIGNATURE_LENGTH = 10;
|
|
47
53
|
const COUNTER_DEFAULT_VALUE = [0, 0, 0, 0];
|
|
48
|
-
const
|
|
49
|
-
const
|
|
54
|
+
const UNDEFINED_TYPE = "undefined";
|
|
55
|
+
const FUNCTION_TYPE = "function";
|
|
56
|
+
const CRYPTO_API_SUPPORTED = typeof crypto != UNDEFINED_TYPE;
|
|
57
|
+
const SUBTLE_API_SUPPORTED = CRYPTO_API_SUPPORTED && typeof crypto.subtle != UNDEFINED_TYPE;
|
|
58
|
+
const GET_RANDOM_VALUES_SUPPORTED = CRYPTO_API_SUPPORTED && typeof crypto.getRandomValues == FUNCTION_TYPE;
|
|
59
|
+
const IMPORT_KEY_SUPPORTED = CRYPTO_API_SUPPORTED && SUBTLE_API_SUPPORTED && typeof crypto.subtle.importKey == FUNCTION_TYPE;
|
|
60
|
+
const DERIVE_BITS_SUPPORTED = CRYPTO_API_SUPPORTED && SUBTLE_API_SUPPORTED && typeof crypto.subtle.deriveBits == FUNCTION_TYPE;
|
|
50
61
|
const codecBytes = codec.bytes;
|
|
51
62
|
const Aes = cipher.aes;
|
|
52
63
|
const CtrGladman = mode.ctrGladman;
|
|
53
64
|
const HmacSha1 = misc.hmacSha1;
|
|
54
65
|
|
|
55
|
-
class
|
|
66
|
+
class AESDecryptionStream extends TransformStream {
|
|
56
67
|
|
|
57
68
|
constructor(password, signed, strength) {
|
|
58
69
|
let stream;
|
|
@@ -67,23 +78,21 @@ class AESDecryptStream extends TransformStream {
|
|
|
67
78
|
});
|
|
68
79
|
},
|
|
69
80
|
async transform(chunk, controller) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
await aesCrypto.ready;
|
|
83
|
-
}
|
|
84
|
-
const output = new Uint8Array(chunk.length - SIGNATURE_LENGTH - ((chunk.length - SIGNATURE_LENGTH) % BLOCK_LENGTH));
|
|
85
|
-
controller.enqueue(append(aesCrypto, chunk, output, 0, SIGNATURE_LENGTH, true));
|
|
81
|
+
const aesCrypto = this;
|
|
82
|
+
if (aesCrypto.password) {
|
|
83
|
+
const password = aesCrypto.password;
|
|
84
|
+
aesCrypto.password = null;
|
|
85
|
+
const preamble = subarray(chunk, 0, SALT_LENGTH[aesCrypto.strength] + 2);
|
|
86
|
+
await createDecryptionKeys(aesCrypto, preamble, password);
|
|
87
|
+
aesCrypto.ctr = new CtrGladman(new Aes(aesCrypto.keys.key), Array.from(COUNTER_DEFAULT_VALUE));
|
|
88
|
+
aesCrypto.hmac = new HmacSha1(aesCrypto.keys.authentication);
|
|
89
|
+
chunk = subarray(chunk, SALT_LENGTH[aesCrypto.strength] + 2);
|
|
90
|
+
aesCrypto.resolveReady();
|
|
91
|
+
} else {
|
|
92
|
+
await aesCrypto.ready;
|
|
86
93
|
}
|
|
94
|
+
const output = new Uint8Array(chunk.length - SIGNATURE_LENGTH - ((chunk.length - SIGNATURE_LENGTH) % BLOCK_LENGTH));
|
|
95
|
+
controller.enqueue(append(aesCrypto, chunk, output, 0, SIGNATURE_LENGTH, true));
|
|
87
96
|
},
|
|
88
97
|
async flush(controller) {
|
|
89
98
|
const aesCrypto = this;
|
|
@@ -93,14 +102,14 @@ class AESDecryptStream extends TransformStream {
|
|
|
93
102
|
const originalSignature = subarray(pending, pending.length - SIGNATURE_LENGTH);
|
|
94
103
|
let decryptedChunkArray = new Uint8Array(0);
|
|
95
104
|
if (chunkToDecrypt.length) {
|
|
96
|
-
const encryptedChunk =
|
|
105
|
+
const encryptedChunk = toBits(codecBytes, chunkToDecrypt);
|
|
97
106
|
aesCrypto.hmac.update(encryptedChunk);
|
|
98
|
-
const decryptedChunk = aesCrypto.
|
|
99
|
-
decryptedChunkArray =
|
|
107
|
+
const decryptedChunk = aesCrypto.ctr.update(encryptedChunk);
|
|
108
|
+
decryptedChunkArray = fromBits(codecBytes, decryptedChunk);
|
|
100
109
|
}
|
|
101
110
|
stream.valid = true;
|
|
102
111
|
if (aesCrypto.signed) {
|
|
103
|
-
const signature = subarray(
|
|
112
|
+
const signature = subarray(fromBits(codecBytes, aesCrypto.hmac.digest()), 0, SIGNATURE_LENGTH);
|
|
104
113
|
for (let indexSignature = 0; indexSignature < SIGNATURE_LENGTH; indexSignature++) {
|
|
105
114
|
if (signature[indexSignature] != originalSignature[indexSignature]) {
|
|
106
115
|
stream.valid = false;
|
|
@@ -114,7 +123,7 @@ class AESDecryptStream extends TransformStream {
|
|
|
114
123
|
}
|
|
115
124
|
}
|
|
116
125
|
|
|
117
|
-
class
|
|
126
|
+
class AESEncryptionStream extends TransformStream {
|
|
118
127
|
|
|
119
128
|
constructor(password, strength) {
|
|
120
129
|
let stream;
|
|
@@ -128,34 +137,32 @@ class AESEncryptStream extends TransformStream {
|
|
|
128
137
|
});
|
|
129
138
|
},
|
|
130
139
|
async transform(chunk, controller) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
await aesCrypto.ready;
|
|
143
|
-
}
|
|
144
|
-
const output = new Uint8Array(preamble.length + chunk.length - (chunk.length % BLOCK_LENGTH));
|
|
145
|
-
output.set(preamble, 0);
|
|
146
|
-
controller.enqueue(append(aesCrypto, chunk, output, preamble.length, 0));
|
|
140
|
+
const aesCrypto = this;
|
|
141
|
+
let preamble = new Uint8Array(0);
|
|
142
|
+
if (aesCrypto.password) {
|
|
143
|
+
const password = aesCrypto.password;
|
|
144
|
+
aesCrypto.password = null;
|
|
145
|
+
preamble = await createEncryptionKeys(aesCrypto, password);
|
|
146
|
+
aesCrypto.ctr = new CtrGladman(new Aes(aesCrypto.keys.key), Array.from(COUNTER_DEFAULT_VALUE));
|
|
147
|
+
aesCrypto.hmac = new HmacSha1(aesCrypto.keys.authentication);
|
|
148
|
+
aesCrypto.resolveReady();
|
|
149
|
+
} else {
|
|
150
|
+
await aesCrypto.ready;
|
|
147
151
|
}
|
|
152
|
+
const output = new Uint8Array(preamble.length + chunk.length - (chunk.length % BLOCK_LENGTH));
|
|
153
|
+
output.set(preamble, 0);
|
|
154
|
+
controller.enqueue(append(aesCrypto, chunk, output, preamble.length, 0));
|
|
148
155
|
},
|
|
149
156
|
async flush(controller) {
|
|
150
157
|
const aesCrypto = this;
|
|
151
158
|
await aesCrypto.ready;
|
|
152
159
|
let encryptedChunkArray = new Uint8Array(0);
|
|
153
160
|
if (aesCrypto.pending.length) {
|
|
154
|
-
const encryptedChunk = aesCrypto.
|
|
161
|
+
const encryptedChunk = aesCrypto.ctr.update(toBits(codecBytes, aesCrypto.pending));
|
|
155
162
|
aesCrypto.hmac.update(encryptedChunk);
|
|
156
|
-
encryptedChunkArray =
|
|
163
|
+
encryptedChunkArray = fromBits(codecBytes, encryptedChunk);
|
|
157
164
|
}
|
|
158
|
-
stream.signature =
|
|
165
|
+
stream.signature = fromBits(codecBytes, aesCrypto.hmac.digest()).slice(0, SIGNATURE_LENGTH);
|
|
159
166
|
controller.enqueue(concat(encryptedChunkArray, stream.signature));
|
|
160
167
|
}
|
|
161
168
|
});
|
|
@@ -164,8 +171,8 @@ class AESEncryptStream extends TransformStream {
|
|
|
164
171
|
}
|
|
165
172
|
|
|
166
173
|
export {
|
|
167
|
-
|
|
168
|
-
|
|
174
|
+
AESDecryptionStream,
|
|
175
|
+
AESEncryptionStream,
|
|
169
176
|
ERR_INVALID_PASSWORD
|
|
170
177
|
};
|
|
171
178
|
|
|
@@ -177,15 +184,15 @@ function append(aesCrypto, input, output, paddingStart, paddingEnd, verifySignat
|
|
|
177
184
|
}
|
|
178
185
|
let offset;
|
|
179
186
|
for (offset = 0; offset <= inputLength - BLOCK_LENGTH; offset += BLOCK_LENGTH) {
|
|
180
|
-
const inputChunk =
|
|
187
|
+
const inputChunk = toBits(codecBytes, subarray(input, offset, offset + BLOCK_LENGTH));
|
|
181
188
|
if (verifySignature) {
|
|
182
189
|
aesCrypto.hmac.update(inputChunk);
|
|
183
190
|
}
|
|
184
|
-
const outputChunk = aesCrypto.
|
|
191
|
+
const outputChunk = aesCrypto.ctr.update(inputChunk);
|
|
185
192
|
if (!verifySignature) {
|
|
186
193
|
aesCrypto.hmac.update(outputChunk);
|
|
187
194
|
}
|
|
188
|
-
output.set(
|
|
195
|
+
output.set(fromBits(codecBytes, outputChunk), offset + paddingStart);
|
|
189
196
|
}
|
|
190
197
|
aesCrypto.pending = subarray(input, offset);
|
|
191
198
|
return output;
|
|
@@ -212,14 +219,14 @@ async function createKeys(target, password, salt) {
|
|
|
212
219
|
const derivedBits = await deriveBits(Object.assign({ salt }, DERIVED_BITS_ALGORITHM), basekey, 8 * ((KEY_LENGTH[target.strength] * 2) + 2));
|
|
213
220
|
const compositeKey = new Uint8Array(derivedBits);
|
|
214
221
|
target.keys = {
|
|
215
|
-
key:
|
|
216
|
-
authentication:
|
|
222
|
+
key: toBits(codecBytes, subarray(compositeKey, 0, KEY_LENGTH[target.strength])),
|
|
223
|
+
authentication: toBits(codecBytes, subarray(compositeKey, KEY_LENGTH[target.strength], KEY_LENGTH[target.strength] * 2)),
|
|
217
224
|
passwordVerification: subarray(compositeKey, KEY_LENGTH[target.strength] * 2)
|
|
218
225
|
};
|
|
219
226
|
}
|
|
220
227
|
|
|
221
228
|
function getRandomValues(array) {
|
|
222
|
-
if (
|
|
229
|
+
if (GET_RANDOM_VALUES_SUPPORTED) {
|
|
223
230
|
return crypto.getRandomValues(array);
|
|
224
231
|
} else {
|
|
225
232
|
return random.getRandomValues(array);
|
|
@@ -227,7 +234,7 @@ function getRandomValues(array) {
|
|
|
227
234
|
}
|
|
228
235
|
|
|
229
236
|
function importKey(format, password, algorithm, extractable, keyUsages) {
|
|
230
|
-
if (
|
|
237
|
+
if (IMPORT_KEY_SUPPORTED) {
|
|
231
238
|
return crypto.subtle.importKey(format, password, algorithm, extractable, keyUsages);
|
|
232
239
|
} else {
|
|
233
240
|
return misc.importKey(password);
|
|
@@ -235,7 +242,7 @@ function importKey(format, password, algorithm, extractable, keyUsages) {
|
|
|
235
242
|
}
|
|
236
243
|
|
|
237
244
|
async function deriveBits(algorithm, baseKey, length) {
|
|
238
|
-
if (
|
|
245
|
+
if (DERIVE_BITS_SUPPORTED) {
|
|
239
246
|
return await crypto.subtle.deriveBits(algorithm, baseKey, length);
|
|
240
247
|
} else {
|
|
241
248
|
return misc.pbkdf2(baseKey, algorithm.salt, DERIVED_BITS_ALGORITHM.iterations, length);
|
|
@@ -263,4 +270,11 @@ function expand(inputArray, length) {
|
|
|
263
270
|
|
|
264
271
|
function subarray(array, begin, end) {
|
|
265
272
|
return array.subarray(begin, end);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function fromBits(codecBytes, chunk) {
|
|
276
|
+
return codecBytes.fromBits(chunk);
|
|
277
|
+
}
|
|
278
|
+
function toBits(codecBytes, chunk) {
|
|
279
|
+
return codecBytes.toBits(chunk);
|
|
266
280
|
}
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
/* global TransformStream */
|
|
37
37
|
|
|
38
38
|
class CodecStream extends TransformStream {
|
|
39
|
+
|
|
39
40
|
constructor(Codec, options) {
|
|
40
41
|
super({
|
|
41
42
|
start() {
|
|
@@ -43,15 +44,11 @@ class CodecStream extends TransformStream {
|
|
|
43
44
|
},
|
|
44
45
|
transform(chunk, controller) {
|
|
45
46
|
chunk = this.codec.append(chunk);
|
|
46
|
-
|
|
47
|
-
controller.enqueue(chunk);
|
|
48
|
-
}
|
|
47
|
+
controller.enqueue(chunk);
|
|
49
48
|
},
|
|
50
49
|
flush(controller) {
|
|
51
50
|
const chunk = this.codec.flush();
|
|
52
|
-
|
|
53
|
-
controller.enqueue(chunk);
|
|
54
|
-
}
|
|
51
|
+
controller.enqueue(chunk);
|
|
55
52
|
}
|
|
56
53
|
});
|
|
57
54
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -28,18 +28,17 @@
|
|
|
28
28
|
|
|
29
29
|
/* global TransformStream */
|
|
30
30
|
|
|
31
|
-
import Crc32 from "./crc32.js";
|
|
31
|
+
import Crc32 from "./codecs/crc32.js";
|
|
32
32
|
|
|
33
33
|
class Crc32Stream extends TransformStream {
|
|
34
|
+
|
|
34
35
|
constructor() {
|
|
35
36
|
super({
|
|
36
37
|
start() {
|
|
37
38
|
this.crc32 = new Crc32();
|
|
38
39
|
},
|
|
39
40
|
transform(chunk) {
|
|
40
|
-
|
|
41
|
-
this.crc32.append(chunk);
|
|
42
|
-
}
|
|
41
|
+
this.crc32.append(chunk);
|
|
43
42
|
},
|
|
44
43
|
flush(controller) {
|
|
45
44
|
const value = new Uint8Array(4);
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
/* global crypto, TransformStream */
|
|
30
30
|
// deno-lint-ignore-file no-this-alias
|
|
31
31
|
|
|
32
|
-
import Crc32 from "./crc32.js";
|
|
32
|
+
import Crc32 from "./codecs/crc32.js";
|
|
33
33
|
import { ERR_INVALID_PASSWORD } from "./aes-crypto-stream.js";
|
|
34
34
|
|
|
35
35
|
const HEADER_LENGTH = 12;
|
|
36
36
|
|
|
37
|
-
class
|
|
37
|
+
class ZipCryptoDecryptionStream extends TransformStream {
|
|
38
38
|
|
|
39
39
|
constructor(password, passwordVerification) {
|
|
40
40
|
let stream;
|
|
@@ -47,18 +47,16 @@ class ZipCryptoDecryptStream extends TransformStream {
|
|
|
47
47
|
createKeys(this, password);
|
|
48
48
|
},
|
|
49
49
|
transform(chunk, controller) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
throw new Error(ERR_INVALID_PASSWORD);
|
|
57
|
-
}
|
|
58
|
-
chunk = chunk.subarray(HEADER_LENGTH);
|
|
50
|
+
const zipCrypto = this;
|
|
51
|
+
if (zipCrypto.password) {
|
|
52
|
+
const decryptedHeader = decrypt(zipCrypto, chunk.subarray(0, HEADER_LENGTH));
|
|
53
|
+
zipCrypto.password = null;
|
|
54
|
+
if (decryptedHeader[HEADER_LENGTH - 1] != zipCrypto.passwordVerification) {
|
|
55
|
+
throw new Error(ERR_INVALID_PASSWORD);
|
|
59
56
|
}
|
|
60
|
-
|
|
57
|
+
chunk = chunk.subarray(HEADER_LENGTH);
|
|
61
58
|
}
|
|
59
|
+
controller.enqueue(decrypt(zipCrypto, chunk));
|
|
62
60
|
},
|
|
63
61
|
flush() {
|
|
64
62
|
stream.valid = true;
|
|
@@ -68,7 +66,7 @@ class ZipCryptoDecryptStream extends TransformStream {
|
|
|
68
66
|
}
|
|
69
67
|
}
|
|
70
68
|
|
|
71
|
-
class
|
|
69
|
+
class ZipCryptoEncryptionStream extends TransformStream {
|
|
72
70
|
|
|
73
71
|
constructor(password, passwordVerification) {
|
|
74
72
|
super({
|
|
@@ -80,24 +78,22 @@ class ZipCryptoEncryptStream extends TransformStream {
|
|
|
80
78
|
createKeys(this, password);
|
|
81
79
|
},
|
|
82
80
|
transform(chunk, controller) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
offset = 0;
|
|
97
|
-
}
|
|
98
|
-
output.set(encrypt(zipCrypto, chunk), offset);
|
|
99
|
-
controller.enqueue(output);
|
|
81
|
+
const zipCrypto = this;
|
|
82
|
+
let output;
|
|
83
|
+
let offset;
|
|
84
|
+
if (zipCrypto.password) {
|
|
85
|
+
zipCrypto.password = null;
|
|
86
|
+
const header = crypto.getRandomValues(new Uint8Array(HEADER_LENGTH));
|
|
87
|
+
header[HEADER_LENGTH - 1] = zipCrypto.passwordVerification;
|
|
88
|
+
output = new Uint8Array(chunk.length + header.length);
|
|
89
|
+
output.set(encrypt(zipCrypto, header), 0);
|
|
90
|
+
offset = HEADER_LENGTH;
|
|
91
|
+
} else {
|
|
92
|
+
output = new Uint8Array(chunk.length);
|
|
93
|
+
offset = 0;
|
|
100
94
|
}
|
|
95
|
+
output.set(encrypt(zipCrypto, chunk), offset);
|
|
96
|
+
controller.enqueue(output);
|
|
101
97
|
},
|
|
102
98
|
flush() {
|
|
103
99
|
}
|
|
@@ -106,8 +102,8 @@ class ZipCryptoEncryptStream extends TransformStream {
|
|
|
106
102
|
}
|
|
107
103
|
|
|
108
104
|
export {
|
|
109
|
-
|
|
110
|
-
|
|
105
|
+
ZipCryptoDecryptionStream,
|
|
106
|
+
ZipCryptoEncryptionStream,
|
|
111
107
|
ERR_INVALID_PASSWORD
|
|
112
108
|
};
|
|
113
109
|
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
this list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
notice, this list of conditions and the following disclaimer in
|
|
12
|
+
the documentation and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
3. The names of the authors may not be used to endorse or promote products
|
|
15
|
+
derived from this software without specific prior written permission.
|
|
16
|
+
|
|
17
|
+
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
18
|
+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
20
|
+
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
21
|
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
22
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
23
|
+
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
24
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
25
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
26
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/* global CompressionStream, DecompressionStream, TransformStream */
|
|
30
|
+
|
|
31
|
+
import Crc32Stream from "./crc32-stream.js";
|
|
32
|
+
import {
|
|
33
|
+
AESEncryptionStream,
|
|
34
|
+
AESDecryptionStream
|
|
35
|
+
} from "./aes-crypto-stream.js";
|
|
36
|
+
import {
|
|
37
|
+
ZipCryptoEncryptionStream,
|
|
38
|
+
ZipCryptoDecryptionStream,
|
|
39
|
+
ERR_INVALID_PASSWORD
|
|
40
|
+
} from "./zip-crypto-stream.js";
|
|
41
|
+
import CodecStream from "./codec-stream.js";
|
|
42
|
+
|
|
43
|
+
const ERR_INVALID_SIGNATURE = "Invalid signature";
|
|
44
|
+
const COMPRESSION_FORMAT = "deflate-raw";
|
|
45
|
+
const UNDEFINED_TYPE = "undefined";
|
|
46
|
+
|
|
47
|
+
class DeflateStream extends TransformStream {
|
|
48
|
+
|
|
49
|
+
constructor(codecConstructor, { compressed, useCompressionStream, password, passwordVerification, encryptionStrength, zipCrypto, signed }, { chunkSize }, ...strategies) {
|
|
50
|
+
super({}, ...strategies);
|
|
51
|
+
const stream = this;
|
|
52
|
+
let crc32Stream, encryptionStream;
|
|
53
|
+
let readable = filterEmptyChunks(super.readable);
|
|
54
|
+
const encrypted = Boolean(password);
|
|
55
|
+
if ((!encrypted || zipCrypto) && signed) {
|
|
56
|
+
[readable, crc32Stream] = readable.tee();
|
|
57
|
+
crc32Stream = crc32Stream.pipeThrough(new Crc32Stream());
|
|
58
|
+
}
|
|
59
|
+
if (compressed) {
|
|
60
|
+
if ((useCompressionStream !== undefined && !useCompressionStream) || typeof CompressionStream == UNDEFINED_TYPE) {
|
|
61
|
+
readable = pipeCodecStream(codecConstructor, readable, chunkSize);
|
|
62
|
+
} else {
|
|
63
|
+
try {
|
|
64
|
+
const compressionStream = new CompressionStream(COMPRESSION_FORMAT);
|
|
65
|
+
readable = readable.pipeThrough(compressionStream);
|
|
66
|
+
} catch (_error) {
|
|
67
|
+
readable = pipeCodecStream(codecConstructor, readable, chunkSize);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (encrypted) {
|
|
72
|
+
if (zipCrypto) {
|
|
73
|
+
readable = readable.pipeThrough(new ZipCryptoEncryptionStream(password, passwordVerification));
|
|
74
|
+
} else {
|
|
75
|
+
encryptionStream = new AESEncryptionStream(password, encryptionStrength);
|
|
76
|
+
readable = readable.pipeThrough(encryptionStream);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
setReadable(stream, readable, async () => {
|
|
80
|
+
let signature;
|
|
81
|
+
if (encrypted && !zipCrypto) {
|
|
82
|
+
signature = encryptionStream.signature;
|
|
83
|
+
}
|
|
84
|
+
if ((!encrypted || zipCrypto) && signed) {
|
|
85
|
+
signature = await crc32Stream.getReader().read();
|
|
86
|
+
signature = new DataView(signature.value.buffer).getUint32(0);
|
|
87
|
+
}
|
|
88
|
+
stream.signature = signature;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
class InflateStream extends TransformStream {
|
|
94
|
+
|
|
95
|
+
constructor(codecConstructor, { zipCrypto, password, passwordVerification, signed, encryptionStrength, compressed, useCompressionStream }, { chunkSize }, ...strategies) {
|
|
96
|
+
super({}, ...strategies);
|
|
97
|
+
const stream = this;
|
|
98
|
+
let crc32Stream, decryptionStream;
|
|
99
|
+
let readable = filterEmptyChunks(super.readable);
|
|
100
|
+
const encrypted = Boolean(password);
|
|
101
|
+
if (encrypted) {
|
|
102
|
+
if (zipCrypto) {
|
|
103
|
+
readable = readable.pipeThrough(new ZipCryptoDecryptionStream(password, passwordVerification));
|
|
104
|
+
} else {
|
|
105
|
+
decryptionStream = new AESDecryptionStream(password, signed, encryptionStrength);
|
|
106
|
+
readable = readable.pipeThrough(decryptionStream);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (compressed) {
|
|
110
|
+
if ((useCompressionStream !== undefined && !useCompressionStream) || typeof DecompressionStream == UNDEFINED_TYPE) {
|
|
111
|
+
readable = pipeCodecStream(codecConstructor, readable, chunkSize);
|
|
112
|
+
} else {
|
|
113
|
+
try {
|
|
114
|
+
readable = readable.pipeThrough(new DecompressionStream(COMPRESSION_FORMAT));
|
|
115
|
+
} catch (_error) {
|
|
116
|
+
readable = pipeCodecStream(codecConstructor, readable, chunkSize);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if ((!encrypted || zipCrypto) && signed) {
|
|
121
|
+
[readable, crc32Stream] = readable.tee();
|
|
122
|
+
crc32Stream = crc32Stream.pipeThrough(new Crc32Stream());
|
|
123
|
+
}
|
|
124
|
+
setReadable(stream, readable, async () => {
|
|
125
|
+
let signature;
|
|
126
|
+
if (encrypted && !zipCrypto) {
|
|
127
|
+
if (!decryptionStream.valid) {
|
|
128
|
+
throw new Error(ERR_INVALID_SIGNATURE);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if ((!encrypted || zipCrypto) && signed) {
|
|
132
|
+
signature = await crc32Stream.getReader().read();
|
|
133
|
+
const dataViewSignature = new DataView(signature.value.buffer);
|
|
134
|
+
if (signature != dataViewSignature.getUint32(0, false)) {
|
|
135
|
+
throw new Error(ERR_INVALID_SIGNATURE);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export {
|
|
143
|
+
DeflateStream,
|
|
144
|
+
InflateStream,
|
|
145
|
+
ERR_INVALID_PASSWORD,
|
|
146
|
+
ERR_INVALID_SIGNATURE
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
function pipeCodecStream(codecConstructor, stream, chunkSize) {
|
|
150
|
+
return stream.pipeThrough(new CodecStream(codecConstructor, { chunkSize }));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function filterEmptyChunks(readable) {
|
|
154
|
+
return readable.pipeThrough(new TransformStream({
|
|
155
|
+
transform(chunk, controller) {
|
|
156
|
+
if (chunk && chunk.length) {
|
|
157
|
+
controller.enqueue(chunk);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function setReadable(stream, readable, flush) {
|
|
164
|
+
stream.length = 0;
|
|
165
|
+
readable = readable.pipeThrough(new TransformStream({
|
|
166
|
+
transform(chunk, controller) {
|
|
167
|
+
if (chunk && chunk.length) {
|
|
168
|
+
stream.length += chunk.length;
|
|
169
|
+
controller.enqueue(chunk);
|
|
170
|
+
}
|
|
171
|
+
}, flush
|
|
172
|
+
}));
|
|
173
|
+
Object.defineProperty(stream, "readable", {
|
|
174
|
+
get() {
|
|
175
|
+
return readable;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
/* global self, importScripts, addEventListener, postMessage */
|
|
29
|
+
/* global self, importScripts, addEventListener, postMessage, ReadableStream, WritableStream */
|
|
30
30
|
|
|
31
31
|
import {
|
|
32
32
|
createCodec,
|
|
@@ -37,7 +37,8 @@ import {
|
|
|
37
37
|
MESSAGE_PULL,
|
|
38
38
|
MESSAGE_DATA,
|
|
39
39
|
MESSAGE_ABORT,
|
|
40
|
-
|
|
40
|
+
MESSAGE_CLOSE
|
|
41
|
+
} from "./codec-interface.js";
|
|
41
42
|
|
|
42
43
|
const pendingMessages = new Map();
|
|
43
44
|
let codec;
|
|
@@ -78,16 +79,30 @@ async function init(message) {
|
|
|
78
79
|
} else if (options.codecType.startsWith(CODEC_INFLATE)) {
|
|
79
80
|
codecConstructor = self.Inflate;
|
|
80
81
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
const readable = new ReadableStream({
|
|
83
|
+
async pull(controller) {
|
|
84
|
+
let result = new Promise((resolve, reject) => pendingMessages.set(messageId, { resolve, reject }));
|
|
85
|
+
sendMessage({ type: MESSAGE_PULL, messageId });
|
|
86
|
+
messageId++;
|
|
87
|
+
const { value, done } = await result;
|
|
88
|
+
// debugger;
|
|
89
|
+
controller.enqueue(value);
|
|
90
|
+
if (done) {
|
|
91
|
+
controller.close();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}, {
|
|
95
|
+
highWaterMark: 1
|
|
96
|
+
});
|
|
97
|
+
const writable = new WritableStream({
|
|
98
|
+
write(data) {
|
|
99
|
+
sendMessage({ type: MESSAGE_DATA, data });
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
codec = createCodec(codecConstructor, readable, writable, options, message.config);
|
|
103
|
+
const result = await codec.run();
|
|
104
|
+
sendMessage({ type: MESSAGE_CLOSE, result });
|
|
105
|
+
|
|
91
106
|
} catch (error) {
|
|
92
107
|
postMessage({ error: { message: error.message, stack: error.stack } });
|
|
93
108
|
}
|
|
@@ -95,11 +110,11 @@ async function init(message) {
|
|
|
95
110
|
|
|
96
111
|
function sendMessage(message) {
|
|
97
112
|
if (message.data) {
|
|
98
|
-
const data = message.data
|
|
113
|
+
const data = message.data;
|
|
99
114
|
if (data && data.length) {
|
|
100
115
|
try {
|
|
101
|
-
message.data
|
|
102
|
-
postMessage(message, [message.data
|
|
116
|
+
message.data = data.buffer;
|
|
117
|
+
postMessage(message, [message.data]);
|
|
103
118
|
} catch (_error) {
|
|
104
119
|
postMessage(message);
|
|
105
120
|
}
|
package/lib/core/zip-fs-core.js
CHANGED
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
export { ERR_ABORT } from "./engine.js";
|
|
30
29
|
import {
|
|
31
30
|
Reader,
|
|
32
31
|
TextReader,
|
|
@@ -419,6 +418,7 @@ class FS {
|
|
|
419
418
|
}
|
|
420
419
|
|
|
421
420
|
const fs = { FS, ZipDirectoryEntry, ZipFileEntry };
|
|
421
|
+
export { ERR_ABORT } from "./codec-pool.js";
|
|
422
422
|
export { fs };
|
|
423
423
|
|
|
424
424
|
function getTotalSize(entries, propertyName) {
|