117 lines
5.0 KiB
TypeScript
117 lines
5.0 KiB
TypeScript
import type { TpmsAttest } from "../attest/attest.types.js";
|
|
import type { TpmtPublic } from "../public/public.types.js";
|
|
|
|
/**
|
|
* Render a parsed `TpmsAttest` or `TpmtPublic` as a human-readable multi-line
|
|
* dump for debugging.
|
|
*
|
|
* Telemetry is NEVER emitted from this function — the diagnostic dumper is
|
|
* for human consumption only and SHOULD NOT be invoked from production hot
|
|
* paths. Symbolic `TPM_ALG_*` and `TPM_ECC_*` names appear inline so the
|
|
* output is grep-friendly.
|
|
*/
|
|
export function toDiagnostic(value: TpmsAttest | TpmtPublic): string {
|
|
if (isAttest(value)) {
|
|
return renderAttest(value);
|
|
}
|
|
return renderPublic(value);
|
|
}
|
|
|
|
function isAttest(v: TpmsAttest | TpmtPublic): v is TpmsAttest {
|
|
return (
|
|
typeof (v as { type?: string }).type === "string" &&
|
|
(v as { type: string }).type.startsWith("TPM_ST_ATTEST_")
|
|
);
|
|
}
|
|
|
|
function renderAttest(a: TpmsAttest): string {
|
|
const lines: string[] = [];
|
|
lines.push("TPMS_ATTEST {");
|
|
lines.push(` magic: 0x${a.magic.toString(16)}`);
|
|
lines.push(` type: ${a.type}`);
|
|
lines.push(` qualifiedSigner: ${hex(a.qualifiedSigner)}`);
|
|
lines.push(` extraData: ${hex(a.extraData)}`);
|
|
lines.push(
|
|
` clockInfo: { clock: ${a.clockInfo.clock}n, resetCount: ${a.clockInfo.resetCount}, restartCount: ${a.clockInfo.restartCount}, safe: ${a.clockInfo.safe} }`
|
|
);
|
|
lines.push(` firmwareVersion: ${a.firmwareVersion}n`);
|
|
lines.push(` attested: ${renderAttested(a)}`);
|
|
lines.push("}");
|
|
return lines.join("\n");
|
|
}
|
|
|
|
function renderAttested(a: TpmsAttest): string {
|
|
switch (a.type) {
|
|
case "TPM_ST_ATTEST_CERTIFY":
|
|
return `TPMS_CERTIFY_INFO { name: ${hex(a.attested.name)}, qualifiedName: ${hex(a.attested.qualifiedName)} }`;
|
|
case "TPM_ST_ATTEST_QUOTE":
|
|
return `TPMS_QUOTE_INFO { banks: ${a.attested.pcrSelect.length}, pcrDigest: ${hex(a.attested.pcrDigest)} }`;
|
|
case "TPM_ST_ATTEST_SESSION_AUDIT":
|
|
return `TPMS_SESSION_AUDIT_INFO { exclusive: ${a.attested.exclusiveSession}, sessionDigest: ${hex(a.attested.sessionDigest)} }`;
|
|
case "TPM_ST_ATTEST_COMMAND_AUDIT":
|
|
return `TPMS_COMMAND_AUDIT_INFO { counter: ${a.attested.auditCounter}n, digestAlg: ${a.attested.digestAlg}, auditDigest: ${hex(a.attested.auditDigest)} }`;
|
|
case "TPM_ST_ATTEST_TIME":
|
|
return `TPMS_TIME_ATTEST_INFO { time: ${a.attested.time.time}n, firmwareVersion: ${a.attested.firmwareVersion}n }`;
|
|
case "TPM_ST_ATTEST_CREATION":
|
|
return `TPMS_CREATION_INFO { objectName: ${hex(a.attested.objectName)}, creationHash: ${hex(a.attested.creationHash)} }`;
|
|
case "TPM_ST_ATTEST_NV":
|
|
return `TPMS_NV_CERTIFY_INFO { indexName: ${hex(a.attested.indexName)}, offset: ${a.attested.offset}, contents: ${hex(a.attested.nvContents)} }`;
|
|
case "TPM_ST_ATTEST_NV_DIGEST":
|
|
return `TPMS_NV_DIGEST_CERTIFY_INFO { indexName: ${hex(a.attested.indexName)}, nvDigest: ${hex(a.attested.nvDigest)} }`;
|
|
}
|
|
}
|
|
|
|
function renderPublic(p: TpmtPublic): string {
|
|
const lines: string[] = [];
|
|
lines.push("TPMT_PUBLIC {");
|
|
lines.push(` type: ${p.type}`);
|
|
lines.push(` nameAlg: ${p.nameAlg}`);
|
|
lines.push(
|
|
` objectAttributes: { raw: 0x${p.objectAttributes.raw.toString(16).padStart(8, "0")}, flags: ${flagSummary(p.objectAttributes.flags)} }`
|
|
);
|
|
lines.push(` authPolicy: ${hex(p.authPolicy)}`);
|
|
if (p.type === "TPM_ALG_RSA") {
|
|
lines.push(
|
|
` parameters: TPMS_RSA_PARMS { keyBits: ${p.parameters.keyBits}, exponent: ${p.parameters.exponent}, scheme: ${p.parameters.scheme.scheme} }`
|
|
);
|
|
lines.push(
|
|
` unique: TPM2B_PUBLIC_KEY_RSA(${p.unique.modulus.length} bytes)`
|
|
);
|
|
} else if (p.type === "TPM_ALG_ECC") {
|
|
lines.push(
|
|
` parameters: TPMS_ECC_PARMS { curve: ${p.parameters.curveID}, scheme: ${p.parameters.scheme.scheme}, kdf: ${p.parameters.kdf.scheme} }`
|
|
);
|
|
lines.push(
|
|
` unique: TPMS_ECC_POINT { x: ${p.unique.x.length} bytes, y: ${p.unique.y.length} bytes }`
|
|
);
|
|
} else if (p.type === "TPM_ALG_KEYEDHASH") {
|
|
lines.push(
|
|
` parameters: TPMS_KEYEDHASH_PARMS { scheme: ${p.parameters.scheme} }`
|
|
);
|
|
lines.push(` unique: TPM2B_DIGEST(${p.unique.length} bytes)`);
|
|
} else {
|
|
lines.push(
|
|
` parameters: TPMS_SYMCIPHER_PARMS { algorithm: ${p.parameters.sym.algorithm} }`
|
|
);
|
|
lines.push(` unique: TPM2B_DIGEST(${p.unique.length} bytes)`);
|
|
}
|
|
lines.push("}");
|
|
return lines.join("\n");
|
|
}
|
|
|
|
function flagSummary(flags: object): string {
|
|
const enabled = Object.entries(flags)
|
|
.filter(([, v]) => v === true)
|
|
.map(([k]) => k);
|
|
return enabled.length === 0 ? "{}" : `{${enabled.join(", ")}}`;
|
|
}
|
|
|
|
function hex(b: Uint8Array): string {
|
|
if (b.length === 0) return "0x";
|
|
let s = "0x";
|
|
for (let i = 0; i < b.length; i++) {
|
|
s += (b[i] as number).toString(16).padStart(2, "0");
|
|
}
|
|
return s;
|
|
}
|