mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2025-01-22 18:21:39 +01:00
Store cross-signing keys in format as returned from server, in separate store
This will make it easier to sign and verify signatures with these keys, as the signed value needs to have the same layout when signing and for every verification.
This commit is contained in:
parent
8c74e54f9d
commit
151090527b
@ -14,9 +14,10 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {verifyEd25519Signature, SIGNATURE_ALGORITHM} from "./common.js";
|
import {verifyEd25519Signature, getEd25519Signature, SIGNATURE_ALGORITHM} from "./common.js";
|
||||||
import {HistoryVisibility, shouldShareKey} from "./common.js";
|
import {HistoryVisibility, shouldShareKey} from "./common.js";
|
||||||
import {RoomMember} from "../room/members/RoomMember.js";
|
import {RoomMember} from "../room/members/RoomMember.js";
|
||||||
|
import {getKeyUsage, getKeyEd25519Key, getKeyUserId, KeyUsage} from "../verification/CrossSigning";
|
||||||
|
|
||||||
const TRACKING_STATUS_OUTDATED = 0;
|
const TRACKING_STATUS_OUTDATED = 0;
|
||||||
const TRACKING_STATUS_UPTODATE = 1;
|
const TRACKING_STATUS_UPTODATE = 1;
|
||||||
@ -153,7 +154,7 @@ export class DeviceTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCrossSigningKeysForUser(userId, hsApi, log) {
|
async getCrossSigningKeyForUser(userId, usage, hsApi, log) {
|
||||||
return await log.wrap("DeviceTracker.getMasterKeyForUser", async log => {
|
return await log.wrap("DeviceTracker.getMasterKeyForUser", async log => {
|
||||||
let txn = await this._storage.readTxn([
|
let txn = await this._storage.readTxn([
|
||||||
this._storage.storeNames.userIdentities
|
this._storage.storeNames.userIdentities
|
||||||
@ -163,13 +164,16 @@ export class DeviceTracker {
|
|||||||
return userIdentity.crossSigningKeys;
|
return userIdentity.crossSigningKeys;
|
||||||
}
|
}
|
||||||
// fetch from hs
|
// fetch from hs
|
||||||
await this._queryKeys([userId], hsApi, log);
|
const keys = await this._queryKeys([userId], hsApi, log);
|
||||||
// Retreive from storage now
|
switch (usage) {
|
||||||
txn = await this._storage.readTxn([
|
case KeyUsage.Master:
|
||||||
this._storage.storeNames.userIdentities
|
return keys.masterKeys.get(userId);
|
||||||
]);
|
case KeyUsage.SelfSigning:
|
||||||
userIdentity = await txn.userIdentities.get(userId);
|
return keys.selfSigningKeys.get(userId);
|
||||||
return userIdentity?.crossSigningKeys;
|
case KeyUsage.UserSigning:
|
||||||
|
return keys.userSigningKeys.get(userId);
|
||||||
|
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,22 +249,29 @@ export class DeviceTracker {
|
|||||||
"token": this._getSyncToken()
|
"token": this._getSyncToken()
|
||||||
}, {log}).response();
|
}, {log}).response();
|
||||||
|
|
||||||
const masterKeys = log.wrap("master keys", log => this._filterValidMasterKeys(deviceKeyResponse, log));
|
const masterKeys = log.wrap("master keys", log => this._filterVerifiedCrossSigningKeys(deviceKeyResponse["master_keys"], KeyUsage.Master, undefined, log));
|
||||||
const selfSigningKeys = log.wrap("self-signing keys", log => this._filterVerifiedCrossSigningKeys(deviceKeyResponse["self_signing_keys"], "self_signing", masterKeys, log))
|
const selfSigningKeys = log.wrap("self-signing keys", log => this._filterVerifiedCrossSigningKeys(deviceKeyResponse["self_signing_keys"], KeyUsage.SelfSigning, masterKeys, log));
|
||||||
const verifiedKeysPerUser = log.wrap("verify", log => this._filterVerifiedDeviceKeys(deviceKeyResponse["device_keys"], log));
|
const userSigningKeys = log.wrap("user-signing keys", log => this._filterVerifiedCrossSigningKeys(deviceKeyResponse["user_signing_keys"], KeyUsage.UserSigning, masterKeys, log));
|
||||||
|
const verifiedKeysPerUser = log.wrap("device keys", log => this._filterVerifiedDeviceKeys(deviceKeyResponse["device_keys"], log));
|
||||||
const txn = await this._storage.readWriteTxn([
|
const txn = await this._storage.readWriteTxn([
|
||||||
this._storage.storeNames.userIdentities,
|
this._storage.storeNames.userIdentities,
|
||||||
this._storage.storeNames.deviceIdentities,
|
this._storage.storeNames.deviceIdentities,
|
||||||
|
this._storage.storeNames.crossSigningKeys,
|
||||||
]);
|
]);
|
||||||
let deviceIdentities;
|
let deviceIdentities;
|
||||||
try {
|
try {
|
||||||
|
for (const key of masterKeys.values()) {
|
||||||
|
txn.crossSigningKeys.set(key);
|
||||||
|
}
|
||||||
|
for (const key of selfSigningKeys.values()) {
|
||||||
|
txn.crossSigningKeys.set(key);
|
||||||
|
}
|
||||||
|
for (const key of userSigningKeys.values()) {
|
||||||
|
txn.crossSigningKeys.set(key);
|
||||||
|
}
|
||||||
const devicesIdentitiesPerUser = await Promise.all(verifiedKeysPerUser.map(async ({userId, verifiedKeys}) => {
|
const devicesIdentitiesPerUser = await Promise.all(verifiedKeysPerUser.map(async ({userId, verifiedKeys}) => {
|
||||||
const deviceIdentities = verifiedKeys.map(deviceKeysAsDeviceIdentity);
|
const deviceIdentities = verifiedKeys.map(deviceKeysAsDeviceIdentity);
|
||||||
const crossSigningKeys = {
|
return await this._storeQueriedDevicesForUserId(userId, deviceIdentities, txn);
|
||||||
masterKey: masterKeys.get(userId),
|
|
||||||
selfSigningKey: selfSigningKeys.get(userId),
|
|
||||||
};
|
|
||||||
return await this._storeQueriedDevicesForUserId(userId, crossSigningKeys, deviceIdentities, txn);
|
|
||||||
}));
|
}));
|
||||||
deviceIdentities = devicesIdentitiesPerUser.reduce((all, devices) => all.concat(devices), []);
|
deviceIdentities = devicesIdentitiesPerUser.reduce((all, devices) => all.concat(devices), []);
|
||||||
log.set("devices", deviceIdentities.length);
|
log.set("devices", deviceIdentities.length);
|
||||||
@ -269,10 +280,15 @@ export class DeviceTracker {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
await txn.complete();
|
await txn.complete();
|
||||||
return deviceIdentities;
|
return {
|
||||||
|
deviceIdentities,
|
||||||
|
masterKeys,
|
||||||
|
selfSigningKeys,
|
||||||
|
userSigningKeys
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async _storeQueriedDevicesForUserId(userId, crossSigningKeys, deviceIdentities, txn) {
|
async _storeQueriedDevicesForUserId(userId, deviceIdentities, txn) {
|
||||||
const knownDeviceIds = await txn.deviceIdentities.getAllDeviceIds(userId);
|
const knownDeviceIds = await txn.deviceIdentities.getAllDeviceIds(userId);
|
||||||
// delete any devices that we know off but are not in the response anymore.
|
// delete any devices that we know off but are not in the response anymore.
|
||||||
// important this happens before checking if the ed25519 key changed,
|
// important this happens before checking if the ed25519 key changed,
|
||||||
@ -313,67 +329,63 @@ export class DeviceTracker {
|
|||||||
identity = createUserIdentity(userId);
|
identity = createUserIdentity(userId);
|
||||||
}
|
}
|
||||||
identity.deviceTrackingStatus = TRACKING_STATUS_UPTODATE;
|
identity.deviceTrackingStatus = TRACKING_STATUS_UPTODATE;
|
||||||
identity.crossSigningKeys = crossSigningKeys;
|
|
||||||
txn.userIdentities.set(identity);
|
txn.userIdentities.set(identity);
|
||||||
|
|
||||||
return allDeviceIdentities;
|
return allDeviceIdentities;
|
||||||
}
|
}
|
||||||
|
|
||||||
_filterValidMasterKeys(keyQueryResponse, log) {
|
_filterVerifiedCrossSigningKeys(crossSigningKeysResponse, usage, parentKeys, log) {
|
||||||
const masterKeys = new Map();
|
|
||||||
const masterKeysResponse = keyQueryResponse["master_keys"];
|
|
||||||
if (!masterKeysResponse) {
|
|
||||||
return masterKeys;
|
|
||||||
}
|
|
||||||
const validMasterKeyResponses = Object.entries(masterKeysResponse).filter(([userId, keyInfo]) => {
|
|
||||||
if (keyInfo["user_id"] !== userId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!Array.isArray(keyInfo.usage) || !keyInfo.usage.includes("master")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
validMasterKeyResponses.reduce((msks, [userId, keyInfo]) => {
|
|
||||||
const keyIds = Object.keys(keyInfo.keys);
|
|
||||||
if (keyIds.length !== 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const masterKey = keyInfo.keys[keyIds[0]];
|
|
||||||
msks.set(userId, masterKey);
|
|
||||||
return msks;
|
|
||||||
}, masterKeys);
|
|
||||||
return masterKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
_filterVerifiedCrossSigningKeys(crossSigningKeysResponse, usage, masterKeys, log) {
|
|
||||||
const keys = new Map();
|
const keys = new Map();
|
||||||
if (!crossSigningKeysResponse) {
|
if (!crossSigningKeysResponse) {
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
const validKeysResponses = Object.entries(crossSigningKeysResponse).filter(([userId, keyInfo]) => {
|
for (const [userId, keyInfo] of Object.entries(crossSigningKeysResponse)) {
|
||||||
if (keyInfo["user_id"] !== userId) {
|
log.wrap({l: userId}, log => {
|
||||||
return false;
|
const parentKeyInfo = parentKeys?.get(userId);
|
||||||
}
|
const parentKey = parentKeyInfo && getKeyEd25519Key(parentKeyInfo);
|
||||||
if (!Array.isArray(keyInfo.usage) || !keyInfo.usage.includes(usage)) {
|
if (this._validateCrossSigningKey(userId, keyInfo, usage, parentKey, log)) {
|
||||||
return false;
|
keys.set(getKeyUserId(keyInfo), keyInfo);
|
||||||
}
|
}
|
||||||
// verify with master key
|
});
|
||||||
const masterKey = masterKeys.get(userId);
|
}
|
||||||
return verifyEd25519Signature(this._olmUtil, userId, masterKey, masterKey, keyInfo, log);
|
|
||||||
});
|
|
||||||
validKeysResponses.reduce((keys, [userId, keyInfo]) => {
|
|
||||||
const keyIds = Object.keys(keyInfo.keys);
|
|
||||||
if (keyIds.length !== 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const key = keyInfo.keys[keyIds[0]];
|
|
||||||
keys.set(userId, key);
|
|
||||||
return keys;
|
|
||||||
}, keys);
|
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_validateCrossSigningKey(userId, keyInfo, usage, parentKey, log) {
|
||||||
|
if (getKeyUserId(keyInfo) !== userId) {
|
||||||
|
log.log({l: "user_id mismatch", userId: keyInfo["user_id"]});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getKeyUsage(keyInfo) !== usage) {
|
||||||
|
log.log({l: "usage mismatch", usage: keyInfo.usage});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const publicKey = getKeyEd25519Key(keyInfo);
|
||||||
|
if (!publicKey) {
|
||||||
|
log.log({l: "no ed25519 key", keys: keyInfo.keys});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const isSelfSigned = usage === "master";
|
||||||
|
const keyToVerifyWith = isSelfSigned ? publicKey : parentKey;
|
||||||
|
if (!keyToVerifyWith) {
|
||||||
|
log.log("signing_key not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const hasSignature = !!getEd25519Signature(keyInfo, userId, keyToVerifyWith);
|
||||||
|
// self-signature is optional for now, not all keys seem to have it
|
||||||
|
if (!hasSignature && keyToVerifyWith !== publicKey) {
|
||||||
|
log.log({l: "signature not found", key: keyToVerifyWith});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (hasSignature) {
|
||||||
|
if(!verifyEd25519Signature(this._olmUtil, userId, keyToVerifyWith, keyToVerifyWith, keyInfo, log)) {
|
||||||
|
log.log("signature mismatch");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {Array<{userId, verifiedKeys: Array<DeviceSection>>}
|
* @return {Array<{userId, verifiedKeys: Array<DeviceSection>>}
|
||||||
*/
|
*/
|
||||||
@ -580,7 +592,8 @@ export class DeviceTracker {
|
|||||||
// TODO: ignore the race between /sync and /keys/query for now,
|
// TODO: ignore the race between /sync and /keys/query for now,
|
||||||
// where users could get marked as outdated or added/removed from the room while
|
// where users could get marked as outdated or added/removed from the room while
|
||||||
// querying keys
|
// querying keys
|
||||||
queriedDevices = await this._queryKeys(outdatedUserIds, hsApi, log);
|
const {deviceIdentities} = await this._queryKeys(outdatedUserIds, hsApi, log);
|
||||||
|
queriedDevices = deviceIdentities;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deviceTxn = await this._storage.readTxn([
|
const deviceTxn = await this._storage.readTxn([
|
||||||
|
@ -35,16 +35,21 @@ export class DecryptionError extends Error {
|
|||||||
|
|
||||||
export const SIGNATURE_ALGORITHM = "ed25519";
|
export const SIGNATURE_ALGORITHM = "ed25519";
|
||||||
|
|
||||||
|
export function getEd25519Signature(signedValue, userId, deviceOrKeyId) {
|
||||||
|
return signedValue?.signatures?.[userId]?.[`${SIGNATURE_ALGORITHM}:${deviceOrKeyId}`];
|
||||||
|
}
|
||||||
|
|
||||||
export function verifyEd25519Signature(olmUtil, userId, deviceOrKeyId, ed25519Key, value, log = undefined) {
|
export function verifyEd25519Signature(olmUtil, userId, deviceOrKeyId, ed25519Key, value, log = undefined) {
|
||||||
|
const signature = getEd25519Signature(value, userId, deviceOrKeyId);
|
||||||
|
if (!signature) {
|
||||||
|
log?.set("no_signature", true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const clone = Object.assign({}, value);
|
const clone = Object.assign({}, value);
|
||||||
delete clone.unsigned;
|
delete clone.unsigned;
|
||||||
delete clone.signatures;
|
delete clone.signatures;
|
||||||
const canonicalJson = anotherjson.stringify(clone);
|
const canonicalJson = anotherjson.stringify(clone);
|
||||||
const signature = value?.signatures?.[userId]?.[`${SIGNATURE_ALGORITHM}:${deviceOrKeyId}`];
|
|
||||||
try {
|
try {
|
||||||
if (!signature) {
|
|
||||||
throw new Error("no signature");
|
|
||||||
}
|
|
||||||
// throws when signature is invalid
|
// throws when signature is invalid
|
||||||
olmUtil.ed25519_verify(ed25519Key, canonicalJson, signature);
|
olmUtil.ed25519_verify(ed25519Key, canonicalJson, signature);
|
||||||
return true;
|
return true;
|
||||||
|
@ -33,7 +33,8 @@ export enum StoreNames {
|
|||||||
groupSessionDecryptions = "groupSessionDecryptions",
|
groupSessionDecryptions = "groupSessionDecryptions",
|
||||||
operations = "operations",
|
operations = "operations",
|
||||||
accountData = "accountData",
|
accountData = "accountData",
|
||||||
calls = "calls"
|
calls = "calls",
|
||||||
|
crossSigningKeys = "crossSigningKeys"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const STORE_NAMES: Readonly<StoreNames[]> = Object.values(StoreNames);
|
export const STORE_NAMES: Readonly<StoreNames[]> = Object.values(StoreNames);
|
||||||
|
@ -30,6 +30,7 @@ import {TimelineFragmentStore} from "./stores/TimelineFragmentStore";
|
|||||||
import {PendingEventStore} from "./stores/PendingEventStore";
|
import {PendingEventStore} from "./stores/PendingEventStore";
|
||||||
import {UserIdentityStore} from "./stores/UserIdentityStore";
|
import {UserIdentityStore} from "./stores/UserIdentityStore";
|
||||||
import {DeviceIdentityStore} from "./stores/DeviceIdentityStore";
|
import {DeviceIdentityStore} from "./stores/DeviceIdentityStore";
|
||||||
|
import {CrossSigningKeyStore} from "./stores/CrossSigningKeyStore";
|
||||||
import {OlmSessionStore} from "./stores/OlmSessionStore";
|
import {OlmSessionStore} from "./stores/OlmSessionStore";
|
||||||
import {InboundGroupSessionStore} from "./stores/InboundGroupSessionStore";
|
import {InboundGroupSessionStore} from "./stores/InboundGroupSessionStore";
|
||||||
import {OutboundGroupSessionStore} from "./stores/OutboundGroupSessionStore";
|
import {OutboundGroupSessionStore} from "./stores/OutboundGroupSessionStore";
|
||||||
@ -145,6 +146,10 @@ export class Transaction {
|
|||||||
return this._store(StoreNames.deviceIdentities, idbStore => new DeviceIdentityStore(idbStore));
|
return this._store(StoreNames.deviceIdentities, idbStore => new DeviceIdentityStore(idbStore));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get crossSigningKeys(): CrossSigningKeyStore {
|
||||||
|
return this._store(StoreNames.crossSigningKeys, idbStore => new CrossSigningKeyStore(idbStore));
|
||||||
|
}
|
||||||
|
|
||||||
get olmSessions(): OlmSessionStore {
|
get olmSessions(): OlmSessionStore {
|
||||||
return this._store(StoreNames.olmSessions, idbStore => new OlmSessionStore(idbStore));
|
return this._store(StoreNames.olmSessions, idbStore => new OlmSessionStore(idbStore));
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,8 @@ export const schema: MigrationFunc[] = [
|
|||||||
clearAllStores,
|
clearAllStores,
|
||||||
addInboundSessionBackupIndex,
|
addInboundSessionBackupIndex,
|
||||||
migrateBackupStatus,
|
migrateBackupStatus,
|
||||||
createCallStore
|
createCallStore,
|
||||||
|
createCrossSigningKeyStore
|
||||||
];
|
];
|
||||||
// TODO: how to deal with git merge conflicts of this array?
|
// TODO: how to deal with git merge conflicts of this array?
|
||||||
|
|
||||||
@ -275,3 +276,8 @@ async function migrateBackupStatus(db: IDBDatabase, txn: IDBTransaction, localSt
|
|||||||
function createCallStore(db: IDBDatabase) : void {
|
function createCallStore(db: IDBDatabase) : void {
|
||||||
db.createObjectStore("calls", {keyPath: "key"});
|
db.createObjectStore("calls", {keyPath: "key"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//v18 create calls store
|
||||||
|
function createCrossSigningKeyStore(db: IDBDatabase) : void {
|
||||||
|
db.createObjectStore("crossSigningKeys", {keyPath: "key"});
|
||||||
|
}
|
||||||
|
70
src/matrix/storage/idb/stores/CrossSigningKeyStore.ts
Normal file
70
src/matrix/storage/idb/stores/CrossSigningKeyStore.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
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 {MAX_UNICODE, MIN_UNICODE} from "./common";
|
||||||
|
import {Store} from "../Store";
|
||||||
|
|
||||||
|
// we store cross-signing keys in the format we get them from the server
|
||||||
|
// as that is what the signature is calculated on, so to verify, we need
|
||||||
|
// it in this format anyway.
|
||||||
|
export type CrossSigningKey = {
|
||||||
|
readonly user_id: string;
|
||||||
|
readonly usage: ReadonlyArray<string>;
|
||||||
|
readonly keys: {[keyId: string]: string};
|
||||||
|
readonly signatures: {[userId: string]: {[keyId: string]: string}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CrossSigningKeyEntry = CrossSigningKey & {
|
||||||
|
key: string; // key in storage, not a crypto key
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeKey(userId: string, usage: string): string {
|
||||||
|
return `${userId}|${usage}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeKey(key: string): { userId: string, usage: string } {
|
||||||
|
const [userId, usage] = key.split("|");
|
||||||
|
return {userId, usage};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CrossSigningKeyStore {
|
||||||
|
private _store: Store<CrossSigningKeyEntry>;
|
||||||
|
|
||||||
|
constructor(store: Store<CrossSigningKeyEntry>) {
|
||||||
|
this._store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(userId: string, deviceId: string): Promise<CrossSigningKey | undefined> {
|
||||||
|
return this._store.get(encodeKey(userId, deviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
set(crossSigningKey: CrossSigningKey): void {
|
||||||
|
const deviceIdentityEntry = crossSigningKey as CrossSigningKeyEntry;
|
||||||
|
deviceIdentityEntry.key = encodeKey(crossSigningKey["user_id"], crossSigningKey.usage[0]);
|
||||||
|
this._store.put(deviceIdentityEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(userId: string, usage: string): void {
|
||||||
|
this._store.delete(encodeKey(userId, usage));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAllForUser(userId: string): void {
|
||||||
|
// exclude both keys as they are theoretical min and max,
|
||||||
|
// but we should't have a match for just the room id, or room id with max
|
||||||
|
const range = this._store.IDBKeyRange.bound(encodeKey(userId, MIN_UNICODE), encodeKey(userId, MAX_UNICODE), true, true);
|
||||||
|
this._store.delete(range);
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,12 @@ import type {ISignatures} from "./common";
|
|||||||
|
|
||||||
type Olm = typeof OlmNamespace;
|
type Olm = typeof OlmNamespace;
|
||||||
|
|
||||||
|
export enum KeyUsage {
|
||||||
|
Master = "master",
|
||||||
|
SelfSigning = "self_signing",
|
||||||
|
UserSigning = "user_signing"
|
||||||
|
};
|
||||||
|
|
||||||
export class CrossSigning {
|
export class CrossSigning {
|
||||||
private readonly storage: Storage;
|
private readonly storage: Storage;
|
||||||
private readonly secretStorage: SecretStorage;
|
private readonly secretStorage: SecretStorage;
|
||||||
@ -72,9 +78,9 @@ export class CrossSigning {
|
|||||||
} finally {
|
} finally {
|
||||||
signing.free();
|
signing.free();
|
||||||
}
|
}
|
||||||
const publishedKeys = await this.deviceTracker.getCrossSigningKeysForUser(this.ownUserId, this.hsApi, log);
|
const masterKey = await this.deviceTracker.getCrossSigningKeyForUser(this.ownUserId, KeyUsage.Master, this.hsApi, log);
|
||||||
log.set({publishedMasterKey: publishedKeys.masterKey, derivedPublicKey});
|
log.set({publishedMasterKey: masterKey, derivedPublicKey});
|
||||||
this._isMasterKeyTrusted = publishedKeys.masterKey === derivedPublicKey;
|
this._isMasterKeyTrusted = masterKey === derivedPublicKey;
|
||||||
log.set("isMasterKeyTrusted", this.isMasterKeyTrusted);
|
log.set("isMasterKeyTrusted", this.isMasterKeyTrusted);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -86,7 +92,7 @@ export class CrossSigning {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const deviceKey = this.e2eeAccount.getDeviceKeysToSignWithCrossSigning();
|
const deviceKey = this.e2eeAccount.getDeviceKeysToSignWithCrossSigning();
|
||||||
const signedDeviceKey = await this.signDevice(deviceKey);
|
const signedDeviceKey = await this.signDeviceData(deviceKey);
|
||||||
const payload = {
|
const payload = {
|
||||||
[signedDeviceKey["user_id"]]: {
|
[signedDeviceKey["user_id"]]: {
|
||||||
[signedDeviceKey["device_id"]]: signedDeviceKey
|
[signedDeviceKey["device_id"]]: signedDeviceKey
|
||||||
@ -97,7 +103,15 @@ export class CrossSigning {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async signDevice<T extends object>(data: T): Promise<T & { signatures: ISignatures }> {
|
signDevice(deviceId: string) {
|
||||||
|
// need to get the device key for the device
|
||||||
|
}
|
||||||
|
|
||||||
|
signUser(userId: string) {
|
||||||
|
// need to be able to get the msk for the user
|
||||||
|
}
|
||||||
|
|
||||||
|
private async signDeviceData<T extends object>(data: T): Promise<T & { signatures: ISignatures }> {
|
||||||
const txn = await this.storage.readTxn([this.storage.storeNames.accountData]);
|
const txn = await this.storage.readTxn([this.storage.storeNames.accountData]);
|
||||||
const seedStr = await this.secretStorage.readSecret(`m.cross_signing.self_signing`, txn);
|
const seedStr = await this.secretStorage.readSecret(`m.cross_signing.self_signing`, txn);
|
||||||
const seed = new Uint8Array(this.platform.encoding.base64.decode(seedStr));
|
const seed = new Uint8Array(this.platform.encoding.base64.decode(seedStr));
|
||||||
@ -110,3 +124,30 @@ export class CrossSigning {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getKeyUsage(keyInfo): KeyUsage | undefined {
|
||||||
|
if (!Array.isArray(keyInfo.usage) || keyInfo.usage.length !== 1) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const usage = keyInfo.usage[0];
|
||||||
|
if (usage !== KeyUsage.Master && usage !== KeyUsage.SelfSigning && usage !== KeyUsage.UserSigning) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
const algorithm = "ed25519";
|
||||||
|
const prefix = `${algorithm}:`;
|
||||||
|
|
||||||
|
export function getKeyEd25519Key(keyInfo): string | undefined {
|
||||||
|
const ed25519KeyIds = Object.keys(keyInfo.keys).filter(keyId => keyId.startsWith(prefix));
|
||||||
|
if (ed25519KeyIds.length !== 1) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const keyId = ed25519KeyIds[0];
|
||||||
|
const publicKey = keyInfo.keys[keyId];
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getKeyUserId(keyInfo): string | undefined {
|
||||||
|
return keyInfo["user_id"];
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user