"use strict"; const crypto = require("node:crypto"); const fs = require("node:fs"); const path = require("node:path"); class GhdrAuthorizer { constructor({ privateKeyFile = "/var/lib/guanghu/lake-lamp-authz/ghdr-authorizer-private.pem", now = () => Math.floor(Date.now() / 1000), } = {}) { this.privateKeyFile = privateKeyFile; this.now = now; this.privateKey = this.loadOrCreate(); this.publicKey = crypto.createPublicKey(this.privateKey); } loadOrCreate() { if (fs.existsSync(this.privateKeyFile)) { const metadata = fs.lstatSync(this.privateKeyFile); if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.mode & 0o077) { throw new Error("ghdr_authorizer_private_key_permissions_invalid"); } return crypto.createPrivateKey(fs.readFileSync(this.privateKeyFile)); } fs.mkdirSync(path.dirname(this.privateKeyFile), { recursive: true, mode: 0o700 }); const { privateKey } = crypto.generateKeyPairSync("ed25519"); const pem = privateKey.export({ type: "pkcs8", format: "pem" }); const temporary = `${this.privateKeyFile}.${process.pid}.tmp`; fs.writeFileSync(temporary, pem, { mode: 0o600, flag: "wx" }); fs.renameSync(temporary, this.privateKeyFile); return privateKey; } publicBinding() { const publicPem = this.publicKey.export({ type: "spki", format: "pem" }); const fingerprint = crypto.createHash("sha256").update(publicPem).digest("hex"); return { schema: "guanghu.ghdr-authorizer-public-binding/v1", authorizer_id: "JD-FD-PRIMARY-LAKE-LAMP", algorithm: "Ed25519", public_key_pem: publicPem, public_key_sha256: fingerprint, }; } issue({ controllerNodeId, targetNodeId, layoutPayloadSha256, resource, workorderId, }) { const issued = this.now(); const capability = { schema: "guanghu.ghdr-signing-capability/v1", authorizer_id: "JD-FD-PRIMARY-LAKE-LAMP", controller_node_id: controllerNodeId, target_node_id: targetNodeId, layout_payload_sha256: layoutPayloadSha256, resource, workorder_id: workorderId, issued_at_unix: issued, expires_at_unix: issued + 120, nonce: crypto.randomBytes(24).toString("base64url"), }; const canonical = Buffer.from(JSON.stringify(capability)); return { capability, capability_signature_base64url: crypto.sign(null, canonical, this.privateKey).toString("base64url"), }; } } module.exports = { GhdrAuthorizer };