Skip to content
File

Blob: src/workerd/api/tests/crypto-streams-test.js

javascript233 lines
1// Copyright (c) 2023 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4import { strictEqual, deepStrictEqual, rejects, throws } from 'node:assert';
5import { Buffer } from 'node:buffer';
6 
7export const digeststream = {
8 async test() {
9 {
10 const check = new Uint8Array([
11 198, 247, 195, 114, 100, 29, 210, 94, 15, 221, 240, 33, 83, 117, 86, 31,
12 ]);
13 
14 const stream = new crypto.DigestStream('md5');
15 const writer = stream.getWriter();
16 const enc = new TextEncoder();
17 
18 writer.write(enc.encode('hello'));
19 writer.write(enc.encode('there'));
20 writer.close();
21 
22 const digest = new Uint8Array(await stream.digest);
23 
24 strictEqual(stream.bytesWritten, 10n);
25 deepStrictEqual(digest, check);
26 }
27 
28 {
29 const stream = new crypto.DigestStream('md5');
30 const writer = stream.getWriter();
31 const enc = new TextEncoder();
32 
33 writer.write(enc.encode('hello'));
34 writer.write(enc.encode('there'));
35 writer.abort(new Error('boom'));
36 
37 await rejects(stream.digest);
38 }
39 
40 {
41 // Creating for other known types works...
42 new crypto.DigestStream('SHA-256');
43 new crypto.DigestStream('SHA-384');
44 new crypto.DigestStream('SHA-512');
45 new crypto.DigestStream('crc32');
46 
47 // But fails for unknown digest names...
48 throws(() => new crypto.DigestStream('foo'));
49 }
50 
51 (async () => {
52 let digestPromise;
53 {
54 digestPromise = new crypto.DigestStream('md5').digest;
55 }
56 globalThis.gc();
57 await digestPromise;
58 throw new Error('The promise should not have resolved');
59 })();
60 
61 {
62 const enc = new TextEncoder();
63 const check = new Uint8Array([
64 93, 65, 64, 42, 188, 75, 42, 118, 185, 113, 157, 145, 16, 23, 197, 146,
65 ]);
66 const digestStream = new crypto.DigestStream('md5');
67 const writer = digestStream.getWriter();
68 await writer.write(enc.encode('hello'));
69 await writer.close();
70 const digest = new Uint8Array(await digestStream.digest);
71 deepStrictEqual(digest, check);
72 }
73 
74 {
75 const check = new Uint8Array([
76 93, 65, 64, 42, 188, 75, 42, 118, 185, 113, 157, 145, 16, 23, 197, 146,
77 ]);
78 const digestStream = new crypto.DigestStream('md5');
79 const writer = digestStream.getWriter();
80 await writer.write('hello');
81 await writer.close();
82 const digest = new Uint8Array(await digestStream.digest);
83 deepStrictEqual(digest, check);
84 }
85 
86 {
87 const check = new Uint8Array([
88 70, 54, 153, 61, 62, 29, 164, 233, 214, 184, 248, 123, 121, 232, 247,
89 198, 208, 24, 88, 13, 82, 102, 25, 80, 234, 188, 56, 69, 197, 137, 122,
90 77,
91 ]);
92 const digestStream = new crypto.DigestStream('SHA-256');
93 const writer = digestStream.getWriter();
94 await writer.write(new Uint32Array([1, 2, 3]));
95 await writer.close();
96 const digest = new Uint8Array(await digestStream.digest);
97 deepStrictEqual(digest, check);
98 }
99 
100 {
101 const check = new Uint8Array([
102 70, 54, 153, 61, 62, 29, 164, 233, 214, 184, 248, 123, 121, 232, 247,
103 198, 208, 24, 88, 13, 82, 102, 25, 80, 234, 188, 56, 69, 197, 137, 122,
104 77,
105 ]);
106 const digestStream = new crypto.DigestStream('SHA-256');
107 const writer = digestStream.getWriter();
108 // Ensures that byteOffset is correctly handled.
109 await writer.write(new Uint32Array([0, 1, 2, 3]).subarray(1));
110 await writer.close();
111 const digest = new Uint8Array(await digestStream.digest);
112 deepStrictEqual(digest, check);
113 }
114 
115 {
116 const check = new Uint8Array([176, 224, 34, 147]);
117 const digestStream = new crypto.DigestStream('crc32');
118 const writer = digestStream.getWriter();
119 await writer.write(new Uint32Array([1, 2, 3]));
120 await writer.close();
121 const digest = new Uint8Array(await digestStream.digest);
122 deepStrictEqual(digest, check);
123 }
124 
125 {
126 const digestStream = new crypto.DigestStream('md5');
127 const writer = digestStream.getWriter();
128 
129 try {
130 await writer.write(123);
131 throw new Error('should have failed');
132 } catch (err) {
133 strictEqual(
134 err.message,
135 'DigestStream is a byte stream but received an object ' +
136 'of non-ArrayBuffer/ArrayBufferView/string type on its writable side.'
137 );
138 }
139 }
140 
141 // AWS CRC tests, source:
142 // https://github.com/aws/aws-sdk-js-v3/blob/c3f3d0a1c652c88fef5859881c9a12cfc8df61c1/packages/middleware-flexible-checksums/src/middleware-flexible-checksums.integ.spec.ts#L21
143 const testCases = [
144 ['', 'crc32', 'AAAAAA=='],
145 ['abc', 'crc32', 'NSRBwg=='],
146 ['Hello world', 'crc32', 'i9aeUg=='],
147 
148 ['', 'crc32c', 'AAAAAA=='],
149 ['abc', 'crc32c', 'Nks/tw=='],
150 ['Hello world', 'crc32c', 'crUfeA=='],
151 
152 ['', 'crc64nvme', 'AAAAAAAAAAA='],
153 ['abc', 'crc64nvme', 'BeXKuz/B+us='],
154 ['Hello world', 'crc64nvme', 'OOJZ0D8xKts='],
155 
156 ['', 'SHA-1', '2jmj7l5rSw0yVb/vlWAYkK/YBwk='],
157 ['abc', 'SHA-1', 'qZk+NkcGgWq6PiVxeFDCbJzQ2J0='],
158 ['Hello world', 'SHA-1', 'e1AsOh9IyGCa4hLN+2Od7jlnP14='],
159 
160 ['', 'SHA-256', '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='],
161 ['abc', 'SHA-256', 'ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0='],
162 [
163 'Hello world',
164 'SHA-256',
165 'ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=',
166 ],
167 ];
168 {
169 for (const [body, algorithm, expected] of testCases) {
170 const digestStream = new crypto.DigestStream(algorithm);
171 const writer = digestStream.getWriter();
172 const enc = new TextEncoder();
173 writer.write(enc.encode(body));
174 writer.close();
175 const digest = await digestStream.digest;
176 deepStrictEqual(digest, Buffer.from(expected, 'base64').buffer);
177 }
178 }
179 
180 // Creating and not using a digest stream doesn't crash
181 new crypto.DigestStream('SHA-1');
182 },
183};
184 
185export const digestStreamNoEnd = {
186 async test() {
187 const stream = new crypto.DigestStream('md5');
188 const writer = stream.getWriter();
189 const enc = new TextEncoder();
190 
191 writer.write(enc.encode('hello'));
192 writer.write(enc.encode('there'));
193 // stream never ends, should not crash.
194 },
195};
196 
197export const digestStreamDisposable = {
198 async test() {
199 const enc = new TextEncoder();
200 const stream = new crypto.DigestStream('md5');
201 stream[Symbol.dispose]();
202 
203 const writer = stream.getWriter();
204 
205 try {
206 await writer.write(enc.encode('hello'));
207 throw new Error('should have failed');
208 } catch (err) {
209 strictEqual(err.message, 'The DigestStream was disposed.');
210 }
211 
212 // Calling dispose again should have no impact
213 stream[Symbol.dispose]();
214 },
215};
216 
217export const digestStreamLargeChunks = {
218 async test() {
219 {
220 const check = new Uint8Array([0x13, 0xb3, 0xf0, 0x58]);
221 const digestStream = new crypto.DigestStream('crc32');
222 const writer = digestStream.getWriter();
223 await writer.write(Buffer.alloc(1024, 'a'));
224 await writer.write(Buffer.alloc(1024, 'b'));
225 await writer.write(Buffer.alloc(1024, 'c'));
226 await writer.write(Buffer.alloc(1024 * 1024, 'd'));
227 await writer.close();
228 const digest = new Uint8Array(await digestStream.digest);
229 deepStrictEqual(digest, check);
230 }
231 },
232};