mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-24 20:39:34 +00:00
fix(matrix): require durable authenticated recovery keys
This commit is contained in:
committed by
Vincent Koc
parent
592d458943
commit
afa7684e4b
@@ -1645,6 +1645,54 @@ describe("MatrixClient crypto bootstrapping", () => {
|
||||
expect(bootstrapSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("rejects recovery keys when secret-storage metadata cannot authenticate them", async () => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "matrix-sdk-test-"));
|
||||
const recoveryKeyPath = path.join(tmpDir, "recovery-key.json");
|
||||
fs.writeFileSync(
|
||||
recoveryKeyPath,
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
createdAt: new Date().toISOString(),
|
||||
keyId: "SSSSKEY",
|
||||
privateKeyBase64: Buffer.from([1, 2, 3, 4]).toString("base64"),
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
const checkKey = vi.fn(async () => true);
|
||||
Object.assign(matrixJsClient, {
|
||||
secretStorage: {
|
||||
getDefaultKeyId: vi.fn(async () => "SSSSKEY"),
|
||||
getKey: vi.fn(async () => [
|
||||
"SSSSKEY",
|
||||
{
|
||||
algorithm: "m.secret_storage.v1.aes-hmac-sha2",
|
||||
iv: "authenticated-iv",
|
||||
},
|
||||
]),
|
||||
checkKey,
|
||||
},
|
||||
});
|
||||
const client = new MatrixClient("https://matrix.example.org", "token", {
|
||||
encryption: true,
|
||||
recoveryKeyPath,
|
||||
});
|
||||
await (
|
||||
client as unknown as {
|
||||
ensureCryptoSupportInitialized: () => Promise<void>;
|
||||
}
|
||||
).ensureCryptoSupportInitialized();
|
||||
const bootstrapper = (
|
||||
client as unknown as {
|
||||
cryptoBootstrapper: {
|
||||
deps: { canUnlockSecretStorage: () => Promise<boolean> };
|
||||
};
|
||||
}
|
||||
).cryptoBootstrapper;
|
||||
|
||||
await expect(bootstrapper.deps.canUnlockSecretStorage()).resolves.toBe(false);
|
||||
expect(checkKey).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("provides secret storage callbacks and resolves stored recovery key", async () => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "matrix-sdk-test-"));
|
||||
const recoveryKeyPath = path.join(tmpDir, "recovery-key.json");
|
||||
|
||||
@@ -494,7 +494,11 @@ export class MatrixClient {
|
||||
if (!keyTuple || !key) {
|
||||
return false;
|
||||
}
|
||||
return await this.client.secretStorage.checkKey(key, keyTuple[1]);
|
||||
const keyInfo = keyTuple[1];
|
||||
if (!keyInfo.iv?.trim() || !keyInfo.mac?.trim()) {
|
||||
return false;
|
||||
}
|
||||
return await this.client.secretStorage.checkKey(key, keyInfo);
|
||||
},
|
||||
getDeviceId: () => this.client.getDeviceId(),
|
||||
verificationManager: this.verificationManager,
|
||||
|
||||
@@ -240,6 +240,15 @@ describe("MatrixRecoveryKeyStore", () => {
|
||||
expect(saved.privateKeyBase64).toBe(Buffer.from([9, 8, 7]).toString("base64"));
|
||||
});
|
||||
|
||||
it("does not authorize destructive reset from an ephemeral cached key", () => {
|
||||
const store = new MatrixRecoveryKeyStore();
|
||||
const callbacks = store.buildCryptoCallbacks();
|
||||
|
||||
callbacks.cacheSecretStorageKey?.("KEY123", { name: "openclaw" }, new Uint8Array([9, 8, 7]));
|
||||
|
||||
expect(store.getSecretStorageKeyCandidate("KEY123")).toBeNull();
|
||||
});
|
||||
|
||||
it("creates and persists a recovery key when secret storage is missing", async () => {
|
||||
const { store, createRecoveryKeyFromPassphrase, bootstrapSecretStorage } =
|
||||
await runSecretStorageBootstrapScenario({
|
||||
|
||||
@@ -144,10 +144,6 @@ export class MatrixRecoveryKeyStore {
|
||||
if (staged) {
|
||||
return staged[1];
|
||||
}
|
||||
const cached = this.secretStorageKeyCache.get(normalizedKeyId);
|
||||
if (cached) {
|
||||
return new Uint8Array(cached.key);
|
||||
}
|
||||
const stored = this.loadStoredRecoveryKey();
|
||||
if (!stored?.privateKeyBase64) {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user