/** * @license * Copyright 2022-2026 Matter.js Authors * SPDX-License-Identifier: Apache-2.0 */ import { Diagnostic } from "@matter/general"; import type { Argv } from "yargs"; import { MatterNode } from "../MatterNode.js"; export default function commands(theNode: MatterNode) { return { command: "cert", describe: "Certificate management operations", builder: (yargs: Argv) => yargs .command( ["*", "list [vendor-id]"], "List all stored certificates", yargs => { return yargs.positional("vendor-id", { describe: "Filter by vendor ID (hex format like 0xFFF1 or decimal)", type: "string", }); }, async argv => { const { vendorId: vendorIdStr } = argv; await theNode.start(); let certificates = (await theNode.certificateService()).certificates; // Filter by vendor ID if provided if (vendorIdStr) { let vendorId: number; if (vendorIdStr.startsWith("0x")) { const hexStr = vendorIdStr.replace(/^0x/i, ""); vendorId = parseInt(hexStr, 16); } else { vendorId = parseInt(vendorIdStr, 10); } if (!isFinite(vendorId)) { console.error(`Error: Invalid vendor ID "${vendorIdStr}"`); return; } certificates = certificates.filter(cert => cert.vid === vendorId); } if (certificates.length === 0) { console.log( vendorIdStr ? `No certificates found for vendor ID ${vendorIdStr}.` : "No certificates found in storage.", ); return; } console.log(`\nFound ${certificates.length} certificate(s):\n`); certificates.forEach(cert => { console.log(`Subject Key ID: ${cert.subjectKeyId}`); console.log(` Subject: ${cert.subjectAsText || cert.subject || "N/A"}`); console.log(""); }); }, ) .command( "details ", "Display detailed information about a certificate", yargs => { return yargs.positional("subject-key-id", { describe: "Subject Key ID of the certificate", type: "string", demandOption: true, }); }, async argv => { const { subjectKeyId } = argv; await theNode.start(); const cert = (await theNode.certificateService()).getCertificate(subjectKeyId); if (!cert) { console.error(`Certificate with subject key ID ${subjectKeyId} not found`); return; } console.log("\nCertificate Details:"); console.log(Diagnostic.json(cert)); }, ) .command( "as-pem ", "Get certificate in PEM format", yargs => { return yargs.positional("subject-key-id", { describe: "Subject Key ID of the certificate", type: "string", demandOption: true, }); }, async argv => { const { subjectKeyId } = argv; // Normalize subject key ID by removing colons const normalizedId = subjectKeyId.replace(/:/g, "").toUpperCase(); await theNode.start(); const pemCert = await (await theNode.certificateService()).getCertificateAsPem(normalizedId); console.log(pemCert); }, ) .command( "delete ", "Deletes a certificate from the storage", yargs => { return yargs.positional("subject-key-id", { describe: "Subject Key ID of the certificate to delete", type: "string", demandOption: true, }); }, async argv => { const { subjectKeyId } = argv; // Normalize subject key ID by removing colons const normalizedId = subjectKeyId.replace(/:/g, "").toUpperCase(); await theNode.start(); await (await theNode.certificateService()).deleteCertificate(normalizedId); console.log(`Certificate ${subjectKeyId} deleted successfully`); }, ) .command( "update", "Update certificates from DCL", yargs => yargs.option("force", { describe: "Force re-download and overwrite existing certificates", type: "boolean", default: false, }), async argv => { const { force } = argv; await theNode.start(); console.log(`Updating certificates from DCL${force ? " (force mode)" : ""}...`); await (await theNode.certificateService()).update(force); console.log("Certificate update completed successfully"); const count = (await theNode.certificateService()).certificates.length; console.log(`Total certificates in storage: ${count}`); }, ) .command( "check-revoked ", "Check if a certificate is revoked (fetches from DCL on demand)", yargs => { return yargs .positional("issuer-key-id", { describe: "Authority Key Identifier of the issuer (hex, with or without colons)", type: "string", demandOption: true, }) .positional("serial-number", { describe: "Serial number of the certificate to check (hex)", type: "string", demandOption: true, }); }, async argv => { const { issuerKeyId, serialNumber } = argv; await theNode.start(); const service = await theNode.certificateService(); console.log("Checking revocation status against DCL..."); const revoked = await service.isRevoked(issuerKeyId, serialNumber); if (revoked) { console.log( `REVOKED: Certificate with serial ${serialNumber} (issuer ${issuerKeyId}) is revoked.`, ); } else { console.log( `OK: Certificate with serial ${serialNumber} (issuer ${issuerKeyId}) is not revoked.`, ); } }, ), handler: async (argv: any) => { argv.unhandled = true; }, }; }