You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-06 12:02:40 +03:00
Merge pull request #3818 from matrix-org/dbkr/all_your_base64
Refactor & make base64 functions browser-safe
This commit is contained in:
@@ -81,7 +81,7 @@ import {
|
|||||||
ToDeviceEvent,
|
ToDeviceEvent,
|
||||||
} from "./olm-utils";
|
} from "./olm-utils";
|
||||||
import { KeyBackupInfo } from "../../../src/crypto-api";
|
import { KeyBackupInfo } from "../../../src/crypto-api";
|
||||||
import { encodeBase64 } from "../../../src/crypto/olmlib";
|
import { encodeBase64 } from "../../../src/base64";
|
||||||
|
|
||||||
// The verification flows use javascript timers to set timeouts. We tell jest to use mock timer implementations
|
// The verification flows use javascript timers to set timeouts. We tell jest to use mock timer implementations
|
||||||
// to ensure that we don't end up with dangling timeouts.
|
// to ensure that we don't end up with dangling timeouts.
|
||||||
|
@@ -15,16 +15,44 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { TextEncoder, TextDecoder } from "util";
|
import { TextEncoder, TextDecoder } from "util";
|
||||||
|
import NodeBuffer from "node:buffer";
|
||||||
|
|
||||||
import { decodeBase64, encodeBase64, encodeUnpaddedBase64 } from "../../../src/common-crypto/base64";
|
import { decodeBase64, encodeBase64, encodeUnpaddedBase64 } 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;
|
||||||
|
|
||||||
|
global.atob = NodeBuffer.atob;
|
||||||
|
global.btoa = NodeBuffer.btoa;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
// eslint-disable-next-line no-global-assign
|
||||||
|
Buffer = origBuffer;
|
||||||
|
// @ts-ignore
|
||||||
|
global.atob = undefined;
|
||||||
|
// @ts-ignore
|
||||||
|
global.btoa = undefined;
|
||||||
|
});
|
||||||
|
|
||||||
describe("Crypto Base64 encoding", () => {
|
|
||||||
it("Should decode properly encoded data", async () => {
|
it("Should decode properly encoded data", async () => {
|
||||||
const toEncode = "encoding hello world";
|
const decoded = new TextDecoder().decode(decodeBase64("ZW5jb2RpbmcgaGVsbG8gd29ybGQ="));
|
||||||
const encoded = encodeBase64(new TextEncoder().encode(toEncode));
|
|
||||||
const decoded = new TextDecoder().decode(decodeBase64(encoded));
|
|
||||||
|
|
||||||
expect(decoded).toStrictEqual(toEncode);
|
expect(decoded).toStrictEqual("encoding hello world");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should decode URL-safe base64", async () => {
|
||||||
|
const decoded = new TextDecoder().decode(decodeBase64("Pz8_Pz8="));
|
||||||
|
|
||||||
|
expect(decoded).toStrictEqual("?????");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Encode unpadded should not have padding", async () => {
|
it("Encode unpadded should not have padding", async () => {
|
@@ -28,6 +28,7 @@ import { DeviceInfo } from "../../../src/crypto/deviceinfo";
|
|||||||
import { ISignatures } from "../../../src/@types/signed";
|
import { ISignatures } from "../../../src/@types/signed";
|
||||||
import { ICurve25519AuthData } from "../../../src/crypto/keybackup";
|
import { ICurve25519AuthData } from "../../../src/crypto/keybackup";
|
||||||
import { SecretStorageKeyDescription, SECRET_STORAGE_ALGORITHM_V1_AES } from "../../../src/secret-storage";
|
import { SecretStorageKeyDescription, SECRET_STORAGE_ALGORITHM_V1_AES } from "../../../src/secret-storage";
|
||||||
|
import { decodeBase64 } from "../../../src/base64";
|
||||||
|
|
||||||
async function makeTestClient(
|
async function makeTestClient(
|
||||||
userInfo: { userId: string; deviceId: string },
|
userInfo: { userId: string; deviceId: string },
|
||||||
@@ -275,13 +276,13 @@ describe("Secrets", function () {
|
|||||||
|
|
||||||
describe("bootstrap", function () {
|
describe("bootstrap", function () {
|
||||||
// keys used in some of the tests
|
// keys used in some of the tests
|
||||||
const XSK = new Uint8Array(olmlib.decodeBase64("3lo2YdJugHjfE+Or7KJ47NuKbhE7AAGLgQ/dc19913Q="));
|
const XSK = new Uint8Array(decodeBase64("3lo2YdJugHjfE+Or7KJ47NuKbhE7AAGLgQ/dc19913Q="));
|
||||||
const XSPubKey = "DRb8pFVJyEJ9OWvXeUoM0jq/C2Wt+NxzBZVuk2nRb+0";
|
const XSPubKey = "DRb8pFVJyEJ9OWvXeUoM0jq/C2Wt+NxzBZVuk2nRb+0";
|
||||||
const USK = new Uint8Array(olmlib.decodeBase64("lKWi3hJGUie5xxHgySoz8PHFnZv6wvNaud/p2shN9VU="));
|
const USK = new Uint8Array(decodeBase64("lKWi3hJGUie5xxHgySoz8PHFnZv6wvNaud/p2shN9VU="));
|
||||||
const USPubKey = "CUpoiTtHiyXpUmd+3ohb7JVxAlUaOG1NYs9Jlx8soQU";
|
const USPubKey = "CUpoiTtHiyXpUmd+3ohb7JVxAlUaOG1NYs9Jlx8soQU";
|
||||||
const SSK = new Uint8Array(olmlib.decodeBase64("1R6JVlXX99UcfUZzKuCDGQgJTw8ur1/ofgPD8pp+96M="));
|
const SSK = new Uint8Array(decodeBase64("1R6JVlXX99UcfUZzKuCDGQgJTw8ur1/ofgPD8pp+96M="));
|
||||||
const SSPubKey = "0DfNsRDzEvkCLA0gD3m7VAGJ5VClhjEsewI35xq873Q";
|
const SSPubKey = "0DfNsRDzEvkCLA0gD3m7VAGJ5VClhjEsewI35xq873Q";
|
||||||
const SSSSKey = new Uint8Array(olmlib.decodeBase64("XrmITOOdBhw6yY5Bh7trb/bgp1FRdIGyCUxxMP873R0="));
|
const SSSSKey = new Uint8Array(decodeBase64("XrmITOOdBhw6yY5Bh7trb/bgp1FRdIGyCUxxMP873R0="));
|
||||||
|
|
||||||
it("bootstraps when no storage or cross-signing keys locally", async function () {
|
it("bootstraps when no storage or cross-signing keys locally", async function () {
|
||||||
const key = new Uint8Array(16);
|
const key = new Uint8Array(16);
|
||||||
|
@@ -16,7 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
import "../../../olm-loader";
|
import "../../../olm-loader";
|
||||||
import { MatrixClient, MatrixEvent } from "../../../../src/matrix";
|
import { MatrixClient, MatrixEvent } from "../../../../src/matrix";
|
||||||
import { encodeBase64 } from "../../../../src/crypto/olmlib";
|
import { encodeBase64 } from "../../../../src/base64";
|
||||||
import "../../../../src/crypto"; // import this to cycle-break
|
import "../../../../src/crypto"; // import this to cycle-break
|
||||||
import { CrossSigningInfo } from "../../../../src/crypto/CrossSigning";
|
import { CrossSigningInfo } from "../../../../src/crypto/CrossSigning";
|
||||||
import { VerificationRequest } from "../../../../src/crypto/verification/request/VerificationRequest";
|
import { VerificationRequest } from "../../../../src/crypto/verification/request/VerificationRequest";
|
||||||
|
@@ -17,7 +17,7 @@ limitations under the License.
|
|||||||
import "../../olm-loader";
|
import "../../olm-loader";
|
||||||
import { RendezvousFailureReason, RendezvousIntent } from "../../../src/rendezvous";
|
import { RendezvousFailureReason, RendezvousIntent } from "../../../src/rendezvous";
|
||||||
import { MSC3903ECDHPayload, MSC3903ECDHv2RendezvousChannel } from "../../../src/rendezvous/channels";
|
import { MSC3903ECDHPayload, MSC3903ECDHv2RendezvousChannel } from "../../../src/rendezvous/channels";
|
||||||
import { decodeBase64 } from "../../../src/crypto/olmlib";
|
import { decodeBase64 } from "../../../src/base64";
|
||||||
import { DummyTransport } from "./DummyTransport";
|
import { DummyTransport } from "./DummyTransport";
|
||||||
|
|
||||||
function makeTransport(name: string) {
|
function makeTransport(name: string) {
|
||||||
|
@@ -29,7 +29,7 @@ import {
|
|||||||
MSC3886SimpleHttpRendezvousTransportDetails,
|
MSC3886SimpleHttpRendezvousTransportDetails,
|
||||||
} from "../../../src/rendezvous/transports";
|
} from "../../../src/rendezvous/transports";
|
||||||
import { DummyTransport } from "./DummyTransport";
|
import { DummyTransport } from "./DummyTransport";
|
||||||
import { decodeBase64 } from "../../../src/crypto/olmlib";
|
import { decodeBase64 } from "../../../src/base64";
|
||||||
import { logger } from "../../../src/logger";
|
import { logger } from "../../../src/logger";
|
||||||
import { DeviceInfo } from "../../../src/crypto/deviceinfo";
|
import { DeviceInfo } from "../../../src/crypto/deviceinfo";
|
||||||
|
|
||||||
|
79
src/base64.ts
Normal file
79
src/base64.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64 encoding and decoding utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a typed array of uint8 as unpadded base64.
|
||||||
|
* @param uint8Array - The data to encode.
|
||||||
|
* @returns The unpadded base64.
|
||||||
|
*/
|
||||||
|
export function encodeUnpaddedBase64(uint8Array: ArrayBuffer | Uint8Array): string {
|
||||||
|
return encodeBase64(uint8Array).replace(/={1,2}$/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a base64 string to a typed array of uint8.
|
||||||
|
* @param base64 - The base64 to decode.
|
||||||
|
* @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<number> {
|
||||||
|
const decoded = atob(
|
||||||
|
// built-in atob doesn't support base64url: convert so we support either
|
||||||
|
base64.replace("-", "+").replace("_", "/"),
|
||||||
|
);
|
||||||
|
for (let i = 0; i < decoded.length; ++i) {
|
||||||
|
yield decoded.charCodeAt(i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return Uint8Array.from(itFunc());
|
||||||
|
} else {
|
||||||
|
throw new Error("No base64 impl found!");
|
||||||
|
}
|
||||||
|
}
|
@@ -47,7 +47,7 @@ import { Direction, EventTimeline } from "./models/event-timeline";
|
|||||||
import { IActionsObject, PushProcessor } from "./pushprocessor";
|
import { IActionsObject, PushProcessor } from "./pushprocessor";
|
||||||
import { AutoDiscovery, AutoDiscoveryAction } from "./autodiscovery";
|
import { AutoDiscovery, AutoDiscoveryAction } from "./autodiscovery";
|
||||||
import * as olmlib from "./crypto/olmlib";
|
import * as olmlib from "./crypto/olmlib";
|
||||||
import { decodeBase64, encodeBase64 } from "./crypto/olmlib";
|
import { decodeBase64, encodeBase64 } from "./base64";
|
||||||
import { IExportedDevice as IExportedOlmDevice } from "./crypto/OlmDevice";
|
import { IExportedDevice as IExportedOlmDevice } from "./crypto/OlmDevice";
|
||||||
import { IOlmDevice } from "./crypto/algorithms/megolm";
|
import { IOlmDevice } from "./crypto/algorithms/megolm";
|
||||||
import { TypedReEmitter } from "./ReEmitter";
|
import { TypedReEmitter } from "./ReEmitter";
|
||||||
|
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base64 encoding and decoding utility for crypo.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a typed array of uint8 as base64.
|
|
||||||
* @param uint8Array - The data to encode.
|
|
||||||
* @returns The base64.
|
|
||||||
*/
|
|
||||||
export function encodeBase64(uint8Array: ArrayBuffer | Uint8Array): string {
|
|
||||||
return Buffer.from(uint8Array).toString("base64");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a typed array of uint8 as unpadded base64.
|
|
||||||
* @param uint8Array - The data to encode.
|
|
||||||
* @returns The unpadded base64.
|
|
||||||
*/
|
|
||||||
export function encodeUnpaddedBase64(uint8Array: ArrayBuffer | Uint8Array): string {
|
|
||||||
return encodeBase64(uint8Array).replace(/={1,2}$/, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a base64 string to a typed array of uint8.
|
|
||||||
* @param base64 - The base64 to decode.
|
|
||||||
* @returns The decoded data.
|
|
||||||
*/
|
|
||||||
export function decodeBase64(base64: string): Uint8Array {
|
|
||||||
return Buffer.from(base64, "base64");
|
|
||||||
}
|
|
@@ -19,7 +19,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { PkSigning } from "@matrix-org/olm";
|
import type { PkSigning } from "@matrix-org/olm";
|
||||||
import { decodeBase64, encodeBase64, IObject, pkSign, pkVerify } from "./olmlib";
|
import { IObject, pkSign, pkVerify } from "./olmlib";
|
||||||
import { logger } from "../logger";
|
import { logger } from "../logger";
|
||||||
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store";
|
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store";
|
||||||
import { decryptAES, encryptAES } from "./aes";
|
import { decryptAES, encryptAES } from "./aes";
|
||||||
@@ -31,6 +31,7 @@ import { ISignatures } from "../@types/signed";
|
|||||||
import { CryptoStore, SecretStorePrivateKeys } from "./store/base";
|
import { CryptoStore, SecretStorePrivateKeys } from "./store/base";
|
||||||
import { ServerSideSecretStorage, SecretStorageKeyDescription } from "../secret-storage";
|
import { ServerSideSecretStorage, SecretStorageKeyDescription } from "../secret-storage";
|
||||||
import { DeviceVerificationStatus, UserVerificationStatus as UserTrustLevel } from "../crypto-api";
|
import { DeviceVerificationStatus, UserVerificationStatus as UserTrustLevel } from "../crypto-api";
|
||||||
|
import { decodeBase64, encodeBase64 } from "../base64";
|
||||||
|
|
||||||
// backwards-compatibility re-exports
|
// backwards-compatibility re-exports
|
||||||
export { UserTrustLevel };
|
export { UserTrustLevel };
|
||||||
|
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { decodeBase64, encodeBase64 } from "./olmlib";
|
import { decodeBase64, encodeBase64 } from "../base64";
|
||||||
import { subtleCrypto, crypto, TextEncoder } from "./crypto";
|
import { subtleCrypto, crypto, TextEncoder } from "./crypto";
|
||||||
|
|
||||||
// salt for HKDF, with 8 bytes of zeros
|
// salt for HKDF, with 8 bytes of zeros
|
||||||
|
@@ -17,7 +17,7 @@ limitations under the License.
|
|||||||
import anotherjson from "another-json";
|
import anotherjson from "another-json";
|
||||||
|
|
||||||
import type { IDeviceKeys, IOneTimeKey } from "../@types/crypto";
|
import type { IDeviceKeys, IOneTimeKey } from "../@types/crypto";
|
||||||
import { decodeBase64, encodeBase64 } from "./olmlib";
|
import { decodeBase64, encodeBase64 } from "../base64";
|
||||||
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store";
|
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store";
|
||||||
import { decryptAES, encryptAES } from "./aes";
|
import { decryptAES, encryptAES } from "./aes";
|
||||||
import { logger } from "../logger";
|
import { logger } from "../logger";
|
||||||
|
@@ -102,6 +102,7 @@ import {
|
|||||||
import { Device, DeviceMap } from "../models/device";
|
import { Device, DeviceMap } from "../models/device";
|
||||||
import { deviceInfoToDevice } from "./device-converter";
|
import { deviceInfoToDevice } from "./device-converter";
|
||||||
import { ClientPrefix, MatrixError, Method } from "../http-api";
|
import { ClientPrefix, MatrixError, Method } from "../http-api";
|
||||||
|
import { decodeBase64, encodeBase64 } from "../base64";
|
||||||
|
|
||||||
/* re-exports for backwards compatibility */
|
/* re-exports for backwards compatibility */
|
||||||
export type {
|
export type {
|
||||||
@@ -500,7 +501,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
await this.secretStorage.store("m.megolm_backup.v1", fixedKey, [keys![0]]);
|
await this.secretStorage.store("m.megolm_backup.v1", fixedKey, [keys![0]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return olmlib.decodeBase64(fixedKey || storedKey);
|
return decodeBase64(fixedKey || storedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to get key from app
|
// try to get key from app
|
||||||
@@ -1050,7 +1051,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
newKeyId = await createSSSS(opts, backupKey);
|
newKeyId = await createSSSS(opts, backupKey);
|
||||||
|
|
||||||
// store the backup key in secret storage
|
// store the backup key in secret storage
|
||||||
await secretStorage.store("m.megolm_backup.v1", olmlib.encodeBase64(backupKey!), [newKeyId]);
|
await secretStorage.store("m.megolm_backup.v1", encodeBase64(backupKey!), [newKeyId]);
|
||||||
|
|
||||||
// The backup is trusted because the user provided the private key.
|
// The backup is trusted because the user provided the private key.
|
||||||
// Sign the backup with the cross-signing key so the key backup can
|
// Sign the backup with the cross-signing key so the key backup can
|
||||||
@@ -1094,7 +1095,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
);
|
);
|
||||||
// write the key to 4S
|
// write the key to 4S
|
||||||
const privateKey = decodeRecoveryKey(info.recovery_key);
|
const privateKey = decodeRecoveryKey(info.recovery_key);
|
||||||
await secretStorage.store("m.megolm_backup.v1", olmlib.encodeBase64(privateKey));
|
await secretStorage.store("m.megolm_backup.v1", encodeBase64(privateKey));
|
||||||
|
|
||||||
// create keyBackupInfo object to add to builder
|
// create keyBackupInfo object to add to builder
|
||||||
const data: IKeyBackupInfo = {
|
const data: IKeyBackupInfo = {
|
||||||
@@ -1122,7 +1123,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
const keyId = newKeyId || oldKeyId;
|
const keyId = newKeyId || oldKeyId;
|
||||||
await secretStorage.store("m.megolm_backup.v1", fixedBackupKey, keyId ? [keyId] : null);
|
await secretStorage.store("m.megolm_backup.v1", fixedBackupKey, keyId ? [keyId] : null);
|
||||||
}
|
}
|
||||||
const decodedBackupKey = new Uint8Array(olmlib.decodeBase64(fixedBackupKey || sessionBackupKey));
|
const decodedBackupKey = new Uint8Array(decodeBase64(fixedBackupKey || sessionBackupKey));
|
||||||
builder.addSessionBackupPrivateKeyToCache(decodedBackupKey);
|
builder.addSessionBackupPrivateKeyToCache(decodedBackupKey);
|
||||||
} else if (this.backupManager.getKeyBackupEnabled()) {
|
} else if (this.backupManager.getKeyBackupEnabled()) {
|
||||||
// key backup is enabled but we don't have a session backup key in SSSS: see if we have one in
|
// key backup is enabled but we don't have a session backup key in SSSS: see if we have one in
|
||||||
@@ -1137,7 +1138,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.info("Got session backup key from cache/user that wasn't in SSSS: saving to SSSS");
|
logger.info("Got session backup key from cache/user that wasn't in SSSS: saving to SSSS");
|
||||||
await secretStorage.store("m.megolm_backup.v1", olmlib.encodeBase64(backupKey));
|
await secretStorage.store("m.megolm_backup.v1", encodeBase64(backupKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
const operation = builder.buildOperation();
|
const operation = builder.buildOperation();
|
||||||
@@ -1178,7 +1179,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
|
|
||||||
// write the key to 4S
|
// write the key to 4S
|
||||||
const privateKey = info.privateKey;
|
const privateKey = info.privateKey;
|
||||||
await this.secretStorage.store("m.megolm_backup.v1", olmlib.encodeBase64(privateKey));
|
await this.secretStorage.store("m.megolm_backup.v1", encodeBase64(privateKey));
|
||||||
await this.storeSessionBackupPrivateKey(privateKey);
|
await this.storeSessionBackupPrivateKey(privateKey);
|
||||||
|
|
||||||
await this.backupManager.checkAndStart();
|
await this.backupManager.checkAndStart();
|
||||||
@@ -1301,13 +1302,13 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
|
|
||||||
// make sure we have a Uint8Array, rather than a string
|
// make sure we have a Uint8Array, rather than a string
|
||||||
if (typeof encodedKey === "string") {
|
if (typeof encodedKey === "string") {
|
||||||
key = new Uint8Array(olmlib.decodeBase64(fixBackupKey(encodedKey) || encodedKey));
|
key = new Uint8Array(decodeBase64(fixBackupKey(encodedKey) || encodedKey));
|
||||||
await this.storeSessionBackupPrivateKey(key);
|
await this.storeSessionBackupPrivateKey(key);
|
||||||
}
|
}
|
||||||
if (encodedKey && typeof encodedKey === "object" && "ciphertext" in encodedKey) {
|
if (encodedKey && typeof encodedKey === "object" && "ciphertext" in encodedKey) {
|
||||||
const pickleKey = Buffer.from(this.olmDevice.pickleKey);
|
const pickleKey = Buffer.from(this.olmDevice.pickleKey);
|
||||||
const decrypted = await decryptAES(encodedKey, pickleKey, "m.megolm_backup.v1");
|
const decrypted = await decryptAES(encodedKey, pickleKey, "m.megolm_backup.v1");
|
||||||
key = olmlib.decodeBase64(decrypted);
|
key = decodeBase64(decrypted);
|
||||||
}
|
}
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
@@ -1323,7 +1324,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
throw new Error(`storeSessionBackupPrivateKey expects Uint8Array, got ${key}`);
|
throw new Error(`storeSessionBackupPrivateKey expects Uint8Array, got ${key}`);
|
||||||
}
|
}
|
||||||
const pickleKey = Buffer.from(this.olmDevice.pickleKey);
|
const pickleKey = Buffer.from(this.olmDevice.pickleKey);
|
||||||
const encryptedKey = await encryptAES(olmlib.encodeBase64(key), pickleKey, "m.megolm_backup.v1");
|
const encryptedKey = await encryptAES(encodeBase64(key), pickleKey, "m.megolm_backup.v1");
|
||||||
return this.cryptoStore.doTxn("readwrite", [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
|
return this.cryptoStore.doTxn("readwrite", [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
|
||||||
this.cryptoStore.storeSecretStorePrivateKey(txn, "m.megolm_backup.v1", encryptedKey);
|
this.cryptoStore.storeSecretStorePrivateKey(txn, "m.megolm_backup.v1", encryptedKey);
|
||||||
});
|
});
|
||||||
@@ -4196,7 +4197,7 @@ export function fixBackupKey(key?: string): string | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const fixedKey = Uint8Array.from(key.split(","), (x) => parseInt(x));
|
const fixedKey = Uint8Array.from(key.split(","), (x) => parseInt(x));
|
||||||
return olmlib.encodeBase64(fixedKey);
|
return encodeBase64(fixedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -537,30 +537,3 @@ export function isOlmEncrypted(event: MatrixEvent): boolean {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a typed array of uint8 as base64.
|
|
||||||
* @param uint8Array - The data to encode.
|
|
||||||
* @returns The base64.
|
|
||||||
*/
|
|
||||||
export function encodeBase64(uint8Array: ArrayBuffer | Uint8Array): string {
|
|
||||||
return Buffer.from(uint8Array).toString("base64");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a typed array of uint8 as unpadded base64.
|
|
||||||
* @param uint8Array - The data to encode.
|
|
||||||
* @returns The unpadded base64.
|
|
||||||
*/
|
|
||||||
export function encodeUnpaddedBase64(uint8Array: ArrayBuffer | Uint8Array): string {
|
|
||||||
return encodeBase64(uint8Array).replace(/=+$/g, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a base64 string to a typed array of uint8.
|
|
||||||
* @param base64 - The base64 to decode.
|
|
||||||
* @returns The decoded data.
|
|
||||||
*/
|
|
||||||
export function decodeBase64(base64: string): Uint8Array {
|
|
||||||
return Buffer.from(base64, "base64");
|
|
||||||
}
|
|
||||||
|
@@ -21,7 +21,7 @@ limitations under the License.
|
|||||||
import { crypto } from "../crypto";
|
import { crypto } from "../crypto";
|
||||||
import { VerificationBase as Base } from "./Base";
|
import { VerificationBase as Base } from "./Base";
|
||||||
import { newKeyMismatchError, newUserCancelledError } from "./Error";
|
import { newKeyMismatchError, newUserCancelledError } from "./Error";
|
||||||
import { decodeBase64, encodeUnpaddedBase64 } from "../olmlib";
|
import { decodeBase64, encodeUnpaddedBase64 } from "../../base64";
|
||||||
import { logger } from "../../logger";
|
import { logger } from "../../logger";
|
||||||
import { VerificationRequest } from "./request/VerificationRequest";
|
import { VerificationRequest } from "./request/VerificationRequest";
|
||||||
import { MatrixClient } from "../../client";
|
import { MatrixClient } from "../../client";
|
||||||
|
@@ -25,7 +25,7 @@ import {
|
|||||||
RendezvousTransport,
|
RendezvousTransport,
|
||||||
RendezvousFailureReason,
|
RendezvousFailureReason,
|
||||||
} from "..";
|
} from "..";
|
||||||
import { encodeUnpaddedBase64, decodeBase64 } from "../../crypto/olmlib";
|
import { encodeUnpaddedBase64, decodeBase64 } from "../../base64";
|
||||||
import { crypto, subtleCrypto, TextEncoder } from "../../crypto/crypto";
|
import { crypto, subtleCrypto, TextEncoder } from "../../crypto/crypto";
|
||||||
import { generateDecimalSas } from "../../crypto/verification/SASDecimal";
|
import { generateDecimalSas } from "../../crypto/verification/SASDecimal";
|
||||||
import { UnstableValue } from "../../NamespacedValue";
|
import { UnstableValue } from "../../NamespacedValue";
|
||||||
|
@@ -70,7 +70,7 @@ import { TypedReEmitter } from "../ReEmitter";
|
|||||||
import { randomString } from "../randomstring";
|
import { randomString } from "../randomstring";
|
||||||
import { ClientStoppedError } from "../errors";
|
import { ClientStoppedError } from "../errors";
|
||||||
import { ISignatures } from "../@types/signed";
|
import { ISignatures } from "../@types/signed";
|
||||||
import { encodeBase64 } from "../common-crypto/base64";
|
import { encodeBase64 } from "../base64";
|
||||||
import { DecryptionError } from "../crypto/algorithms";
|
import { DecryptionError } from "../crypto/algorithms";
|
||||||
|
|
||||||
const ALL_VERIFICATION_METHODS = ["m.sas.v1", "m.qr_code.scan.v1", "m.qr_code.show.v1", "m.reciprocate.v1"];
|
const ALL_VERIFICATION_METHODS = ["m.sas.v1", "m.qr_code.scan.v1", "m.qr_code.show.v1", "m.reciprocate.v1"];
|
||||||
|
Reference in New Issue
Block a user