From 17df9f8d3140d7e904731434b814ebb381e51ad5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 4 Dec 2024 22:10:35 +0000 Subject: [PATCH] Avoid use of Buffer as it does not exist in the Web natively (#4569) * Avoid use of Buffer as it does not exist in the Web natively Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Use TC39 Uint8Array methods when available Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Replace usage of Buffer in encodeRecoveryKey Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- spec/integ/crypto/verification.spec.ts | 32 ++++---- spec/unit/base64.spec.ts | 26 +------ spec/unit/matrixrtc/MatrixRTCSession.spec.ts | 16 ++-- src/@types/global.d.ts | 20 +++++ src/base64.ts | 78 +++++++++---------- src/client.ts | 8 +- src/common-crypto/CryptoBackend.ts | 2 +- src/crypto-api/recovery-key.ts | 2 +- src/crypto-api/verification.ts | 6 +- src/crypto/index.ts | 2 +- .../request/VerificationRequest.ts | 9 ++- src/digest.ts | 6 +- src/rust-crypto/rust-crypto.ts | 4 +- src/rust-crypto/verification.ts | 10 +-- src/utils/encryptAESSecretStorageItem.ts | 4 +- 15 files changed, 112 insertions(+), 113 deletions(-) diff --git a/spec/integ/crypto/verification.spec.ts b/spec/integ/crypto/verification.spec.ts index a9f9ba522e7..7b207a432da 100644 --- a/spec/integ/crypto/verification.spec.ts +++ b/spec/integ/crypto/verification.spec.ts @@ -473,21 +473,23 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st expect(request.phase).toEqual(VerificationPhase.Ready); // we should now have QR data we can display - const qrCodeBuffer = (await request.generateQRCode())!; - expect(qrCodeBuffer).toBeTruthy(); + const rawQrCodeBuffer = (await request.generateQRCode())!; + expect(rawQrCodeBuffer).toBeTruthy(); + const qrCodeBuffer = new Uint8Array(rawQrCodeBuffer); + const textDecoder = new TextDecoder(); // https://spec.matrix.org/v1.7/client-server-api/#qr-code-format - expect(qrCodeBuffer.subarray(0, 6).toString("latin1")).toEqual("MATRIX"); - expect(qrCodeBuffer.readUint8(6)).toEqual(0x02); // version - expect(qrCodeBuffer.readUint8(7)).toEqual(0x02); // mode - const txnIdLen = qrCodeBuffer.readUint16BE(8); - expect(qrCodeBuffer.subarray(10, 10 + txnIdLen).toString("utf-8")).toEqual(transactionId); + expect(textDecoder.decode(qrCodeBuffer.slice(0, 6))).toEqual("MATRIX"); + expect(qrCodeBuffer[6]).toEqual(0x02); // version + expect(qrCodeBuffer[7]).toEqual(0x02); // mode + const txnIdLen = (qrCodeBuffer[8] << 8) + qrCodeBuffer[9]; + expect(textDecoder.decode(qrCodeBuffer.slice(10, 10 + txnIdLen))).toEqual(transactionId); // Alice's device's public key comes next, but we have nothing to do with it here. - // const aliceDevicePubKey = qrCodeBuffer.subarray(10 + txnIdLen, 32 + 10 + txnIdLen); - expect(qrCodeBuffer.subarray(42 + txnIdLen, 32 + 42 + txnIdLen)).toEqual( - Buffer.from(MASTER_CROSS_SIGNING_PUBLIC_KEY_BASE64, "base64"), + // const aliceDevicePubKey = qrCodeBuffer.slice(10 + txnIdLen, 32 + 10 + txnIdLen); + expect(encodeUnpaddedBase64(qrCodeBuffer.slice(42 + txnIdLen, 32 + 42 + txnIdLen))).toEqual( + MASTER_CROSS_SIGNING_PUBLIC_KEY_BASE64, ); - const sharedSecret = qrCodeBuffer.subarray(74 + txnIdLen); + const sharedSecret = qrCodeBuffer.slice(74 + txnIdLen); // we should still be "Ready" and have no verifier expect(request.phase).toEqual(VerificationPhase.Ready); @@ -805,7 +807,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st // we should now have QR data we can display const qrCodeBuffer = (await request.generateQRCode())!; expect(qrCodeBuffer).toBeTruthy(); - const sharedSecret = qrCodeBuffer.subarray(74 + transactionId.length); + const sharedSecret = qrCodeBuffer.slice(74 + transactionId.length); // the dummy device "scans" the displayed QR code and acknowledges it with a "m.key.verification.start" returnToDeviceMessageFromSync(buildReciprocateStartMessage(transactionId, sharedSecret)); @@ -1627,7 +1629,7 @@ function buildReadyMessage( } /** build an m.key.verification.start to-device message suitable for the m.reciprocate.v1 flow, originating from the dummy device */ -function buildReciprocateStartMessage(transactionId: string, sharedSecret: Uint8Array) { +function buildReciprocateStartMessage(transactionId: string, sharedSecret: ArrayBuffer) { return { type: "m.key.verification.start", content: { @@ -1723,7 +1725,7 @@ function buildQRCode( key2Base64: string, sharedSecret: string, mode = 0x02, -): Uint8Array { +): Uint8ClampedArray { // https://spec.matrix.org/v1.7/client-server-api/#qr-code-format const qrCodeBuffer = Buffer.alloc(150); // oversize @@ -1739,5 +1741,5 @@ function buildQRCode( idx += qrCodeBuffer.write(sharedSecret, idx); // truncate to the right length - return qrCodeBuffer.subarray(0, idx); + return new Uint8ClampedArray(qrCodeBuffer.subarray(0, idx)); } diff --git a/spec/unit/base64.spec.ts b/spec/unit/base64.spec.ts index cba73bec9f3..69a64399d27 100644 --- a/spec/unit/base64.spec.ts +++ b/spec/unit/base64.spec.ts @@ -15,34 +15,10 @@ limitations under the License. */ import { TextEncoder, TextDecoder } from "util"; -import NodeBuffer from "node:buffer"; import { decodeBase64, encodeBase64, encodeUnpaddedBase64, encodeUnpaddedBase64Url } from "../../src/base64"; -describe.each(["browser", "node"])("Base64 encoding (%s)", (env) => { - let origBuffer = Buffer; - - beforeAll(() => { - if (env === "browser") { - origBuffer = Buffer; - // @ts-ignore - // eslint-disable-next-line no-global-assign - Buffer = undefined; - - globalThis.atob = NodeBuffer.atob; - globalThis.btoa = NodeBuffer.btoa; - } - }); - - afterAll(() => { - // eslint-disable-next-line no-global-assign - Buffer = origBuffer; - // @ts-ignore - globalThis.atob = undefined; - // @ts-ignore - globalThis.btoa = undefined; - }); - +describe("Base64 encoding", () => { it("Should decode properly encoded data", () => { const decoded = new TextDecoder().decode(decodeBase64("ZW5jb2RpbmcgaGVsbG8gd29ybGQ=")); diff --git a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts index 26368e0b33f..248be4c19ec 100644 --- a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts +++ b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts @@ -38,6 +38,8 @@ const membershipTemplate: CallMembershipData = { const mockFocus = { type: "mock" }; +const textEncoder = new TextEncoder(); + describe("MatrixRTCSession", () => { let client: MatrixClient; let sess: MatrixRTCSession | undefined; @@ -1345,7 +1347,7 @@ describe("MatrixRTCSession", () => { sess!.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); expect(encryptionKeyChangedListener).toHaveBeenCalledWith( - Buffer.from("this is the key", "utf-8"), + textEncoder.encode("this is the key"), 0, "@bob:example.org:bobsphone", ); @@ -1377,7 +1379,7 @@ describe("MatrixRTCSession", () => { sess!.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); expect(encryptionKeyChangedListener).toHaveBeenCalledWith( - Buffer.from("this is the key", "utf-8"), + textEncoder.encode("this is the key"), 4, "@bob:example.org:bobsphone", ); @@ -1409,7 +1411,7 @@ describe("MatrixRTCSession", () => { sess!.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); expect(encryptionKeyChangedListener).toHaveBeenCalledWith( - Buffer.from("this is the key", "utf-8"), + textEncoder.encode("this is the key"), 0, "@bob:example.org:bobsphone", ); @@ -1436,12 +1438,12 @@ describe("MatrixRTCSession", () => { sess!.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(2); expect(encryptionKeyChangedListener).toHaveBeenCalledWith( - Buffer.from("this is the key", "utf-8"), + textEncoder.encode("this is the key"), 0, "@bob:example.org:bobsphone", ); expect(encryptionKeyChangedListener).toHaveBeenCalledWith( - Buffer.from("this is the key", "utf-8"), + textEncoder.encode("this is the key"), 4, "@bob:example.org:bobsphone", ); @@ -1489,7 +1491,7 @@ describe("MatrixRTCSession", () => { sess!.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); expect(encryptionKeyChangedListener).toHaveBeenCalledWith( - Buffer.from("newer key", "utf-8"), + textEncoder.encode("newer key"), 0, "@bob:example.org:bobsphone", ); @@ -1537,7 +1539,7 @@ describe("MatrixRTCSession", () => { sess!.reemitEncryptionKeys(); expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); expect(encryptionKeyChangedListener).toHaveBeenCalledWith( - Buffer.from("second key", "utf-8"), + textEncoder.encode("second key"), 0, "@bob:example.org:bobsphone", ); diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 5a280f0e2ce..c9103dbebf1 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -67,4 +67,24 @@ declare global { // on webkit: we should check if we still need to do this webkitGetUserMedia?: DummyInterfaceWeShouldntBeUsingThis; } + + export interface Uint8ArrayToBase64Options { + alphabet?: "base64" | "base64url"; + omitPadding?: boolean; + } + + interface Uint8Array { + // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tobase64 + toBase64?(options?: Uint8ArrayToBase64Options): string; + } + + export interface Uint8ArrayFromBase64Options { + alphabet?: "base64"; // Our fallback code only handles base64. + lastChunkHandling?: "loose"; // Our fallback code doesn't support other handling at this time. + } + + interface Uint8ArrayConstructor { + // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.frombase64 + fromBase64?(base64: string, options?: Uint8ArrayFromBase64Options): Uint8Array; + } } diff --git a/src/base64.ts b/src/base64.ts index 450468b0889..4cb876b876d 100644 --- a/src/base64.ts +++ b/src/base64.ts @@ -18,30 +18,32 @@ limitations under the License. * Base64 encoding and decoding utilities */ +function toBase64(uint8Array: Uint8Array, options: Uint8ArrayToBase64Options): string { + if (typeof uint8Array.toBase64 === "function") { + // Currently this is only supported in Firefox, + // but we match the options in the hope in the future we can rely on it for all environments. + // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tobase64 + return uint8Array.toBase64(options); + } + + let base64 = btoa(uint8Array.reduce((acc, current) => acc + String.fromCharCode(current), "")); + if (options.omitPadding) { + base64 = base64.replace(/={1,2}$/, ""); + } + if (options.alphabet === "base64url") { + base64 = base64.replace(/\+/g, "-").replace(/\//g, "_"); + } + + return base64; +} + /** * Encode a typed array of uint8 as base64. * @param uint8Array - The data to encode. * @returns The base64. */ -export function encodeBase64(uint8Array: ArrayBuffer | Uint8Array): string { - // A brief note on the state of base64 encoding in Javascript. - // As of 2023, there is still no common native impl between both browsers and - // node. Older Webpack provides an impl for Buffer and there is a polyfill class - // for it. There are also plenty of pure js impls, eg. base64-js which has 2336 - // dependents at current count. Using this would probably be fine although it's - // a little under-docced and run by an individual. The node impl works fine, - // the browser impl works but predates Uint8Array and so only uses strings. - // Right now, switching between native (or polyfilled) impls like this feels - // like the least bad option, but... *shrugs*. - if (typeof Buffer === "function") { - return Buffer.from(uint8Array).toString("base64"); - } else if (typeof btoa === "function" && uint8Array instanceof Uint8Array) { - // ArrayBuffer is a node concept so the param should always be a Uint8Array on - // the browser. We need to check because ArrayBuffers don't have reduce. - return btoa(uint8Array.reduce((acc, current) => acc + String.fromCharCode(current), "")); - } else { - throw new Error("No base64 impl found!"); - } +export function encodeBase64(uint8Array: Uint8Array): string { + return toBase64(uint8Array, { alphabet: "base64", omitPadding: false }); } /** @@ -49,8 +51,8 @@ export function encodeBase64(uint8Array: ArrayBuffer | Uint8Array): string { * @param uint8Array - The data to encode. * @returns The unpadded base64. */ -export function encodeUnpaddedBase64(uint8Array: ArrayBuffer | Uint8Array): string { - return encodeBase64(uint8Array).replace(/={1,2}$/, ""); +export function encodeUnpaddedBase64(uint8Array: Uint8Array): string { + return toBase64(uint8Array, { alphabet: "base64", omitPadding: true }); } /** @@ -58,8 +60,19 @@ export function encodeUnpaddedBase64(uint8Array: ArrayBuffer | Uint8Array): stri * @param uint8Array - The data to encode. * @returns The unpadded base64. */ -export function encodeUnpaddedBase64Url(uint8Array: ArrayBuffer | Uint8Array): string { - return encodeUnpaddedBase64(uint8Array).replace(/\+/g, "-").replace(/\//g, "_"); +export function encodeUnpaddedBase64Url(uint8Array: Uint8Array): string { + return toBase64(uint8Array, { alphabet: "base64url", omitPadding: true }); +} + +function fromBase64(base64: string, options: Uint8ArrayFromBase64Options): Uint8Array { + if (typeof Uint8Array.fromBase64 === "function") { + // Currently this is only supported in Firefox, + // but we match the options in the hope in the future we can rely on it for all environments. + // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.frombase64 + return Uint8Array.fromBase64(base64, options); + } + + return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)); } /** @@ -68,21 +81,6 @@ export function encodeUnpaddedBase64Url(uint8Array: ArrayBuffer | Uint8Array): s * @returns The decoded data. */ export function decodeBase64(base64: string): Uint8Array { - // See encodeBase64 for a short treatise on base64 en/decoding in JS - if (typeof Buffer === "function") { - return Buffer.from(base64, "base64"); - } else if (typeof atob === "function") { - const itFunc = function* (): Generator { - const decoded = atob( - // built-in atob doesn't support base64url: convert so we support either - base64.replace(/-/g, "+").replace(/_/g, "/"), - ); - for (let i = 0; i < decoded.length; ++i) { - yield decoded.charCodeAt(i); - } - }; - return Uint8Array.from(itFunc()); - } else { - throw new Error("No base64 impl found!"); - } + // The function requires us to select an alphabet, but we don't know if base64url was used so we convert. + return fromBase64(base64.replace(/-/g, "+").replace(/_/g, "/"), { alphabet: "base64", lastChunkHandling: "loose" }); } diff --git a/src/client.ts b/src/client.ts index dbe1363942c..a13eceebcc4 100644 --- a/src/client.ts +++ b/src/client.ts @@ -3899,28 +3899,28 @@ export class MatrixClient extends TypedEventEmitter, + privKey: Uint8Array, targetRoomId: undefined, targetSessionId: undefined, backupInfo: IKeyBackupInfo, opts?: IKeyBackupRestoreOpts, ): Promise; private async restoreKeyBackup( - privKey: ArrayLike, + privKey: Uint8Array, targetRoomId: string, targetSessionId: undefined, backupInfo: IKeyBackupInfo, opts?: IKeyBackupRestoreOpts, ): Promise; private async restoreKeyBackup( - privKey: ArrayLike, + privKey: Uint8Array, targetRoomId: string, targetSessionId: string, backupInfo: IKeyBackupInfo, opts?: IKeyBackupRestoreOpts, ): Promise; private async restoreKeyBackup( - privKey: ArrayLike, + privKey: Uint8Array, targetRoomId: string | undefined, targetSessionId: string | undefined, backupInfo: IKeyBackupInfo, diff --git a/src/common-crypto/CryptoBackend.ts b/src/common-crypto/CryptoBackend.ts index 76fb57aa624..85007d06783 100644 --- a/src/common-crypto/CryptoBackend.ts +++ b/src/common-crypto/CryptoBackend.ts @@ -108,7 +108,7 @@ export interface CryptoBackend extends SyncCryptoCallbacks, CryptoApi { * @param backupInfo - The backup information * @param privKey - The private decryption key. */ - getBackupDecryptor(backupInfo: KeyBackupInfo, privKey: ArrayLike): Promise; + getBackupDecryptor(backupInfo: KeyBackupInfo, privKey: Uint8Array): Promise; /** * Import a list of room keys restored from backup diff --git a/src/crypto-api/recovery-key.ts b/src/crypto-api/recovery-key.ts index 4b27b5a8ef8..2c4abdfdfb9 100644 --- a/src/crypto-api/recovery-key.ts +++ b/src/crypto-api/recovery-key.ts @@ -26,7 +26,7 @@ const KEY_SIZE = 32; * @param key */ export function encodeRecoveryKey(key: ArrayLike): string | undefined { - const buf = Buffer.alloc(OLM_RECOVERY_KEY_PREFIX.length + key.length + 1); + const buf = new Uint8Array(OLM_RECOVERY_KEY_PREFIX.length + key.length + 1); buf.set(OLM_RECOVERY_KEY_PREFIX, 0); buf.set(key, OLM_RECOVERY_KEY_PREFIX.length); diff --git a/src/crypto-api/verification.ts b/src/crypto-api/verification.ts index 468a5d7a9f2..a5bf5902130 100644 --- a/src/crypto-api/verification.ts +++ b/src/crypto-api/verification.ts @@ -155,7 +155,7 @@ export interface VerificationRequest * @param qrCodeData - the decoded QR code. * @returns A verifier; call `.verify()` on it to wait for the other side to complete the verification flow. */ - scanQRCode(qrCodeData: Uint8Array): Promise; + scanQRCode(qrCodeData: Uint8ClampedArray): Promise; /** * The verifier which is doing the actual verification, once the method has been established. @@ -170,7 +170,7 @@ export interface VerificationRequest * * @deprecated Not supported in Rust Crypto. Use {@link VerificationRequest#generateQRCode} instead. */ - getQRCodeBytes(): Buffer | undefined; + getQRCodeBytes(): Uint8ClampedArray | undefined; /** * Generate the data for a QR code allowing the other device to verify this one, if it supports it. @@ -178,7 +178,7 @@ export interface VerificationRequest * Only returns data once `phase` is {@link VerificationPhase.Ready} and the other party can scan a QR code; * otherwise returns `undefined`. */ - generateQRCode(): Promise; + generateQRCode(): Promise; /** * If this request has been cancelled, the cancellation code (e.g `m.user`) which is responsible for cancelling diff --git a/src/crypto/index.ts b/src/crypto/index.ts index acbb4e318db..557c1f5cad0 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -1846,7 +1846,7 @@ export class Crypto extends TypedEventEmitter): Promise { + public async getBackupDecryptor(backupInfo: KeyBackupInfo, privKey: Uint8Array): Promise { if (!(privKey instanceof Uint8Array)) { throw new Error(`getBackupDecryptor expects Uint8Array`); } diff --git a/src/crypto/verification/request/VerificationRequest.ts b/src/crypto/verification/request/VerificationRequest.ts index 8a3aca9a287..ac6111d2605 100644 --- a/src/crypto/verification/request/VerificationRequest.ts +++ b/src/crypto/verification/request/VerificationRequest.ts @@ -281,8 +281,9 @@ export class VerificationRequest { + public async generateQRCode(): Promise { return this.getQRCodeBytes(); } @@ -478,7 +479,7 @@ export class VerificationRequest { + public scanQRCode(qrCodeData: Uint8ClampedArray): Promise { throw new Error("QR code scanning not supported by legacy crypto"); } diff --git a/src/digest.ts b/src/digest.ts index 85b5be9643b..102349f6437 100644 --- a/src/digest.ts +++ b/src/digest.ts @@ -18,11 +18,11 @@ limitations under the License. * Computes a SHA-256 hash of a string (after utf-8 encoding) and returns it as an ArrayBuffer. * * @param plaintext The string to hash - * @returns An ArrayBuffer containing the SHA-256 hash of the input string + * @returns An Uint8Array containing the SHA-256 hash of the input string * @throws If the subtle crypto API is not available, for example if the code is running * in a web page with an insecure context (eg. served over plain HTTP). */ -export async function sha256(plaintext: string): Promise { +export async function sha256(plaintext: string): Promise { if (!globalThis.crypto.subtle) { throw new Error("Crypto.subtle is not available: insecure context?"); } @@ -30,5 +30,5 @@ export async function sha256(plaintext: string): Promise { const digest = await globalThis.crypto.subtle.digest("SHA-256", utf8); - return digest; + return new Uint8Array(digest); } diff --git a/src/rust-crypto/rust-crypto.ts b/src/rust-crypto/rust-crypto.ts index d18461e7b22..86853a9867a 100644 --- a/src/rust-crypto/rust-crypto.ts +++ b/src/rust-crypto/rust-crypto.ts @@ -329,7 +329,7 @@ export class RustCrypto extends TypedEventEmitter): Promise { + public async getBackupDecryptor(backupInfo: KeyBackupInfo, privKey: Uint8Array): Promise { if (!(privKey instanceof Uint8Array)) { throw new Error(`getBackupDecryptor: expects Uint8Array`); } @@ -1178,7 +1178,7 @@ export class RustCrypto extends TypedEventEmitter { const backupKeys: RustSdkCryptoJs.BackupKeys = await this.olmMachine.getBackupKeys(); if (!backupKeys.decryptionKey) return null; - return Buffer.from(backupKeys.decryptionKey.toBase64(), "base64"); + return decodeBase64(backupKeys.decryptionKey.toBase64()); } /** diff --git a/src/rust-crypto/verification.ts b/src/rust-crypto/verification.ts index 284bcc61e64..fe25ac54d9b 100644 --- a/src/rust-crypto/verification.ts +++ b/src/rust-crypto/verification.ts @@ -381,8 +381,8 @@ export class RustVerificationRequest * @param qrCodeData - the decoded QR code. * @returns A verifier; call `.verify()` on it to wait for the other side to complete the verification flow. */ - public async scanQRCode(uint8Array: Uint8Array): Promise { - const scan = RustSdkCryptoJs.QrCodeScan.fromBytes(new Uint8ClampedArray(uint8Array)); + public async scanQRCode(uint8Array: Uint8ClampedArray): Promise { + const scan = RustSdkCryptoJs.QrCodeScan.fromBytes(uint8Array); const verifier: RustSdkCryptoJs.Qr = await this.inner.scanQrCode(scan); // this should have triggered the onChange callback, and we should now have a verifier @@ -416,7 +416,7 @@ export class RustVerificationRequest /** * Stub implementation of {@link Crypto.VerificationRequest#getQRCodeBytes}. */ - public getQRCodeBytes(): Buffer | undefined { + public getQRCodeBytes(): Uint8ClampedArray | undefined { throw new Error("getQRCodeBytes() unsupported in Rust Crypto; use generateQRCode() instead."); } @@ -425,7 +425,7 @@ export class RustVerificationRequest * * Implementation of {@link Crypto.VerificationRequest#generateQRCode}. */ - public async generateQRCode(): Promise { + public async generateQRCode(): Promise { // make sure that we have a list of the other user's devices (workaround https://github.com/matrix-org/matrix-rust-sdk/issues/2896) if (!(await this.getOtherDevice())) { throw new Error("generateQRCode(): other device is unknown"); @@ -435,7 +435,7 @@ export class RustVerificationRequest // If we are unable to generate a QRCode, we return undefined if (!innerVerifier) return; - return Buffer.from(innerVerifier.toBytes()); + return innerVerifier.toBytes(); } /** diff --git a/src/utils/encryptAESSecretStorageItem.ts b/src/utils/encryptAESSecretStorageItem.ts index 064e914a125..3592ff48855 100644 --- a/src/utils/encryptAESSecretStorageItem.ts +++ b/src/utils/encryptAESSecretStorageItem.ts @@ -67,7 +67,7 @@ export default async function encryptAESSecretStorageItem( return { iv: encodeBase64(iv), - ciphertext: encodeBase64(ciphertext), - mac: encodeBase64(hmac), + ciphertext: encodeBase64(new Uint8Array(ciphertext)), + mac: encodeBase64(new Uint8Array(hmac)), }; }