From 6d89c0f157e5c8d9a54dc3db7287c82aae45d282 Mon Sep 17 00:00:00 2001 From: Lorenz Brun Date: Mon, 11 Nov 2024 20:54:36 +0100 Subject: [PATCH] fido2-utils: fix BufferSource conversions (#11784) The original implementation of bufferSourceToUint8Array was incorrect as it did not consider that TypedArray instances represent a view of the underlying ArrayBuffer which does not necessarily cover the entire backing ArrayBuffer. This resulted in the output of this function containing data which would not be logically contained in the input. This was partially fixed by #8787 for the common case of the input already being an Uint8Array, but it was still broken for any other TypedArrays. But #8222 introduced another copy of the original broken code, breaking the Uint8Array case again. Fix this once and hopefully for the last time with a correct implementation of bufferSourceToUint8Array and using that in the appropriate places instead of open-coding it. In addition there are now tests which exercise most edge cases with regards to ArrayBuffer and TypedArrays. --- .../services/fido2/fido2-utils.spec.ts | 30 +++++++++++++++++++ .../platform/services/fido2/fido2-utils.ts | 15 ++-------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/libs/common/src/platform/services/fido2/fido2-utils.spec.ts b/libs/common/src/platform/services/fido2/fido2-utils.spec.ts index a05eab5230..9bb4ed0a4c 100644 --- a/libs/common/src/platform/services/fido2/fido2-utils.spec.ts +++ b/libs/common/src/platform/services/fido2/fido2-utils.spec.ts @@ -4,6 +4,36 @@ describe("Fido2 Utils", () => { const asciiHelloWorldArray = [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; const b64HelloWorldString = "aGVsbG8gd29ybGQ="; + describe("bufferSourceToUint8Array(..)", () => { + it("should convert an ArrayBuffer", () => { + const buffer = new Uint8Array(asciiHelloWorldArray).buffer; + const out = Fido2Utils.bufferSourceToUint8Array(buffer); + expect(out).toEqual(new Uint8Array(asciiHelloWorldArray)); + }); + it("should convert an ArrayBuffer slice", () => { + const buffer = new Uint8Array(asciiHelloWorldArray).buffer.slice(8); + const out = Fido2Utils.bufferSourceToUint8Array(buffer); + expect(out).toEqual(new Uint8Array([114, 108, 100])); // 8th byte onwards + }); + it("should pass through an Uint8Array", () => { + const typedArray = new Uint8Array(asciiHelloWorldArray); + const out = Fido2Utils.bufferSourceToUint8Array(typedArray); + expect(out).toEqual(new Uint8Array(asciiHelloWorldArray)); + }); + it("should preserve the view of TypedArray", () => { + const buffer = new Uint8Array(asciiHelloWorldArray).buffer; + const input = new Uint8Array(buffer, 8, 1); + const out = Fido2Utils.bufferSourceToUint8Array(input); + expect(out).toEqual(new Uint8Array([114])); + }); + it("should convert different TypedArrays", () => { + const buffer = new Uint8Array(asciiHelloWorldArray).buffer; + const input = new Uint16Array(buffer, 8, 1); + const out = Fido2Utils.bufferSourceToUint8Array(input); + expect(out).toEqual(new Uint8Array([114, 108])); + }); + }); + describe("fromBufferToB64(...)", () => { it("should convert an ArrayBuffer to a b64 string", () => { const buffer = new Uint8Array(asciiHelloWorldArray).buffer; diff --git a/libs/common/src/platform/services/fido2/fido2-utils.ts b/libs/common/src/platform/services/fido2/fido2-utils.ts index c3c3eba246..5803491297 100644 --- a/libs/common/src/platform/services/fido2/fido2-utils.ts +++ b/libs/common/src/platform/services/fido2/fido2-utils.ts @@ -1,13 +1,6 @@ export class Fido2Utils { static bufferToString(bufferSource: BufferSource): string { - let buffer: Uint8Array; - if (bufferSource instanceof ArrayBuffer || bufferSource.buffer === undefined) { - buffer = new Uint8Array(bufferSource as ArrayBuffer); - } else { - buffer = new Uint8Array(bufferSource.buffer); - } - - return Fido2Utils.fromBufferToB64(buffer) + return Fido2Utils.fromBufferToB64(Fido2Utils.bufferSourceToUint8Array(bufferSource)) .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=/g, ""); @@ -18,12 +11,10 @@ export class Fido2Utils { } static bufferSourceToUint8Array(bufferSource: BufferSource): Uint8Array { - if (bufferSource instanceof Uint8Array) { - return bufferSource; - } else if (Fido2Utils.isArrayBuffer(bufferSource)) { + if (Fido2Utils.isArrayBuffer(bufferSource)) { return new Uint8Array(bufferSource); } else { - return new Uint8Array(bufferSource.buffer); + return new Uint8Array(bufferSource.buffer, bufferSource.byteOffset, bufferSource.byteLength); } }