diff --git a/src/domain/AccountSetupViewModel.js b/src/domain/AccountSetupViewModel.js index 7930b87d..b2ce808a 100644 --- a/src/domain/AccountSetupViewModel.js +++ b/src/domain/AccountSetupViewModel.js @@ -15,7 +15,7 @@ limitations under the License. */ import {ViewModel} from "./ViewModel.js"; -import {KeyType} from "../matrix/ssss/index.js"; +import {KeyType} from "../matrix/ssss/index"; import {Status} from "./session/settings/SessionBackupViewModel.js"; export class AccountSetupViewModel extends ViewModel { diff --git a/src/domain/session/settings/SessionBackupViewModel.js b/src/domain/session/settings/SessionBackupViewModel.js index 52be43b4..5a127904 100644 --- a/src/domain/session/settings/SessionBackupViewModel.js +++ b/src/domain/session/settings/SessionBackupViewModel.js @@ -15,7 +15,7 @@ limitations under the License. */ import {ViewModel} from "../../ViewModel.js"; -import {KeyType} from "../../../matrix/ssss/index.js"; +import {KeyType} from "../../../matrix/ssss/index"; import {createEnum} from "../../../utils/enum"; export const Status = createEnum("Enabled", "SetupKey", "SetupPhrase", "Pending"); diff --git a/src/matrix/Session.js b/src/matrix/Session.js index 17c0cf94..49151252 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -42,8 +42,8 @@ import { writeKey as ssssWriteKey, removeKey as ssssRemoveKey, keyFromDehydratedDeviceKey as createSSSSKeyFromDehydratedDeviceKey -} from "./ssss/index.js"; -import {SecretStorage} from "./ssss/SecretStorage.js"; +} from "./ssss/index"; +import {SecretStorage} from "./ssss/SecretStorage"; import {ObservableValue, RetainedObservableValue} from "../observable/ObservableValue"; const PICKLE_KEY = "DEFAULT_KEY"; diff --git a/src/matrix/e2ee/Dehydration.js b/src/matrix/e2ee/Dehydration.js index 461e6c42..a14c40ce 100644 --- a/src/matrix/e2ee/Dehydration.js +++ b/src/matrix/e2ee/Dehydration.js @@ -15,8 +15,8 @@ limitations under the License. */ const DEHYDRATION_LIBOLM_PICKLE_ALGORITHM = "org.matrix.msc2697.v1.olm.libolm_pickle"; -import {KeyDescription} from "../ssss/common.js"; -import {keyFromCredentialAndDescription} from "../ssss/index.js"; +import {KeyDescription} from "../ssss/common"; +import {keyFromCredentialAndDescription} from "../ssss/index"; export async function getDehydratedDevice(hsApi, olm, platform, log) { try { diff --git a/src/matrix/ssss/SecretStorage.js b/src/matrix/ssss/SecretStorage.ts similarity index 79% rename from src/matrix/ssss/SecretStorage.js rename to src/matrix/ssss/SecretStorage.ts index 04597084..c026b453 100644 --- a/src/matrix/ssss/SecretStorage.js +++ b/src/matrix/ssss/SecretStorage.ts @@ -13,19 +13,31 @@ 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 type {Key} from "./common"; +import type {Platform} from "../../platform/web/Platform.js"; +import type {Transaction} from "../storage/idb/Transaction"; + +type EncryptedData = { + iv: string; + ciphertext: string; + mac: string; +} export class SecretStorage { - constructor({key, platform}) { + private readonly _key: Key; + private readonly _platform: Platform; + + constructor({key, platform}: {key: Key, platform: Platform}) { this._key = key; this._platform = platform; } - async readSecret(name, txn) { + async readSecret(name: string, txn: Transaction): Promise { const accountData = await txn.accountData.get(name); if (!accountData) { return; } - const encryptedData = accountData?.content?.encrypted?.[this._key.id]; + const encryptedData = accountData?.content?.encrypted?.[this._key.id] as EncryptedData; if (!encryptedData) { throw new Error(`Secret ${accountData.type} is not encrypted for key ${this._key.id}`); } @@ -37,7 +49,7 @@ export class SecretStorage { } } - async _decryptAESSecret(type, encryptedData) { + async _decryptAESSecret(type: string, encryptedData: EncryptedData): Promise { const {base64, utf8} = this._platform.encoding; // now derive the aes and mac key from the 4s key const hkdfKey = await this._platform.crypto.derive.hkdf( diff --git a/src/matrix/ssss/common.js b/src/matrix/ssss/common.ts similarity index 71% rename from src/matrix/ssss/common.js rename to src/matrix/ssss/common.ts index 406e8558..2bb5e8b5 100644 --- a/src/matrix/ssss/common.js +++ b/src/matrix/ssss/common.ts @@ -14,25 +14,42 @@ See the License for the specific language governing permissions and limitations under the License. */ +import type {Platform} from "../../platform/web/Platform.js"; + +export type KeyDescriptionData = { + algorithm: string; + passphrase: { + algorithm: string; + iterations: number; + salt: string; + bits?: number; + }; + mac: string; + iv: string; +} + export class KeyDescription { - constructor(id, keyDescription) { + private readonly _id: string; + private readonly _keyDescription: KeyDescriptionData; + + constructor(id: string, keyDescription: KeyDescriptionData) { this._id = id; this._keyDescription = keyDescription; } - get id() { + get id(): string { return this._id; } - get passphraseParams() { + get passphraseParams(): KeyDescriptionData["passphrase"] { return this._keyDescription?.passphrase; } - get algorithm() { + get algorithm(): string { return this._keyDescription?.algorithm; } - async isCompatible(key, platform) { + async isCompatible(key: Key, platform: Platform): Promise { if (this.algorithm === "m.secret_storage.v1.aes-hmac-sha2") { const kd = this._keyDescription; if (kd.mac) { @@ -53,33 +70,36 @@ export class KeyDescription { } export class Key { - constructor(keyDescription, binaryKey) { + private readonly _keyDescription: KeyDescription; + private readonly _binaryKey: Uint8Array; + + constructor(keyDescription: KeyDescription, binaryKey: Uint8Array) { this._keyDescription = keyDescription; this._binaryKey = binaryKey; } - withDescription(description) { + withDescription(description: KeyDescription): Key { return new Key(description, this._binaryKey); } - get description() { + get description(): KeyDescription { return this._keyDescription; } - get id() { + get id(): string { return this._keyDescription.id; } - get binaryKey() { + get binaryKey(): Uint8Array { return this._binaryKey; } - get algorithm() { + get algorithm(): string { return this._keyDescription.algorithm; } } -async function calculateKeyMac(key, ivStr, platform) { +async function calculateKeyMac(key: BufferSource, ivStr: string, platform: Platform): Promise { const {crypto, encoding} = platform; const {utf8, base64} = encoding; const {derive, aes, hmac} = crypto; diff --git a/src/matrix/ssss/index.js b/src/matrix/ssss/index.ts similarity index 57% rename from src/matrix/ssss/index.js rename to src/matrix/ssss/index.ts index b063ab0b..37f47963 100644 --- a/src/matrix/ssss/index.js +++ b/src/matrix/ssss/index.ts @@ -14,17 +14,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {KeyDescription, Key} from "./common.js"; -import {keyFromPassphrase} from "./passphrase.js"; -import {keyFromRecoveryKey} from "./recoveryKey.js"; +import {KeyDescription, Key} from "./common"; +import {keyFromPassphrase} from "./passphrase"; +import {keyFromRecoveryKey} from "./recoveryKey"; import {SESSION_E2EE_KEY_PREFIX} from "../e2ee/common.js"; -import {createEnum} from "../../utils/enum"; +import type {Storage} from "../storage/idb/Storage"; +import type {Transaction} from "../storage/idb/Transaction"; +import type {KeyDescriptionData} from "./common"; +import type {Platform} from "../../platform/web/Platform.js"; +import type * as OlmNamespace from "@matrix-org/olm" + +type Olm = typeof OlmNamespace; const SSSS_KEY = `${SESSION_E2EE_KEY_PREFIX}ssssKey`; -export const KeyType = createEnum("RecoveryKey", "Passphrase"); +export enum KeyType { + "RecoveryKey", + "Passphrase" +} -async function readDefaultKeyDescription(storage) { +async function readDefaultKeyDescription(storage: Storage): Promise { const txn = await storage.readTxn([ storage.storeNames.accountData ]); @@ -37,30 +46,30 @@ async function readDefaultKeyDescription(storage) { if (!keyAccountData) { return; } - return new KeyDescription(id, keyAccountData.content); + return new KeyDescription(id, keyAccountData.content as KeyDescriptionData); } -export async function writeKey(key, txn) { +export async function writeKey(key: Key, txn: Transaction): Promise { txn.session.set(SSSS_KEY, {id: key.id, binaryKey: key.binaryKey}); } -export async function readKey(txn) { +export async function readKey(txn: Transaction): Promise { const keyData = await txn.session.get(SSSS_KEY); if (!keyData) { return; } const keyAccountData = await txn.accountData.get(`m.secret_storage.key.${keyData.id}`); if (keyAccountData) { - return new Key(new KeyDescription(keyData.id, keyAccountData.content), keyData.binaryKey); + return new Key(new KeyDescription(keyData.id, keyAccountData.content as KeyDescriptionData), keyData.binaryKey); } } -export async function removeKey(txn) { - await txn.session.remove(SSSS_KEY); +export async function removeKey(txn: Transaction): Promise { + txn.session.remove(SSSS_KEY); } -export async function keyFromCredential(type, credential, storage, platform, olm) { +export async function keyFromCredential(type: KeyType, credential: string, storage: Storage, platform: Platform, olm: Olm): Promise { const keyDescription = await readDefaultKeyDescription(storage); if (!keyDescription) { throw new Error("Could not find a default secret storage key in account data"); @@ -68,8 +77,8 @@ export async function keyFromCredential(type, credential, storage, platform, olm return await keyFromCredentialAndDescription(type, credential, keyDescription, platform, olm); } -export async function keyFromCredentialAndDescription(type, credential, keyDescription, platform, olm) { - let key; +export async function keyFromCredentialAndDescription(type: KeyType, credential: string, keyDescription: KeyDescription, platform: Platform, olm: Olm): Promise { + let key: Key; if (type === KeyType.Passphrase) { key = await keyFromPassphrase(keyDescription, credential, platform); } else if (type === KeyType.RecoveryKey) { @@ -80,9 +89,9 @@ export async function keyFromCredentialAndDescription(type, credential, keyDescr return key; } -export async function keyFromDehydratedDeviceKey(key, storage, platform) { +export async function keyFromDehydratedDeviceKey(key: Key, storage: Storage, platform: Platform): Promise { const keyDescription = await readDefaultKeyDescription(storage); - if (await keyDescription.isCompatible(key, platform)) { - return key.withDescription(keyDescription); + if (await keyDescription?.isCompatible(key, platform)) { + return key.withDescription(keyDescription!); } } diff --git a/src/matrix/ssss/passphrase.js b/src/matrix/ssss/passphrase.ts similarity index 84% rename from src/matrix/ssss/passphrase.js rename to src/matrix/ssss/passphrase.ts index 681e4548..00e4801e 100644 --- a/src/matrix/ssss/passphrase.js +++ b/src/matrix/ssss/passphrase.ts @@ -14,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {Key} from "./common.js"; +import {Key} from "./common"; +import type {KeyDescription} from "./common"; +import type {Platform} from "../../platform/web/Platform.js"; const DEFAULT_ITERATIONS = 500000; const DEFAULT_BITSIZE = 256; @@ -25,7 +27,7 @@ const DEFAULT_BITSIZE = 256; * @param {Platform} platform * @return {Key} */ -export async function keyFromPassphrase(keyDescription, passphrase, platform) { +export async function keyFromPassphrase(keyDescription: KeyDescription, passphrase: string, platform: Platform): Promise { const {passphraseParams} = keyDescription; if (!passphraseParams) { throw new Error("not a passphrase key"); diff --git a/src/matrix/ssss/recoveryKey.js b/src/matrix/ssss/recoveryKey.ts similarity index 78% rename from src/matrix/ssss/recoveryKey.js rename to src/matrix/ssss/recoveryKey.ts index bfe132a4..c619ed37 100644 --- a/src/matrix/ssss/recoveryKey.js +++ b/src/matrix/ssss/recoveryKey.ts @@ -13,9 +13,13 @@ 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 {Key} from "./common.js"; +import {Key} from "./common"; +import {KeyDescription} from "./common"; +import type {Platform} from "../../platform/web/Platform.js"; +import type * as OlmNamespace from "@matrix-org/olm"; +type Olm = typeof OlmNamespace; -const OLM_RECOVERY_KEY_PREFIX = [0x8B, 0x01]; +const OLM_RECOVERY_KEY_PREFIX = [0x8B, 0x01] as const; /** * @param {Olm} olm @@ -23,7 +27,7 @@ const OLM_RECOVERY_KEY_PREFIX = [0x8B, 0x01]; * @param {string} recoveryKey * @return {Key} */ -export function keyFromRecoveryKey(keyDescription, recoveryKey, olm, platform) { +export function keyFromRecoveryKey(keyDescription: KeyDescription, recoveryKey: string, olm: Olm, platform: Platform): Key { const result = platform.encoding.base58.decode(recoveryKey.replace(/ /g, '')); let parity = 0; diff --git a/tsconfig.json b/tsconfig.json index e09e7cc5..e3fae938 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,8 @@ "compilerOptions": { "strictNullChecks": true, "noEmit": true, - "target": "ES2020" + "target": "ES2020", + "moduleResolution": "node" }, "exclude": [ "src/sdk/paths/*"