Merge pull request 'chore(types): typed secret storage key cache; drop the cryptoCallbacks cast (#210)' (#468) from chore/typing-hygiene into lotus
CI / Secret scan (gitleaks) (push) Successful in 33s
CI / Build & Quality Checks (push) Successful in 4m1s
CI / Docker image build & smoke test (push) Skipped
CI / Trigger Desktop Build (push) Canceled after 0s
CI / Playwright smoke (e2e) (push) Canceled after 0s
CI / Secret scan (gitleaks) (push) Successful in 33s
CI / Build & Quality Checks (push) Successful in 4m1s
CI / Docker image build & smoke test (push) Skipped
CI / Trigger Desktop Build (push) Canceled after 0s
CI / Playwright smoke (e2e) (push) Canceled after 0s
This commit was merged in pull request #468.
This commit is contained in:
@@ -87,7 +87,7 @@ export const initClient = async (session: Session): Promise<MatrixClient> => {
|
||||
// the user (KeyUploadBanner) instead of retrying silently forever.
|
||||
fetchFn: watchKeyUploads((input, init) => globalThis.fetch(input, init)),
|
||||
timelineSupport: true,
|
||||
cryptoCallbacks: cryptoCallbacks as any,
|
||||
cryptoCallbacks,
|
||||
// SAS (emoji) + QR-code verification (show/scan/reciprocate).
|
||||
verificationMethods: ['m.sas.v1', 'm.qr_code.show.v1', 'm.qr_code.scan.v1', 'm.reciprocate.v1'],
|
||||
oauthClientId: oidcIdentity?.oidc.clientId,
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
const secretStorageKeys = new Map();
|
||||
|
||||
export function storePrivateKey(keyId, privateKey) {
|
||||
if (privateKey instanceof Uint8Array === false) {
|
||||
throw new Error('Unable to store, privateKey is invalid.');
|
||||
}
|
||||
secretStorageKeys.set(keyId, privateKey);
|
||||
}
|
||||
|
||||
function hasPrivateKey(keyId) {
|
||||
return secretStorageKeys.get(keyId) instanceof Uint8Array;
|
||||
}
|
||||
|
||||
function getPrivateKey(keyId) {
|
||||
return secretStorageKeys.get(keyId);
|
||||
}
|
||||
|
||||
export function clearSecretStorageKeys() {
|
||||
secretStorageKeys.clear();
|
||||
}
|
||||
|
||||
async function getSecretStorageKey({ keys }) {
|
||||
const keyIds = Object.keys(keys);
|
||||
const keyId = keyIds.find(hasPrivateKey);
|
||||
if (!keyId) return undefined;
|
||||
const privateKey = getPrivateKey(keyId);
|
||||
return [keyId, privateKey];
|
||||
}
|
||||
|
||||
function cacheSecretStorageKey(keyId, keyInfo, privateKey) {
|
||||
secretStorageKeys.set(keyId, privateKey);
|
||||
}
|
||||
|
||||
export const cryptoCallbacks = {
|
||||
getSecretStorageKey,
|
||||
cacheSecretStorageKey,
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { clearSecretStorageKeys, cryptoCallbacks, storePrivateKey } from './secretStorageKeys';
|
||||
|
||||
const ask = (...ids: string[]) =>
|
||||
cryptoCallbacks.getSecretStorageKey!(
|
||||
{ keys: Object.fromEntries(ids.map((id) => [id, {} as never])) },
|
||||
'm.cross_signing.master',
|
||||
);
|
||||
|
||||
test('[#210] secret storage key cache: null when unknown, key when stored, copies input', async () => {
|
||||
clearSecretStorageKeys();
|
||||
assert.equal(await ask('k1'), null);
|
||||
|
||||
const key = new Uint8Array([1, 2, 3]);
|
||||
storePrivateKey('k1', key);
|
||||
key[0] = 9; // the cache keeps its own copy
|
||||
assert.deepEqual(await ask('k0', 'k1'), ['k1', new Uint8Array([1, 2, 3])]);
|
||||
|
||||
cryptoCallbacks.cacheSecretStorageKey!('k2', {} as never, new Uint8Array([7]));
|
||||
assert.deepEqual(await ask('k2'), ['k2', new Uint8Array([7])]);
|
||||
|
||||
clearSecretStorageKeys();
|
||||
assert.equal(await ask('k1', 'k2'), null);
|
||||
assert.throws(() => storePrivateKey('k3', 'nope' as never), /invalid/);
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { CryptoCallbacks } from 'matrix-js-sdk/lib/crypto-api';
|
||||
|
||||
const secretStorageKeys = new Map<string, Uint8Array<ArrayBuffer>>();
|
||||
|
||||
export function storePrivateKey(keyId: string, privateKey: Uint8Array) {
|
||||
if (privateKey instanceof Uint8Array === false) {
|
||||
throw new Error('Unable to store, privateKey is invalid.');
|
||||
}
|
||||
// Our own copy, on a plain ArrayBuffer (what the SDK's callback type wants).
|
||||
secretStorageKeys.set(keyId, new Uint8Array(privateKey));
|
||||
}
|
||||
|
||||
export function clearSecretStorageKeys() {
|
||||
secretStorageKeys.clear();
|
||||
}
|
||||
|
||||
export const cryptoCallbacks: CryptoCallbacks = {
|
||||
async getSecretStorageKey({ keys }) {
|
||||
const keyId = Object.keys(keys).find((id) => secretStorageKeys.get(id) instanceof Uint8Array);
|
||||
const privateKey = keyId ? secretStorageKeys.get(keyId) : undefined;
|
||||
// The SDK expects null (not undefined) when no key is cached.
|
||||
return keyId && privateKey ? [keyId, privateKey] : null;
|
||||
},
|
||||
cacheSecretStorageKey(keyId, keyInfo, privateKey) {
|
||||
secretStorageKeys.set(keyId, privateKey);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user