Files
openclaw/src/infra/device-identity.test.ts
2026-03-13 23:50:15 +00:00

62 lines
2.5 KiB
TypeScript

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);
});
});
});