File
Blob: src/workerd/api/tests/crypto-streams-test.js
| 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 |
| 4 | import { strictEqual, deepStrictEqual, rejects, throws } from 'node:assert'; |
| 5 | import { Buffer } from 'node:buffer'; |
| 6 | |
| 7 | export 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 | |
| 185 | export 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 | |
| 197 | export 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 | |
| 217 | export 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 | }; |