mirror of https://github.com/openclaw/openclaw.git
fix: tighten device identity helper coverage
This commit is contained in:
parent
8240fc519a
commit
e8c300c353
|
|
@ -0,0 +1,61 @@
|
|||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { withTempDir } from "../test-utils/temp-dir.js";
|
||||
import {
|
||||
deriveDeviceIdFromPublicKey,
|
||||
loadOrCreateDeviceIdentity,
|
||||
normalizeDevicePublicKeyBase64Url,
|
||||
publicKeyRawBase64UrlFromPem,
|
||||
signDevicePayload,
|
||||
verifyDeviceSignature,
|
||||
} from "./device-identity.js";
|
||||
|
||||
async function withIdentity(
|
||||
run: (identity: ReturnType<typeof loadOrCreateDeviceIdentity>) => void,
|
||||
) {
|
||||
await withTempDir("openclaw-device-identity-", async (dir) => {
|
||||
const identity = loadOrCreateDeviceIdentity(path.join(dir, "device.json"));
|
||||
run(identity);
|
||||
});
|
||||
}
|
||||
|
||||
describe("device identity crypto helpers", () => {
|
||||
it("derives the same canonical raw key and device id from pem and encoded public keys", async () => {
|
||||
await withIdentity((identity) => {
|
||||
const publicKeyRaw = publicKeyRawBase64UrlFromPem(identity.publicKeyPem);
|
||||
const paddedBase64 = `${publicKeyRaw.replaceAll("-", "+").replaceAll("_", "/")}==`;
|
||||
|
||||
expect(normalizeDevicePublicKeyBase64Url(identity.publicKeyPem)).toBe(publicKeyRaw);
|
||||
expect(normalizeDevicePublicKeyBase64Url(paddedBase64)).toBe(publicKeyRaw);
|
||||
expect(deriveDeviceIdFromPublicKey(identity.publicKeyPem)).toBe(identity.deviceId);
|
||||
expect(deriveDeviceIdFromPublicKey(publicKeyRaw)).toBe(identity.deviceId);
|
||||
});
|
||||
});
|
||||
|
||||
it("signs payloads that verify against pem and raw public key forms", async () => {
|
||||
await withIdentity((identity) => {
|
||||
const payload = JSON.stringify({
|
||||
action: "system.run",
|
||||
ts: 1234,
|
||||
});
|
||||
const signature = signDevicePayload(identity.privateKeyPem, payload);
|
||||
const publicKeyRaw = publicKeyRawBase64UrlFromPem(identity.publicKeyPem);
|
||||
|
||||
expect(verifyDeviceSignature(identity.publicKeyPem, payload, signature)).toBe(true);
|
||||
expect(verifyDeviceSignature(publicKeyRaw, payload, signature)).toBe(true);
|
||||
expect(verifyDeviceSignature(publicKeyRaw, `${payload}!`, signature)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("fails closed for invalid public keys and signatures", async () => {
|
||||
await withIdentity((identity) => {
|
||||
const payload = "hello";
|
||||
const signature = signDevicePayload(identity.privateKeyPem, payload);
|
||||
|
||||
expect(normalizeDevicePublicKeyBase64Url("-----BEGIN PUBLIC KEY-----broken")).toBeNull();
|
||||
expect(deriveDeviceIdFromPublicKey("%%%")).toBeNull();
|
||||
expect(verifyDeviceSignature("%%%invalid%%%", payload, signature)).toBe(false);
|
||||
expect(verifyDeviceSignature(identity.publicKeyPem, payload, "%%%invalid%%%")).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -134,6 +134,9 @@ export function normalizeDevicePublicKeyBase64Url(publicKey: string): string | n
|
|||
return base64UrlEncode(derivePublicKeyRaw(publicKey));
|
||||
}
|
||||
const raw = base64UrlDecode(publicKey);
|
||||
if (raw.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return base64UrlEncode(raw);
|
||||
} catch {
|
||||
return null;
|
||||
|
|
@ -145,6 +148,9 @@ export function deriveDeviceIdFromPublicKey(publicKey: string): string | null {
|
|||
const raw = publicKey.includes("BEGIN")
|
||||
? derivePublicKeyRaw(publicKey)
|
||||
: base64UrlDecode(publicKey);
|
||||
if (raw.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return crypto.createHash("sha256").update(raw).digest("hex");
|
||||
} catch {
|
||||
return null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue