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