mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 06:47:26 +00:00
Matrix: retry cross-signing after secret storage repair
This commit is contained in:
@@ -159,6 +159,55 @@ describe("MatrixCryptoBootstrapper", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("recreates secret storage and retries cross-signing when explicit bootstrap hits a stale server key", async () => {
|
||||||
|
const deps = createBootstrapperDeps();
|
||||||
|
const bootstrapCrossSigning = vi
|
||||||
|
.fn<() => Promise<void>>()
|
||||||
|
.mockRejectedValueOnce(new Error("getSecretStorageKey callback returned falsey"))
|
||||||
|
.mockResolvedValueOnce(undefined);
|
||||||
|
const crypto = createCryptoApi({
|
||||||
|
bootstrapCrossSigning,
|
||||||
|
isCrossSigningReady: vi.fn(async () => true),
|
||||||
|
userHasCrossSigningKeys: vi.fn(async () => true),
|
||||||
|
getDeviceVerificationStatus: vi.fn(async () => ({
|
||||||
|
isVerified: () => true,
|
||||||
|
localVerified: true,
|
||||||
|
crossSigningVerified: true,
|
||||||
|
signedByOwner: true,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
const bootstrapper = new MatrixCryptoBootstrapper(
|
||||||
|
deps as unknown as MatrixCryptoBootstrapperDeps<MatrixRawEvent>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await bootstrapper.bootstrap(crypto, {
|
||||||
|
strict: true,
|
||||||
|
allowSecretStorageRecreateWithoutRecoveryKey: true,
|
||||||
|
allowAutomaticCrossSigningReset: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(deps.recoveryKeyStore.bootstrapSecretStorageWithRecoveryKey).toHaveBeenCalledWith(
|
||||||
|
crypto,
|
||||||
|
{
|
||||||
|
allowSecretStorageRecreateWithoutRecoveryKey: true,
|
||||||
|
forceNewSecretStorage: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(bootstrapCrossSigning).toHaveBeenCalledTimes(2);
|
||||||
|
expect(bootstrapCrossSigning).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
expect.objectContaining({
|
||||||
|
authUploadDeviceSigningKeys: expect.any(Function),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(bootstrapCrossSigning).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
expect.objectContaining({
|
||||||
|
authUploadDeviceSigningKeys: expect.any(Function),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("fails in strict mode when cross-signing keys are still unpublished", async () => {
|
it("fails in strict mode when cross-signing keys are still unpublished", async () => {
|
||||||
const deps = createBootstrapperDeps();
|
const deps = createBootstrapperDeps();
|
||||||
const crypto = createCryptoApi({
|
const crypto = createCryptoApi({
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ export class MatrixCryptoBootstrapper<TRawEvent extends MatrixRawEvent> {
|
|||||||
const crossSigning = await this.bootstrapCrossSigning(crypto, {
|
const crossSigning = await this.bootstrapCrossSigning(crypto, {
|
||||||
forceResetCrossSigning: options.forceResetCrossSigning === true,
|
forceResetCrossSigning: options.forceResetCrossSigning === true,
|
||||||
allowAutomaticCrossSigningReset: options.allowAutomaticCrossSigningReset !== false,
|
allowAutomaticCrossSigningReset: options.allowAutomaticCrossSigningReset !== false,
|
||||||
|
allowSecretStorageRecreateWithoutRecoveryKey:
|
||||||
|
options.allowSecretStorageRecreateWithoutRecoveryKey === true,
|
||||||
strict,
|
strict,
|
||||||
});
|
});
|
||||||
await this.bootstrapSecretStorage(crypto, {
|
await this.bootstrapSecretStorage(crypto, {
|
||||||
@@ -105,6 +107,7 @@ export class MatrixCryptoBootstrapper<TRawEvent extends MatrixRawEvent> {
|
|||||||
options: {
|
options: {
|
||||||
forceResetCrossSigning: boolean;
|
forceResetCrossSigning: boolean;
|
||||||
allowAutomaticCrossSigningReset: boolean;
|
allowAutomaticCrossSigningReset: boolean;
|
||||||
|
allowSecretStorageRecreateWithoutRecoveryKey: boolean;
|
||||||
strict: boolean;
|
strict: boolean;
|
||||||
},
|
},
|
||||||
): Promise<{ ready: boolean; published: boolean }> {
|
): Promise<{ ready: boolean; published: boolean }> {
|
||||||
@@ -171,30 +174,47 @@ export class MatrixCryptoBootstrapper<TRawEvent extends MatrixRawEvent> {
|
|||||||
authUploadDeviceSigningKeys,
|
authUploadDeviceSigningKeys,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!options.allowAutomaticCrossSigningReset) {
|
const shouldRepairSecretStorage =
|
||||||
|
options.allowSecretStorageRecreateWithoutRecoveryKey &&
|
||||||
|
err instanceof Error &&
|
||||||
|
err.message.includes("getSecretStorageKey callback returned falsey");
|
||||||
|
if (shouldRepairSecretStorage) {
|
||||||
|
LogService.warn(
|
||||||
|
"MatrixClientLite",
|
||||||
|
"Cross-signing bootstrap could not access secret storage; recreating secret storage during explicit bootstrap and retrying.",
|
||||||
|
);
|
||||||
|
await this.deps.recoveryKeyStore.bootstrapSecretStorageWithRecoveryKey(crypto, {
|
||||||
|
allowSecretStorageRecreateWithoutRecoveryKey: true,
|
||||||
|
forceNewSecretStorage: true,
|
||||||
|
});
|
||||||
|
await crypto.bootstrapCrossSigning({
|
||||||
|
authUploadDeviceSigningKeys,
|
||||||
|
});
|
||||||
|
} else if (!options.allowAutomaticCrossSigningReset) {
|
||||||
LogService.warn(
|
LogService.warn(
|
||||||
"MatrixClientLite",
|
"MatrixClientLite",
|
||||||
"Initial cross-signing bootstrap failed and automatic reset is disabled:",
|
"Initial cross-signing bootstrap failed and automatic reset is disabled:",
|
||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
return { ready: false, published: false };
|
return { ready: false, published: false };
|
||||||
}
|
} else {
|
||||||
LogService.warn(
|
LogService.warn(
|
||||||
"MatrixClientLite",
|
"MatrixClientLite",
|
||||||
"Initial cross-signing bootstrap failed, trying reset:",
|
"Initial cross-signing bootstrap failed, trying reset:",
|
||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
await crypto.bootstrapCrossSigning({
|
await crypto.bootstrapCrossSigning({
|
||||||
setupNewCrossSigning: true,
|
setupNewCrossSigning: true,
|
||||||
authUploadDeviceSigningKeys,
|
authUploadDeviceSigningKeys,
|
||||||
});
|
});
|
||||||
} catch (resetErr) {
|
} catch (resetErr) {
|
||||||
LogService.warn("MatrixClientLite", "Failed to bootstrap cross-signing:", resetErr);
|
LogService.warn("MatrixClientLite", "Failed to bootstrap cross-signing:", resetErr);
|
||||||
if (options.strict) {
|
if (options.strict) {
|
||||||
throw resetErr instanceof Error ? resetErr : new Error(String(resetErr));
|
throw resetErr instanceof Error ? resetErr : new Error(String(resetErr));
|
||||||
|
}
|
||||||
|
return { ready: false, published: false };
|
||||||
}
|
}
|
||||||
return { ready: false, published: false };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ export class MatrixRecoveryKeyStore {
|
|||||||
options: {
|
options: {
|
||||||
setupNewKeyBackup?: boolean;
|
setupNewKeyBackup?: boolean;
|
||||||
allowSecretStorageRecreateWithoutRecoveryKey?: boolean;
|
allowSecretStorageRecreateWithoutRecoveryKey?: boolean;
|
||||||
|
forceNewSecretStorage?: boolean;
|
||||||
} = {},
|
} = {},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
let status: MatrixSecretStorageStatus | null = null;
|
let status: MatrixSecretStorageStatus | null = null;
|
||||||
@@ -185,6 +186,7 @@ export class MatrixRecoveryKeyStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const shouldRecreateSecretStorage =
|
const shouldRecreateSecretStorage =
|
||||||
|
options.forceNewSecretStorage === true ||
|
||||||
!hasDefaultSecretStorageKey ||
|
!hasDefaultSecretStorageKey ||
|
||||||
(!recoveryKey && status?.ready === false) ||
|
(!recoveryKey && status?.ready === false) ||
|
||||||
hasKnownInvalidSecrets;
|
hasKnownInvalidSecrets;
|
||||||
|
|||||||
Reference in New Issue
Block a user