You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-07 23:02:56 +03:00
Avoid use of Buffer as it does not exist in the Web natively (#4569)
This commit is contained in:
committed by
GitHub
parent
1cad6f4451
commit
beb3721e7a
@@ -473,21 +473,23 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st
|
|||||||
expect(request.phase).toEqual(VerificationPhase.Ready);
|
expect(request.phase).toEqual(VerificationPhase.Ready);
|
||||||
|
|
||||||
// we should now have QR data we can display
|
// we should now have QR data we can display
|
||||||
const qrCodeBuffer = (await request.generateQRCode())!;
|
const rawQrCodeBuffer = (await request.generateQRCode())!;
|
||||||
expect(qrCodeBuffer).toBeTruthy();
|
expect(rawQrCodeBuffer).toBeTruthy();
|
||||||
|
const qrCodeBuffer = new Uint8Array(rawQrCodeBuffer);
|
||||||
|
|
||||||
|
const textDecoder = new TextDecoder();
|
||||||
// https://spec.matrix.org/v1.7/client-server-api/#qr-code-format
|
// https://spec.matrix.org/v1.7/client-server-api/#qr-code-format
|
||||||
expect(qrCodeBuffer.subarray(0, 6).toString("latin1")).toEqual("MATRIX");
|
expect(textDecoder.decode(qrCodeBuffer.slice(0, 6))).toEqual("MATRIX");
|
||||||
expect(qrCodeBuffer.readUint8(6)).toEqual(0x02); // version
|
expect(qrCodeBuffer[6]).toEqual(0x02); // version
|
||||||
expect(qrCodeBuffer.readUint8(7)).toEqual(0x02); // mode
|
expect(qrCodeBuffer[7]).toEqual(0x02); // mode
|
||||||
const txnIdLen = qrCodeBuffer.readUint16BE(8);
|
const txnIdLen = (qrCodeBuffer[8] << 8) + qrCodeBuffer[9];
|
||||||
expect(qrCodeBuffer.subarray(10, 10 + txnIdLen).toString("utf-8")).toEqual(transactionId);
|
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.
|
// 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);
|
// const aliceDevicePubKey = qrCodeBuffer.slice(10 + txnIdLen, 32 + 10 + txnIdLen);
|
||||||
expect(qrCodeBuffer.subarray(42 + txnIdLen, 32 + 42 + txnIdLen)).toEqual(
|
expect(encodeUnpaddedBase64(qrCodeBuffer.slice(42 + txnIdLen, 32 + 42 + txnIdLen))).toEqual(
|
||||||
Buffer.from(MASTER_CROSS_SIGNING_PUBLIC_KEY_BASE64, "base64"),
|
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
|
// we should still be "Ready" and have no verifier
|
||||||
expect(request.phase).toEqual(VerificationPhase.Ready);
|
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
|
// we should now have QR data we can display
|
||||||
const qrCodeBuffer = (await request.generateQRCode())!;
|
const qrCodeBuffer = (await request.generateQRCode())!;
|
||||||
expect(qrCodeBuffer).toBeTruthy();
|
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"
|
// the dummy device "scans" the displayed QR code and acknowledges it with a "m.key.verification.start"
|
||||||
returnToDeviceMessageFromSync(buildReciprocateStartMessage(transactionId, sharedSecret));
|
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 */
|
/** 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 {
|
return {
|
||||||
type: "m.key.verification.start",
|
type: "m.key.verification.start",
|
||||||
content: {
|
content: {
|
||||||
@@ -1723,7 +1725,7 @@ function buildQRCode(
|
|||||||
key2Base64: string,
|
key2Base64: string,
|
||||||
sharedSecret: string,
|
sharedSecret: string,
|
||||||
mode = 0x02,
|
mode = 0x02,
|
||||||
): Uint8Array {
|
): Uint8ClampedArray {
|
||||||
// https://spec.matrix.org/v1.7/client-server-api/#qr-code-format
|
// https://spec.matrix.org/v1.7/client-server-api/#qr-code-format
|
||||||
|
|
||||||
const qrCodeBuffer = Buffer.alloc(150); // oversize
|
const qrCodeBuffer = Buffer.alloc(150); // oversize
|
||||||
@@ -1739,5 +1741,5 @@ function buildQRCode(
|
|||||||
idx += qrCodeBuffer.write(sharedSecret, idx);
|
idx += qrCodeBuffer.write(sharedSecret, idx);
|
||||||
|
|
||||||
// truncate to the right length
|
// truncate to the right length
|
||||||
return qrCodeBuffer.subarray(0, idx);
|
return new Uint8ClampedArray(qrCodeBuffer.subarray(0, idx));
|
||||||
}
|
}
|
||||||
|
@@ -15,34 +15,10 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { TextEncoder, TextDecoder } from "util";
|
import { TextEncoder, TextDecoder } from "util";
|
||||||
import NodeBuffer from "node:buffer";
|
|
||||||
|
|
||||||
import { decodeBase64, encodeBase64, encodeUnpaddedBase64, encodeUnpaddedBase64Url } from "../../src/base64";
|
import { decodeBase64, encodeBase64, encodeUnpaddedBase64, encodeUnpaddedBase64Url } from "../../src/base64";
|
||||||
|
|
||||||
describe.each(["browser", "node"])("Base64 encoding (%s)", (env) => {
|
describe("Base64 encoding", () => {
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Should decode properly encoded data", () => {
|
it("Should decode properly encoded data", () => {
|
||||||
const decoded = new TextDecoder().decode(decodeBase64("ZW5jb2RpbmcgaGVsbG8gd29ybGQ="));
|
const decoded = new TextDecoder().decode(decodeBase64("ZW5jb2RpbmcgaGVsbG8gd29ybGQ="));
|
||||||
|
|
||||||
|
@@ -38,6 +38,8 @@ const membershipTemplate: CallMembershipData = {
|
|||||||
|
|
||||||
const mockFocus = { type: "mock" };
|
const mockFocus = { type: "mock" };
|
||||||
|
|
||||||
|
const textEncoder = new TextEncoder();
|
||||||
|
|
||||||
describe("MatrixRTCSession", () => {
|
describe("MatrixRTCSession", () => {
|
||||||
let client: MatrixClient;
|
let client: MatrixClient;
|
||||||
let sess: MatrixRTCSession | undefined;
|
let sess: MatrixRTCSession | undefined;
|
||||||
@@ -1345,7 +1347,7 @@ describe("MatrixRTCSession", () => {
|
|||||||
sess!.reemitEncryptionKeys();
|
sess!.reemitEncryptionKeys();
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1);
|
expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1);
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
||||||
Buffer.from("this is the key", "utf-8"),
|
textEncoder.encode("this is the key"),
|
||||||
0,
|
0,
|
||||||
"@bob:example.org:bobsphone",
|
"@bob:example.org:bobsphone",
|
||||||
);
|
);
|
||||||
@@ -1377,7 +1379,7 @@ describe("MatrixRTCSession", () => {
|
|||||||
sess!.reemitEncryptionKeys();
|
sess!.reemitEncryptionKeys();
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1);
|
expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1);
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
||||||
Buffer.from("this is the key", "utf-8"),
|
textEncoder.encode("this is the key"),
|
||||||
4,
|
4,
|
||||||
"@bob:example.org:bobsphone",
|
"@bob:example.org:bobsphone",
|
||||||
);
|
);
|
||||||
@@ -1409,7 +1411,7 @@ describe("MatrixRTCSession", () => {
|
|||||||
sess!.reemitEncryptionKeys();
|
sess!.reemitEncryptionKeys();
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1);
|
expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1);
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
||||||
Buffer.from("this is the key", "utf-8"),
|
textEncoder.encode("this is the key"),
|
||||||
0,
|
0,
|
||||||
"@bob:example.org:bobsphone",
|
"@bob:example.org:bobsphone",
|
||||||
);
|
);
|
||||||
@@ -1436,12 +1438,12 @@ describe("MatrixRTCSession", () => {
|
|||||||
sess!.reemitEncryptionKeys();
|
sess!.reemitEncryptionKeys();
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(2);
|
expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(2);
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
||||||
Buffer.from("this is the key", "utf-8"),
|
textEncoder.encode("this is the key"),
|
||||||
0,
|
0,
|
||||||
"@bob:example.org:bobsphone",
|
"@bob:example.org:bobsphone",
|
||||||
);
|
);
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
||||||
Buffer.from("this is the key", "utf-8"),
|
textEncoder.encode("this is the key"),
|
||||||
4,
|
4,
|
||||||
"@bob:example.org:bobsphone",
|
"@bob:example.org:bobsphone",
|
||||||
);
|
);
|
||||||
@@ -1489,7 +1491,7 @@ describe("MatrixRTCSession", () => {
|
|||||||
sess!.reemitEncryptionKeys();
|
sess!.reemitEncryptionKeys();
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1);
|
expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1);
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
||||||
Buffer.from("newer key", "utf-8"),
|
textEncoder.encode("newer key"),
|
||||||
0,
|
0,
|
||||||
"@bob:example.org:bobsphone",
|
"@bob:example.org:bobsphone",
|
||||||
);
|
);
|
||||||
@@ -1537,7 +1539,7 @@ describe("MatrixRTCSession", () => {
|
|||||||
sess!.reemitEncryptionKeys();
|
sess!.reemitEncryptionKeys();
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1);
|
expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1);
|
||||||
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
expect(encryptionKeyChangedListener).toHaveBeenCalledWith(
|
||||||
Buffer.from("second key", "utf-8"),
|
textEncoder.encode("second key"),
|
||||||
0,
|
0,
|
||||||
"@bob:example.org:bobsphone",
|
"@bob:example.org:bobsphone",
|
||||||
);
|
);
|
||||||
|
20
src/@types/global.d.ts
vendored
20
src/@types/global.d.ts
vendored
@@ -67,4 +67,24 @@ declare global {
|
|||||||
// on webkit: we should check if we still need to do this
|
// on webkit: we should check if we still need to do this
|
||||||
webkitGetUserMedia?: DummyInterfaceWeShouldntBeUsingThis;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,30 +18,32 @@ limitations under the License.
|
|||||||
* Base64 encoding and decoding utilities
|
* 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.
|
* Encode a typed array of uint8 as base64.
|
||||||
* @param uint8Array - The data to encode.
|
* @param uint8Array - The data to encode.
|
||||||
* @returns The base64.
|
* @returns The base64.
|
||||||
*/
|
*/
|
||||||
export function encodeBase64(uint8Array: ArrayBuffer | Uint8Array): string {
|
export function encodeBase64(uint8Array: Uint8Array): string {
|
||||||
// A brief note on the state of base64 encoding in Javascript.
|
return toBase64(uint8Array, { alphabet: "base64", omitPadding: false });
|
||||||
// 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!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,8 +51,8 @@ export function encodeBase64(uint8Array: ArrayBuffer | Uint8Array): string {
|
|||||||
* @param uint8Array - The data to encode.
|
* @param uint8Array - The data to encode.
|
||||||
* @returns The unpadded base64.
|
* @returns The unpadded base64.
|
||||||
*/
|
*/
|
||||||
export function encodeUnpaddedBase64(uint8Array: ArrayBuffer | Uint8Array): string {
|
export function encodeUnpaddedBase64(uint8Array: Uint8Array): string {
|
||||||
return encodeBase64(uint8Array).replace(/={1,2}$/, "");
|
return toBase64(uint8Array, { alphabet: "base64", omitPadding: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,8 +60,19 @@ export function encodeUnpaddedBase64(uint8Array: ArrayBuffer | Uint8Array): stri
|
|||||||
* @param uint8Array - The data to encode.
|
* @param uint8Array - The data to encode.
|
||||||
* @returns The unpadded base64.
|
* @returns The unpadded base64.
|
||||||
*/
|
*/
|
||||||
export function encodeUnpaddedBase64Url(uint8Array: ArrayBuffer | Uint8Array): string {
|
export function encodeUnpaddedBase64Url(uint8Array: Uint8Array): string {
|
||||||
return encodeUnpaddedBase64(uint8Array).replace(/\+/g, "-").replace(/\//g, "_");
|
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.
|
* @returns The decoded data.
|
||||||
*/
|
*/
|
||||||
export function decodeBase64(base64: string): Uint8Array {
|
export function decodeBase64(base64: string): Uint8Array {
|
||||||
// See encodeBase64 for a short treatise on base64 en/decoding in JS
|
// The function requires us to select an alphabet, but we don't know if base64url was used so we convert.
|
||||||
if (typeof Buffer === "function") {
|
return fromBase64(base64.replace(/-/g, "+").replace(/_/g, "/"), { alphabet: "base64", lastChunkHandling: "loose" });
|
||||||
return Buffer.from(base64, "base64");
|
|
||||||
} else if (typeof atob === "function") {
|
|
||||||
const itFunc = function* (): Generator<number> {
|
|
||||||
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!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -3899,28 +3899,28 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async restoreKeyBackup(
|
private async restoreKeyBackup(
|
||||||
privKey: ArrayLike<number>,
|
privKey: Uint8Array,
|
||||||
targetRoomId: undefined,
|
targetRoomId: undefined,
|
||||||
targetSessionId: undefined,
|
targetSessionId: undefined,
|
||||||
backupInfo: IKeyBackupInfo,
|
backupInfo: IKeyBackupInfo,
|
||||||
opts?: IKeyBackupRestoreOpts,
|
opts?: IKeyBackupRestoreOpts,
|
||||||
): Promise<IKeyBackupRestoreResult>;
|
): Promise<IKeyBackupRestoreResult>;
|
||||||
private async restoreKeyBackup(
|
private async restoreKeyBackup(
|
||||||
privKey: ArrayLike<number>,
|
privKey: Uint8Array,
|
||||||
targetRoomId: string,
|
targetRoomId: string,
|
||||||
targetSessionId: undefined,
|
targetSessionId: undefined,
|
||||||
backupInfo: IKeyBackupInfo,
|
backupInfo: IKeyBackupInfo,
|
||||||
opts?: IKeyBackupRestoreOpts,
|
opts?: IKeyBackupRestoreOpts,
|
||||||
): Promise<IKeyBackupRestoreResult>;
|
): Promise<IKeyBackupRestoreResult>;
|
||||||
private async restoreKeyBackup(
|
private async restoreKeyBackup(
|
||||||
privKey: ArrayLike<number>,
|
privKey: Uint8Array,
|
||||||
targetRoomId: string,
|
targetRoomId: string,
|
||||||
targetSessionId: string,
|
targetSessionId: string,
|
||||||
backupInfo: IKeyBackupInfo,
|
backupInfo: IKeyBackupInfo,
|
||||||
opts?: IKeyBackupRestoreOpts,
|
opts?: IKeyBackupRestoreOpts,
|
||||||
): Promise<IKeyBackupRestoreResult>;
|
): Promise<IKeyBackupRestoreResult>;
|
||||||
private async restoreKeyBackup(
|
private async restoreKeyBackup(
|
||||||
privKey: ArrayLike<number>,
|
privKey: Uint8Array,
|
||||||
targetRoomId: string | undefined,
|
targetRoomId: string | undefined,
|
||||||
targetSessionId: string | undefined,
|
targetSessionId: string | undefined,
|
||||||
backupInfo: IKeyBackupInfo,
|
backupInfo: IKeyBackupInfo,
|
||||||
|
@@ -108,7 +108,7 @@ export interface CryptoBackend extends SyncCryptoCallbacks, CryptoApi {
|
|||||||
* @param backupInfo - The backup information
|
* @param backupInfo - The backup information
|
||||||
* @param privKey - The private decryption key.
|
* @param privKey - The private decryption key.
|
||||||
*/
|
*/
|
||||||
getBackupDecryptor(backupInfo: KeyBackupInfo, privKey: ArrayLike<number>): Promise<BackupDecryptor>;
|
getBackupDecryptor(backupInfo: KeyBackupInfo, privKey: Uint8Array): Promise<BackupDecryptor>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import a list of room keys restored from backup
|
* Import a list of room keys restored from backup
|
||||||
|
@@ -26,7 +26,7 @@ const KEY_SIZE = 32;
|
|||||||
* @param key
|
* @param key
|
||||||
*/
|
*/
|
||||||
export function encodeRecoveryKey(key: ArrayLike<number>): string | undefined {
|
export function encodeRecoveryKey(key: ArrayLike<number>): 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(OLM_RECOVERY_KEY_PREFIX, 0);
|
||||||
buf.set(key, OLM_RECOVERY_KEY_PREFIX.length);
|
buf.set(key, OLM_RECOVERY_KEY_PREFIX.length);
|
||||||
|
|
||||||
|
@@ -155,7 +155,7 @@ export interface VerificationRequest
|
|||||||
* @param qrCodeData - the decoded QR code.
|
* @param qrCodeData - the decoded QR code.
|
||||||
* @returns A verifier; call `.verify()` on it to wait for the other side to complete the verification flow.
|
* @returns A verifier; call `.verify()` on it to wait for the other side to complete the verification flow.
|
||||||
*/
|
*/
|
||||||
scanQRCode(qrCodeData: Uint8Array): Promise<Verifier>;
|
scanQRCode(qrCodeData: Uint8ClampedArray): Promise<Verifier>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The verifier which is doing the actual verification, once the method has been established.
|
* 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.
|
* @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.
|
* 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;
|
* Only returns data once `phase` is {@link VerificationPhase.Ready} and the other party can scan a QR code;
|
||||||
* otherwise returns `undefined`.
|
* otherwise returns `undefined`.
|
||||||
*/
|
*/
|
||||||
generateQRCode(): Promise<Buffer | undefined>;
|
generateQRCode(): Promise<Uint8ClampedArray | undefined>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this request has been cancelled, the cancellation code (e.g `m.user`) which is responsible for cancelling
|
* If this request has been cancelled, the cancellation code (e.g `m.user`) which is responsible for cancelling
|
||||||
|
@@ -1846,7 +1846,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
/**
|
/**
|
||||||
* Implementation of {@link CryptoBackend#getBackupDecryptor}.
|
* Implementation of {@link CryptoBackend#getBackupDecryptor}.
|
||||||
*/
|
*/
|
||||||
public async getBackupDecryptor(backupInfo: KeyBackupInfo, privKey: ArrayLike<number>): Promise<BackupDecryptor> {
|
public async getBackupDecryptor(backupInfo: KeyBackupInfo, privKey: Uint8Array): Promise<BackupDecryptor> {
|
||||||
if (!(privKey instanceof Uint8Array)) {
|
if (!(privKey instanceof Uint8Array)) {
|
||||||
throw new Error(`getBackupDecryptor expects Uint8Array`);
|
throw new Error(`getBackupDecryptor expects Uint8Array`);
|
||||||
}
|
}
|
||||||
|
@@ -281,8 +281,9 @@ export class VerificationRequest<C extends IVerificationChannel = IVerificationC
|
|||||||
*
|
*
|
||||||
* @deprecated Prefer `generateQRCode`.
|
* @deprecated Prefer `generateQRCode`.
|
||||||
*/
|
*/
|
||||||
public getQRCodeBytes(): Buffer | undefined {
|
public getQRCodeBytes(): Uint8ClampedArray | undefined {
|
||||||
return this._qrCodeData?.getBuffer();
|
if (!this._qrCodeData) return;
|
||||||
|
return new Uint8ClampedArray(this._qrCodeData.getBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -291,7 +292,7 @@ export class VerificationRequest<C extends IVerificationChannel = IVerificationC
|
|||||||
* Only returns data once `phase` is `Ready` and the other party can scan a QR code;
|
* Only returns data once `phase` is `Ready` and the other party can scan a QR code;
|
||||||
* otherwise returns `undefined`.
|
* otherwise returns `undefined`.
|
||||||
*/
|
*/
|
||||||
public async generateQRCode(): Promise<Buffer | undefined> {
|
public async generateQRCode(): Promise<Uint8ClampedArray | undefined> {
|
||||||
return this.getQRCodeBytes();
|
return this.getQRCodeBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,7 +479,7 @@ export class VerificationRequest<C extends IVerificationChannel = IVerificationC
|
|||||||
return verifier;
|
return verifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public scanQRCode(qrCodeData: Uint8Array): Promise<Verifier> {
|
public scanQRCode(qrCodeData: Uint8ClampedArray): Promise<Verifier> {
|
||||||
throw new Error("QR code scanning not supported by legacy crypto");
|
throw new Error("QR code scanning not supported by legacy crypto");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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.
|
* Computes a SHA-256 hash of a string (after utf-8 encoding) and returns it as an ArrayBuffer.
|
||||||
*
|
*
|
||||||
* @param plaintext The string to hash
|
* @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
|
* @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).
|
* in a web page with an insecure context (eg. served over plain HTTP).
|
||||||
*/
|
*/
|
||||||
export async function sha256(plaintext: string): Promise<ArrayBuffer> {
|
export async function sha256(plaintext: string): Promise<Uint8Array> {
|
||||||
if (!globalThis.crypto.subtle) {
|
if (!globalThis.crypto.subtle) {
|
||||||
throw new Error("Crypto.subtle is not available: insecure context?");
|
throw new Error("Crypto.subtle is not available: insecure context?");
|
||||||
}
|
}
|
||||||
@@ -30,5 +30,5 @@ export async function sha256(plaintext: string): Promise<ArrayBuffer> {
|
|||||||
|
|
||||||
const digest = await globalThis.crypto.subtle.digest("SHA-256", utf8);
|
const digest = await globalThis.crypto.subtle.digest("SHA-256", utf8);
|
||||||
|
|
||||||
return digest;
|
return new Uint8Array(digest);
|
||||||
}
|
}
|
||||||
|
@@ -329,7 +329,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
|
|||||||
/**
|
/**
|
||||||
* Implementation of {@link CryptoBackend#getBackupDecryptor}.
|
* Implementation of {@link CryptoBackend#getBackupDecryptor}.
|
||||||
*/
|
*/
|
||||||
public async getBackupDecryptor(backupInfo: KeyBackupInfo, privKey: ArrayLike<number>): Promise<BackupDecryptor> {
|
public async getBackupDecryptor(backupInfo: KeyBackupInfo, privKey: Uint8Array): Promise<BackupDecryptor> {
|
||||||
if (!(privKey instanceof Uint8Array)) {
|
if (!(privKey instanceof Uint8Array)) {
|
||||||
throw new Error(`getBackupDecryptor: expects Uint8Array`);
|
throw new Error(`getBackupDecryptor: expects Uint8Array`);
|
||||||
}
|
}
|
||||||
@@ -1178,7 +1178,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
|
|||||||
public async getSessionBackupPrivateKey(): Promise<Uint8Array | null> {
|
public async getSessionBackupPrivateKey(): Promise<Uint8Array | null> {
|
||||||
const backupKeys: RustSdkCryptoJs.BackupKeys = await this.olmMachine.getBackupKeys();
|
const backupKeys: RustSdkCryptoJs.BackupKeys = await this.olmMachine.getBackupKeys();
|
||||||
if (!backupKeys.decryptionKey) return null;
|
if (!backupKeys.decryptionKey) return null;
|
||||||
return Buffer.from(backupKeys.decryptionKey.toBase64(), "base64");
|
return decodeBase64(backupKeys.decryptionKey.toBase64());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -381,8 +381,8 @@ export class RustVerificationRequest
|
|||||||
* @param qrCodeData - the decoded QR code.
|
* @param qrCodeData - the decoded QR code.
|
||||||
* @returns A verifier; call `.verify()` on it to wait for the other side to complete the verification flow.
|
* @returns A verifier; call `.verify()` on it to wait for the other side to complete the verification flow.
|
||||||
*/
|
*/
|
||||||
public async scanQRCode(uint8Array: Uint8Array): Promise<Verifier> {
|
public async scanQRCode(uint8Array: Uint8ClampedArray): Promise<Verifier> {
|
||||||
const scan = RustSdkCryptoJs.QrCodeScan.fromBytes(new Uint8ClampedArray(uint8Array));
|
const scan = RustSdkCryptoJs.QrCodeScan.fromBytes(uint8Array);
|
||||||
const verifier: RustSdkCryptoJs.Qr = await this.inner.scanQrCode(scan);
|
const verifier: RustSdkCryptoJs.Qr = await this.inner.scanQrCode(scan);
|
||||||
|
|
||||||
// this should have triggered the onChange callback, and we should now have a verifier
|
// 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}.
|
* 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.");
|
throw new Error("getQRCodeBytes() unsupported in Rust Crypto; use generateQRCode() instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,7 +425,7 @@ export class RustVerificationRequest
|
|||||||
*
|
*
|
||||||
* Implementation of {@link Crypto.VerificationRequest#generateQRCode}.
|
* Implementation of {@link Crypto.VerificationRequest#generateQRCode}.
|
||||||
*/
|
*/
|
||||||
public async generateQRCode(): Promise<Buffer | undefined> {
|
public async generateQRCode(): Promise<Uint8ClampedArray | undefined> {
|
||||||
// make sure that we have a list of the other user's devices (workaround https://github.com/matrix-org/matrix-rust-sdk/issues/2896)
|
// 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())) {
|
if (!(await this.getOtherDevice())) {
|
||||||
throw new Error("generateQRCode(): other device is unknown");
|
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 we are unable to generate a QRCode, we return undefined
|
||||||
if (!innerVerifier) return;
|
if (!innerVerifier) return;
|
||||||
|
|
||||||
return Buffer.from(innerVerifier.toBytes());
|
return innerVerifier.toBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -67,7 +67,7 @@ export default async function encryptAESSecretStorageItem(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
iv: encodeBase64(iv),
|
iv: encodeBase64(iv),
|
||||||
ciphertext: encodeBase64(ciphertext),
|
ciphertext: encodeBase64(new Uint8Array(ciphertext)),
|
||||||
mac: encodeBase64(hmac),
|
mac: encodeBase64(new Uint8Array(hmac)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user