mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 11:35:04 +01:00
move base64/58 encoding into platform
fixes https://github.com/vector-im/hydrogen-web/issues/99
This commit is contained in:
parent
2bb7b3b598
commit
bbab1e9ecc
@ -207,7 +207,12 @@ export class Session {
|
|||||||
|
|
||||||
async _createSessionBackup(ssssKey, txn) {
|
async _createSessionBackup(ssssKey, txn) {
|
||||||
const secretStorage = new SecretStorage({key: ssssKey, platform: this._platform});
|
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({
|
||||||
|
platform: this._platform,
|
||||||
|
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()) {
|
||||||
if (room.isEncrypted) {
|
if (room.isEncrypted) {
|
||||||
|
@ -14,8 +14,6 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import base64 from "../../../lib/base64-arraybuffer/index.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypt an attachment.
|
* Decrypt an attachment.
|
||||||
* @param {ArrayBuffer} ciphertextBuffer The encrypted attachment data buffer.
|
* @param {ArrayBuffer} ciphertextBuffer The encrypted attachment data buffer.
|
||||||
@ -25,12 +23,14 @@ import base64 from "../../../lib/base64-arraybuffer/index.js";
|
|||||||
* @param {string} info.hashes.sha256 Base64 encoded SHA-256 hash of the ciphertext.
|
* @param {string} info.hashes.sha256 Base64 encoded SHA-256 hash of the ciphertext.
|
||||||
* @return {Promise} A promise that resolves with an ArrayBuffer when the attachment is decrypted.
|
* @return {Promise} A promise that resolves with an ArrayBuffer when the attachment is decrypted.
|
||||||
*/
|
*/
|
||||||
export async function decryptAttachment(crypto, ciphertextBuffer, info) {
|
export async function decryptAttachment(platform, ciphertextBuffer, info) {
|
||||||
if (info === undefined || info.key === undefined || info.iv === undefined
|
if (info === undefined || info.key === undefined || info.iv === undefined
|
||||||
|| info.hashes === undefined || info.hashes.sha256 === undefined) {
|
|| info.hashes === undefined || info.hashes.sha256 === undefined) {
|
||||||
throw new Error("Invalid info. Missing info.key, info.iv or info.hashes.sha256 key");
|
throw new Error("Invalid info. Missing info.key, info.iv or info.hashes.sha256 key");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {crypto} = platform;
|
||||||
|
const {base64} = platform.encoding;
|
||||||
var ivArray = base64.decode(info.iv);
|
var ivArray = base64.decode(info.iv);
|
||||||
// re-encode to not deal with padded vs unpadded
|
// re-encode to not deal with padded vs unpadded
|
||||||
var expectedSha256base64 = base64.encode(base64.decode(info.hashes.sha256));
|
var expectedSha256base64 = base64.encode(base64.decode(info.hashes.sha256));
|
||||||
@ -59,6 +59,7 @@ export async function decryptAttachment(crypto, ciphertextBuffer, info) {
|
|||||||
|
|
||||||
export async function encryptAttachment(platform, blob) {
|
export async function encryptAttachment(platform, blob) {
|
||||||
const {crypto} = platform;
|
const {crypto} = platform;
|
||||||
|
const {base64} = platform.encoding;
|
||||||
const iv = await crypto.aes.generateIV();
|
const iv = await crypto.aes.generateIV();
|
||||||
const key = await crypto.aes.generateKey("jwk", 256);
|
const key = await crypto.aes.generateKey("jwk", 256);
|
||||||
const buffer = await blob.readAsBuffer();
|
const buffer = await blob.readAsBuffer();
|
||||||
@ -69,20 +70,10 @@ export async function encryptAttachment(platform, blob) {
|
|||||||
info: {
|
info: {
|
||||||
v: "v2",
|
v: "v2",
|
||||||
key,
|
key,
|
||||||
iv: encodeUnpaddedBase64(iv),
|
iv: base64.encodeUnpadded(iv),
|
||||||
hashes: {
|
hashes: {
|
||||||
sha256: encodeUnpaddedBase64(digest)
|
sha256: base64.encodeUnpadded(digest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeUnpaddedBase64(buffer) {
|
|
||||||
const str = base64.encode(buffer);
|
|
||||||
const paddingIdx = str.indexOf("=");
|
|
||||||
if (paddingIdx !== -1) {
|
|
||||||
return str.substr(0, paddingIdx);
|
|
||||||
} else {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -14,8 +14,6 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import base64 from "../../../../lib/base64-arraybuffer/index.js";
|
|
||||||
|
|
||||||
export class SessionBackup {
|
export class SessionBackup {
|
||||||
constructor({backupInfo, decryption, hsApi}) {
|
constructor({backupInfo, decryption, hsApi}) {
|
||||||
this._backupInfo = backupInfo;
|
this._backupInfo = backupInfo;
|
||||||
@ -41,10 +39,10 @@ export class SessionBackup {
|
|||||||
this._decryption.free();
|
this._decryption.free();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fromSecretStorage({olm, secretStorage, hsApi, txn}) {
|
static async fromSecretStorage({platform, olm, secretStorage, hsApi, txn}) {
|
||||||
const base64PrivateKey = await secretStorage.readSecret("m.megolm_backup.v1", txn);
|
const base64PrivateKey = await secretStorage.readSecret("m.megolm_backup.v1", txn);
|
||||||
if (base64PrivateKey) {
|
if (base64PrivateKey) {
|
||||||
const privateKey = new Uint8Array(base64.decode(base64PrivateKey));
|
const privateKey = new Uint8Array(platform.encoding.base64.decode(base64PrivateKey));
|
||||||
const backupInfo = await hsApi.roomKeysVersion().response();
|
const backupInfo = await hsApi.roomKeysVersion().response();
|
||||||
const expectedPubKey = backupInfo.auth_data.public_key;
|
const expectedPubKey = backupInfo.auth_data.public_key;
|
||||||
const decryption = new olm.PkDecryption();
|
const decryption = new olm.PkDecryption();
|
||||||
|
@ -55,7 +55,7 @@ export class MediaRepository {
|
|||||||
async downloadEncryptedFile(fileEntry, cache = false) {
|
async downloadEncryptedFile(fileEntry, cache = false) {
|
||||||
const url = this.mxcUrl(fileEntry.url);
|
const url = this.mxcUrl(fileEntry.url);
|
||||||
const {body: encryptedBuffer} = await this._platform.request(url, {method: "GET", format: "buffer", cache}).response();
|
const {body: encryptedBuffer} = await this._platform.request(url, {method: "GET", format: "buffer", cache}).response();
|
||||||
const decryptedBuffer = await decryptAttachment(this._platform.crypto, encryptedBuffer, fileEntry);
|
const decryptedBuffer = await decryptAttachment(this._platform, encryptedBuffer, fileEntry);
|
||||||
return this._platform.createBlob(decryptedBuffer, fileEntry.mimetype);
|
return this._platform.createBlob(decryptedBuffer, fileEntry.mimetype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,8 +14,6 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import base64 from "../../../lib/base64-arraybuffer/index.js";
|
|
||||||
|
|
||||||
export class SecretStorage {
|
export class SecretStorage {
|
||||||
constructor({key, platform}) {
|
constructor({key, platform}) {
|
||||||
this._key = key;
|
this._key = key;
|
||||||
@ -40,17 +38,17 @@ export class SecretStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _decryptAESSecret(type, encryptedData) {
|
async _decryptAESSecret(type, encryptedData) {
|
||||||
|
const {base64, utf8} = this._platform.encoding;
|
||||||
// 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._platform.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
|
||||||
this._platform.utf8.encode(type), // info
|
utf8.encode(type), // info
|
||||||
"SHA-256",
|
"SHA-256",
|
||||||
512 // 512 bits or 64 bytes
|
512 // 512 bits or 64 bytes
|
||||||
);
|
);
|
||||||
const aesKey = hkdfKey.slice(0, 32);
|
const aesKey = hkdfKey.slice(0, 32);
|
||||||
const hmacKey = hkdfKey.slice(32);
|
const hmacKey = hkdfKey.slice(32);
|
||||||
|
|
||||||
const ciphertextBytes = base64.decode(encryptedData.ciphertext);
|
const ciphertextBytes = base64.decode(encryptedData.ciphertext);
|
||||||
|
|
||||||
const isVerified = await this._platform.crypto.hmac.verify(
|
const isVerified = await this._platform.crypto.hmac.verify(
|
||||||
@ -67,6 +65,6 @@ export class SecretStorage {
|
|||||||
data: ciphertextBytes
|
data: ciphertextBytes
|
||||||
});
|
});
|
||||||
|
|
||||||
return this._platform.utf8.decode(plaintextBytes);
|
return utf8.decode(plaintextBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ export async function keyFromCredential(type, credential, storage, platform, olm
|
|||||||
if (type === "phrase") {
|
if (type === "phrase") {
|
||||||
key = await keyFromPassphrase(keyDescription, credential, platform);
|
key = await keyFromPassphrase(keyDescription, credential, platform);
|
||||||
} else if (type === "key") {
|
} else if (type === "key") {
|
||||||
key = keyFromRecoveryKey(olm, keyDescription, credential);
|
key = keyFromRecoveryKey(keyDescription, credential, olm, platform);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Invalid type: ${type}`);
|
throw new Error(`Invalid type: ${type}`);
|
||||||
}
|
}
|
||||||
|
@ -33,11 +33,12 @@ export async function keyFromPassphrase(keyDescription, passphrase, platform) {
|
|||||||
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}`);
|
||||||
}
|
}
|
||||||
|
const {utf8} = platform.encoding;
|
||||||
const keyBits = await platform.crypto.derive.pbkdf2(
|
const keyBits = await platform.crypto.derive.pbkdf2(
|
||||||
platform.utf8.encode(passphrase),
|
utf8.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
|
||||||
platform.utf8.encode(passphraseParams.salt),
|
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);
|
||||||
|
@ -13,7 +13,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import bs58 from "../../../lib/bs58/index.js";
|
|
||||||
import {Key} from "./common.js";
|
import {Key} from "./common.js";
|
||||||
|
|
||||||
const OLM_RECOVERY_KEY_PREFIX = [0x8B, 0x01];
|
const OLM_RECOVERY_KEY_PREFIX = [0x8B, 0x01];
|
||||||
@ -24,8 +23,8 @@ const OLM_RECOVERY_KEY_PREFIX = [0x8B, 0x01];
|
|||||||
* @param {string} recoveryKey
|
* @param {string} recoveryKey
|
||||||
* @return {Key}
|
* @return {Key}
|
||||||
*/
|
*/
|
||||||
export function keyFromRecoveryKey(olm, keyDescription, recoveryKey) {
|
export function keyFromRecoveryKey(keyDescription, recoveryKey, olm, platform) {
|
||||||
const result = bs58.decode(recoveryKey.replace(/ /g, ''));
|
const result = platform.encoding.base58.decode(recoveryKey.replace(/ /g, ''));
|
||||||
|
|
||||||
let parity = 0;
|
let parity = 0;
|
||||||
for (const b of result) {
|
for (const b of result) {
|
||||||
|
@ -19,7 +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 {Encoding} from "./utils/Encoding.js";
|
||||||
import {OlmWorker} from "../../matrix/e2ee/OlmWorker.js";
|
import {OlmWorker} from "../../matrix/e2ee/OlmWorker.js";
|
||||||
import {IDBLogger} from "../../logs/IDBLogger.js";
|
import {IDBLogger} from "../../logs/IDBLogger.js";
|
||||||
import {RootView} from "./ui/RootView.js";
|
import {RootView} from "./ui/RootView.js";
|
||||||
@ -85,8 +85,8 @@ 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.logger = new IDBLogger("hydrogen_logs", this);
|
this.logger = new IDBLogger("hydrogen_logs", this);
|
||||||
|
this.encoding = new Encoding();
|
||||||
this.clock = new Clock();
|
this.clock = new Clock();
|
||||||
this.history = new History();
|
this.history = new History();
|
||||||
this.onlineStatus = new OnlineStatus();
|
this.onlineStatus = new OnlineStatus();
|
||||||
|
@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import base64 from "../../../../lib/base64-arraybuffer/index.js";
|
||||||
|
|
||||||
// turn IE11 result into promise
|
// turn IE11 result into promise
|
||||||
function subtleCryptoResult(promiseOrOp, method) {
|
function subtleCryptoResult(promiseOrOp, method) {
|
||||||
if (promiseOrOp instanceof Promise) {
|
if (promiseOrOp instanceof Promise) {
|
||||||
@ -302,7 +304,6 @@ function rawKeyToJwk(key) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
import base64 from "../../../../lib/base64-arraybuffer/index.js";
|
|
||||||
|
|
||||||
class AESLegacyCrypto {
|
class AESLegacyCrypto {
|
||||||
constructor(aesjs, crypto) {
|
constructor(aesjs, crypto) {
|
||||||
|
27
src/platform/web/utils/Base58.js
Normal file
27
src/platform/web/utils/Base58.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import bs58 from "../../../../lib/bs58/index.js";
|
||||||
|
|
||||||
|
export class Base58 {
|
||||||
|
encode(buffer) {
|
||||||
|
return bs58.encode(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
decode(str) {
|
||||||
|
return bs58.decode(str);
|
||||||
|
}
|
||||||
|
}
|
37
src/platform/web/utils/Base64.js
Normal file
37
src/platform/web/utils/Base64.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import base64 from "../../../../lib/base64-arraybuffer/index.js";
|
||||||
|
|
||||||
|
export class Base64 {
|
||||||
|
encodeUnpadded(buffer) {
|
||||||
|
const str = base64.encode(buffer);
|
||||||
|
const paddingIdx = str.indexOf("=");
|
||||||
|
if (paddingIdx !== -1) {
|
||||||
|
return str.substr(0, paddingIdx);
|
||||||
|
} else {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encode(buffer) {
|
||||||
|
return base64.encode(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
decode(str) {
|
||||||
|
return base64.decode(str);
|
||||||
|
}
|
||||||
|
}
|
27
src/platform/web/utils/Encoding.js
Normal file
27
src/platform/web/utils/Encoding.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {UTF8} from "../dom/UTF8.js";
|
||||||
|
import {Base64} from "./Base64.js";
|
||||||
|
import {Base58} from "./Base58.js";
|
||||||
|
|
||||||
|
export class Encoding {
|
||||||
|
constructor() {
|
||||||
|
this.utf8 = new UTF8();
|
||||||
|
this.base64 = new Base64();
|
||||||
|
this.base58 = new Base58();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user