forked from mystiq/hydrogen-web
move ssssKey to e2ee prefix as well so it gets backed up too
This commit is contained in:
parent
77bd0d3f3c
commit
2ef7251079
4 changed files with 139 additions and 11 deletions
|
@ -17,6 +17,9 @@ limitations under the License.
|
|||
import {KeyDescription, Key} from "./common.js";
|
||||
import {keyFromPassphrase} from "./passphrase.js";
|
||||
import {keyFromRecoveryKey} from "./recoveryKey.js";
|
||||
import {SESSION_E2EE_KEY_PREFIX} from "../e2ee/common.js";
|
||||
|
||||
const SSSS_KEY = `${SESSION_E2EE_KEY_PREFIX}ssssKey`;
|
||||
|
||||
async function readDefaultKeyDescription(storage) {
|
||||
const txn = await storage.readTxn([
|
||||
|
@ -35,11 +38,11 @@ async function readDefaultKeyDescription(storage) {
|
|||
}
|
||||
|
||||
export async function writeKey(key, txn) {
|
||||
txn.session.set("ssssKey", {id: key.id, binaryKey: key.binaryKey});
|
||||
txn.session.set(SSSS_KEY, {id: key.id, binaryKey: key.binaryKey});
|
||||
}
|
||||
|
||||
export async function readKey(txn) {
|
||||
const keyData = await txn.session.get("ssssKey");
|
||||
const keyData = await txn.session.get(SSSS_KEY);
|
||||
if (!keyData) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import {IDOMStorage} from "./types";
|
|||
import {iterateCursor, NOT_DONE, reqAsPromise} from "./utils";
|
||||
import {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../room/members/RoomMember.js";
|
||||
import {addRoomToIdentity} from "../../e2ee/DeviceTracker.js";
|
||||
import {SESSION_E2EE_KEY_PREFIX} from "../../e2ee/common.js";
|
||||
import {SummaryData} from "../../room/RoomSummary";
|
||||
import {RoomMemberStore, MemberData} from "./stores/RoomMemberStore";
|
||||
import {RoomStateEntry} from "./stores/RoomStateStore";
|
||||
|
@ -25,7 +26,8 @@ export const schema: MigrationFunc[] = [
|
|||
createArchivedRoomSummaryStore,
|
||||
migrateOperationScopeIndex,
|
||||
createTimelineRelationsStore,
|
||||
fixMissingRoomsInUserIdentities
|
||||
fixMissingRoomsInUserIdentities,
|
||||
changeSSSSKeyPrefix,
|
||||
];
|
||||
// TODO: how to deal with git merge conflicts of this array?
|
||||
|
||||
|
@ -204,3 +206,12 @@ async function fixMissingRoomsInUserIdentities(db: IDBDatabase, txn: IDBTransact
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
// v12 move ssssKey to e2ee:ssssKey so it will get backed up in the next step
|
||||
async function changeSSSSKeyPrefix(db: IDBDatabase, txn: IDBTransaction) {
|
||||
const session = txn.objectStore("session");
|
||||
const ssssKey = await reqAsPromise(session.get("ssssKey"));
|
||||
if (ssssKey) {
|
||||
session.put({key: `${SESSION_E2EE_KEY_PREFIX}ssssKey`, value: ssssKey});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ limitations under the License.
|
|||
import {Store} from "../Store";
|
||||
import {IDOMStorage} from "../types";
|
||||
import {SESSION_E2EE_KEY_PREFIX} from "../../../e2ee/common.js";
|
||||
import {LogItem} from "../../../../logging/LogItem.js";
|
||||
import {parse, stringify} from "../../../../utils/typedJSON";
|
||||
|
||||
export interface SessionEntry {
|
||||
key: string;
|
||||
|
@ -42,33 +44,38 @@ export class SessionStore {
|
|||
// we backup to localStorage so when idb gets cleared for some reason, we don't lose our e2ee identity
|
||||
try {
|
||||
const lsKey = `${this._sessionStore.databaseName}.session.${key}`;
|
||||
const lsValue = JSON.stringify(value);
|
||||
const lsValue = stringify(value);
|
||||
this._localStorage.setItem(lsKey, lsValue);
|
||||
} catch (err) {
|
||||
console.error("could not write to localStorage", err);
|
||||
}
|
||||
}
|
||||
|
||||
writeToLocalStorage() {
|
||||
this._sessionStore.iterateValues(undefined, (value: any, key: string) => {
|
||||
writeE2EEIdentityToLocalStorage() {
|
||||
this._sessionStore.iterateValues(undefined, (entry: SessionEntry, key: string) => {
|
||||
if (key.startsWith(SESSION_E2EE_KEY_PREFIX)) {
|
||||
this._writeKeyToLocalStorage(key, value);
|
||||
this._writeKeyToLocalStorage(key, entry.value);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
tryRestoreFromLocalStorage(): boolean {
|
||||
async tryRestoreE2EEIdentityFromLocalStorage(log: LogItem): Promise<boolean> {
|
||||
let success = false;
|
||||
const lsPrefix = `${this._sessionStore.databaseName}.session.`;
|
||||
const prefix = `${lsPrefix}${SESSION_E2EE_KEY_PREFIX}`;
|
||||
for(let i = 0; i < this._localStorage.length; i += 1) {
|
||||
const lsKey = this._localStorage.key(i)!;
|
||||
if (lsKey.startsWith(prefix)) {
|
||||
const value = JSON.parse(this._localStorage.getItem(lsKey)!);
|
||||
const value = parse(this._localStorage.getItem(lsKey)!);
|
||||
const key = lsKey.substr(lsPrefix.length);
|
||||
this._sessionStore.put({key, value});
|
||||
success = true;
|
||||
// we check if we don't have this key already, as we don't want to override anything
|
||||
const hasKey = (await this._sessionStore.getKey(key)) === key;
|
||||
log.set(key, !hasKey);
|
||||
if (!hasKey) {
|
||||
this._sessionStore.put({key, value});
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
|
|
107
src/utils/typedJSON.ts
Normal file
107
src/utils/typedJSON.ts
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
Copyright 2021 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 function stringify(value: any): string {
|
||||
return JSON.stringify(encodeValue(value));
|
||||
}
|
||||
|
||||
export function parse(value: string): any {
|
||||
return decodeValue(JSON.parse(value));
|
||||
}
|
||||
|
||||
|
||||
function encodeValue(value: any): any {
|
||||
switch (typeof value) {
|
||||
case "object": {
|
||||
if (value === null || Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
// TypedArray
|
||||
if (value.byteLength) {
|
||||
return {_type: value.constructor.name, value: Array.from(value)};
|
||||
}
|
||||
let newObj = {};
|
||||
for (const prop in value) {
|
||||
if (value.hasOwnProperty(prop)) {
|
||||
newObj[prop] = encodeValue(value[prop]);
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function decodeValue(value: any): any {
|
||||
switch (typeof value) {
|
||||
case "object": {
|
||||
if (value === null || Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
if (typeof value._type === "string") {
|
||||
switch (value._type) {
|
||||
case "Int8Array": return Int8Array.from(value.value);
|
||||
case "Uint8Array": return Uint8Array.from(value.value);
|
||||
case "Uint8ClampedArray": return Uint8ClampedArray.from(value.value);
|
||||
case "Int16Array": return Int16Array.from(value.value);
|
||||
case "Uint16Array": return Uint16Array.from(value.value);
|
||||
case "Int32Array": return Int32Array.from(value.value);
|
||||
case "Uint32Array": return Uint32Array.from(value.value);
|
||||
case "Float32Array": return Float32Array.from(value.value);
|
||||
case "Float64Array": return Float64Array.from(value.value);
|
||||
case "BigInt64Array": return BigInt64Array.from(value.value);
|
||||
case "BigUint64Array": return BigUint64Array.from(value.value);
|
||||
default:
|
||||
return value.value;
|
||||
}
|
||||
}
|
||||
let newObj = {};
|
||||
for (const prop in value) {
|
||||
if (value.hasOwnProperty(prop)) {
|
||||
newObj[prop] = decodeValue(value[prop]);
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export function tests() {
|
||||
return {
|
||||
"Uint8Array and primitives": assert => {
|
||||
const value = {
|
||||
foo: "bar",
|
||||
bar: 5,
|
||||
baz: false,
|
||||
fuzz: new Uint8Array([3, 1, 2])
|
||||
};
|
||||
const serialized = stringify(value);
|
||||
assert.strictEqual(typeof serialized, "string");
|
||||
const deserialized = parse(serialized);
|
||||
assert.strictEqual(deserialized.foo, "bar");
|
||||
assert.strictEqual(deserialized.bar, 5);
|
||||
assert.strictEqual(deserialized.baz, false);
|
||||
assert(deserialized.fuzz instanceof Uint8Array);
|
||||
assert.strictEqual(deserialized.fuzz.length, 3);
|
||||
assert.strictEqual(deserialized.fuzz[0], 3);
|
||||
assert.strictEqual(deserialized.fuzz[1], 1);
|
||||
assert.strictEqual(deserialized.fuzz[2], 2);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue