From ed32b0d46ee570e405e0d88b43aecd8ef6fea129 Mon Sep 17 00:00:00 2001 From: codedust Date: Wed, 30 Dec 2020 23:53:30 +0100 Subject: [PATCH] fix: workaround for RangeError in browser runtime base64url Fixes RangeError in base64url.ts when encrypting large Uint8Arrays String.fromCharCode.apply causes a RangeError for large Uint8Arrays (> ~500kB). This happens, e.g., when encrypting larger files. See this gist to reproduce the bug (select a large file and see the browser console): https://gist.github.com/codedust/88c8af3b2acd782e72ffbe0c3c8bf5af Error message in Firefox: ``` Uncaught (in promise) RangeError: too many arguments provided for a function call (in base64url.js:8:62) encode http://localhost:8000/jose/runtime/base64url.js:8 encrypt http://localhost:8000/jose/jwe/flattened/encrypt.js:143 ``` Error message in Chromium: ``` Uncaught (in promise) RangeError: Maximum call stack size exceeded at encode (base64url.js:8) at FlattenedEncrypt.encrypt (encrypt.js:143) at async CompactEncrypt.encrypt (encrypt.js:23) at async jwe_test ((index):55) ``` Solution: Apply String.fromCharCode.apply in chunks of 32768 bytes, see https://stackoverflow.com/a/12713326 --- src/runtime/browser/base64url.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/runtime/browser/base64url.ts b/src/runtime/browser/base64url.ts index 8192a180..fe4263fc 100644 --- a/src/runtime/browser/base64url.ts +++ b/src/runtime/browser/base64url.ts @@ -6,7 +6,13 @@ export const encode = (input: Uint8Array | string) => { if (typeof unencoded === 'string') { unencoded = encoder.encode(unencoded) } - const base64string = globalThis.btoa(String.fromCharCode.apply(0, [...unencoded])) + const CHUNK_SIZE = 0x8000 + const arr = [] + for (let i = 0; i < unencoded.length; i += CHUNK_SIZE) { + // @ts-expect-error + arr.push(String.fromCharCode.apply(null, unencoded.subarray(i, i + CHUNK_SIZE))) + } + const base64string = globalThis.btoa(arr.join('')) return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_') }