move textencoder/decoder into platform

This commit is contained in:
Bruno Windels 2021-02-11 17:29:48 +01:00
parent b6938dffdb
commit e49639fda2
6 changed files with 53 additions and 21 deletions

View file

@ -184,7 +184,7 @@ export class Session {
if (this._sessionBackup) { if (this._sessionBackup) {
return false; return false;
} }
const key = await ssssKeyFromCredential(type, credential, this._storage, this._platform.crypto, this._olm); const key = await ssssKeyFromCredential(type, credential, this._storage, this._platform, this._olm);
// and create session backup, which needs to read from accountData // and create session backup, which needs to read from accountData
const readTxn = this._storage.readTxn([ const readTxn = this._storage.readTxn([
this._storage.storeNames.accountData, this._storage.storeNames.accountData,
@ -206,7 +206,7 @@ export class Session {
} }
async _createSessionBackup(ssssKey, txn) { async _createSessionBackup(ssssKey, txn) {
const secretStorage = new SecretStorage({key: ssssKey, crypto: this._platform.crypto}); const secretStorage = new SecretStorage({key: ssssKey, platform: this._platform});
this._sessionBackup = await SessionBackup.fromSecretStorage({olm: this._olm, secretStorage, hsApi: this._hsApi, txn}); this._sessionBackup = await SessionBackup.fromSecretStorage({olm: this._olm, secretStorage, hsApi: this._hsApi, txn});
if (this._sessionBackup) { if (this._sessionBackup) {
for (const room of this._rooms.values()) { for (const room of this._rooms.values()) {

View file

@ -17,9 +17,9 @@ limitations under the License.
import base64 from "../../../lib/base64-arraybuffer/index.js"; import base64 from "../../../lib/base64-arraybuffer/index.js";
export class SecretStorage { export class SecretStorage {
constructor({key, crypto}) { constructor({key, platform}) {
this._key = key; this._key = key;
this._crypto = crypto; this._platform = platform;
} }
async readSecret(name, txn) { async readSecret(name, txn) {
@ -40,14 +40,11 @@ export class SecretStorage {
} }
async _decryptAESSecret(type, encryptedData) { async _decryptAESSecret(type, encryptedData) {
// TODO: we should we move this to platform specific code
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
// now derive the aes and mac key from the 4s key // now derive the aes and mac key from the 4s key
const hkdfKey = await this._crypto.derive.hkdf( const hkdfKey = await this._platform.crypto.derive.hkdf(
this._key.binaryKey, this._key.binaryKey,
new Uint8Array(8).buffer, //zero salt new Uint8Array(8).buffer, //zero salt
textEncoder.encode(type), // info this._platform.utf8.encode(type), // info
"SHA-256", "SHA-256",
512 // 512 bits or 64 bytes 512 // 512 bits or 64 bytes
); );
@ -56,7 +53,7 @@ export class SecretStorage {
const ciphertextBytes = base64.decode(encryptedData.ciphertext); const ciphertextBytes = base64.decode(encryptedData.ciphertext);
const isVerified = await this._crypto.hmac.verify( const isVerified = await this._platform.crypto.hmac.verify(
hmacKey, base64.decode(encryptedData.mac), hmacKey, base64.decode(encryptedData.mac),
ciphertextBytes, "SHA-256"); ciphertextBytes, "SHA-256");
@ -64,12 +61,12 @@ export class SecretStorage {
throw new Error("Bad MAC"); throw new Error("Bad MAC");
} }
const plaintextBytes = await this._crypto.aes.decryptCTR({ const plaintextBytes = await this._platform.crypto.aes.decryptCTR({
key: aesKey, key: aesKey,
iv: base64.decode(encryptedData.iv), iv: base64.decode(encryptedData.iv),
data: ciphertextBytes data: ciphertextBytes
}); });
return textDecoder.decode(plaintextBytes); return this._platform.utf8.decode(plaintextBytes);
} }
} }

View file

@ -47,14 +47,14 @@ export async function readKey(txn) {
return new Key(new KeyDescription(keyData.id, keyAccountData), keyData.binaryKey); return new Key(new KeyDescription(keyData.id, keyAccountData), keyData.binaryKey);
} }
export async function keyFromCredential(type, credential, storage, crypto, olm) { export async function keyFromCredential(type, credential, storage, platform, olm) {
const keyDescription = await readDefaultKeyDescription(storage); const keyDescription = await readDefaultKeyDescription(storage);
if (!keyDescription) { if (!keyDescription) {
throw new Error("Could not find a default secret storage key in account data"); throw new Error("Could not find a default secret storage key in account data");
} }
let key; let key;
if (type === "phrase") { if (type === "phrase") {
key = await keyFromPassphrase(keyDescription, credential, crypto); key = await keyFromPassphrase(keyDescription, credential, platform);
} else if (type === "key") { } else if (type === "key") {
key = keyFromRecoveryKey(olm, keyDescription, credential); key = keyFromRecoveryKey(olm, keyDescription, credential);
} else { } else {

View file

@ -22,10 +22,10 @@ const DEFAULT_BITSIZE = 256;
/** /**
* @param {KeyDescription} keyDescription * @param {KeyDescription} keyDescription
* @param {string} passphrase * @param {string} passphrase
* @param {Crypto} crypto * @param {Platform} platform
* @return {Key} * @return {Key}
*/ */
export async function keyFromPassphrase(keyDescription, passphrase, crypto) { export async function keyFromPassphrase(keyDescription, passphrase, platform) {
const {passphraseParams} = keyDescription; const {passphraseParams} = keyDescription;
if (!passphraseParams) { if (!passphraseParams) {
throw new Error("not a passphrase key"); throw new Error("not a passphrase key");
@ -33,13 +33,11 @@ export async function keyFromPassphrase(keyDescription, passphrase, crypto) {
if (passphraseParams.algorithm !== "m.pbkdf2") { if (passphraseParams.algorithm !== "m.pbkdf2") {
throw new Error(`Unsupported passphrase algorithm: ${passphraseParams.algorithm}`); throw new Error(`Unsupported passphrase algorithm: ${passphraseParams.algorithm}`);
} }
// TODO: we should we move this to platform specific code const keyBits = await platform.crypto.derive.pbkdf2(
const textEncoder = new TextEncoder(); platform.utf8.encode(passphrase),
const keyBits = await crypto.derive.pbkdf2(
textEncoder.encode(passphrase),
passphraseParams.iterations || DEFAULT_ITERATIONS, passphraseParams.iterations || DEFAULT_ITERATIONS,
// salt is just a random string, not encoded in any way // salt is just a random string, not encoded in any way
textEncoder.encode(passphraseParams.salt), platform.utf8.encode(passphraseParams.salt),
"SHA-512", "SHA-512",
passphraseParams.bits || DEFAULT_BITSIZE); passphraseParams.bits || DEFAULT_BITSIZE);
return new Key(keyDescription, keyBits); return new Key(keyDescription, keyBits);

View file

@ -19,6 +19,7 @@ import {xhrRequest} from "./dom/request/xhr.js";
import {StorageFactory} from "../../matrix/storage/idb/StorageFactory.js"; import {StorageFactory} from "../../matrix/storage/idb/StorageFactory.js";
import {SessionInfoStorage} from "../../matrix/sessioninfo/localstorage/SessionInfoStorage.js"; import {SessionInfoStorage} from "../../matrix/sessioninfo/localstorage/SessionInfoStorage.js";
import {SettingsStorage} from "./dom/SettingsStorage.js"; import {SettingsStorage} from "./dom/SettingsStorage.js";
import {UTF8} from "./dom/UTF8.js";
import {OlmWorker} from "../../matrix/e2ee/OlmWorker.js"; import {OlmWorker} from "../../matrix/e2ee/OlmWorker.js";
import {RootView} from "./ui/RootView.js"; import {RootView} from "./ui/RootView.js";
import {Clock} from "./dom/Clock.js"; import {Clock} from "./dom/Clock.js";
@ -83,6 +84,7 @@ export class Platform {
constructor(container, paths, cryptoExtras = null) { constructor(container, paths, cryptoExtras = null) {
this._paths = paths; this._paths = paths;
this._container = container; this._container = container;
this.utf8 = new UTF8();
this.clock = new Clock(); this.clock = new Clock();
this.history = new History(); this.history = new History();
this.onlineStatus = new OnlineStatus(); this.onlineStatus = new OnlineStatus();

View file

@ -0,0 +1,35 @@
/*
Copyright 2020 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.
*/
export class UTF8 {
constructor() {
this._encoder = null;
this._decoder = null;
}
encode(str) {
if (!this._encoder) {
this._encoder = new TextEncoder();
}
return this._encoder.encode(str);
}
decode(buffer) {
if (!this._decoder) {
this._decoder = new TextDecoder();
}
return this._decoder.decode(buffer);
}
}