3.2 KiB
name/name — TPM 2.0 canonical name digest
(no submodules)
Purpose
computeTpmtPublicName produces the canonical TPM 2.0 name for a
TpmtPublic value per TCG TPM 2.0 Library Part 1 — Architecture §16
Names. The canonical name is the two-byte big-endian nameAlg
identifier followed by the digest of the canonical TPMT_PUBLIC byte
serialisation under that algorithm. The W3C WebAuthn Level 3 tpm-
attestation verifier compares this exact value against the certified
name carried in the TPMS_CERTIFY_INFO.name slot to prove that the
public area the relying party recovered is the one the TPM signed
(WebAuthn L3 §8.3 step 5).
Features
- Hash backend injected via a
HashLikeinterface so this package stays universal; consumers wire@suaveplan/crypto-hash(or any compatible primitive) at the call site. - Caller-controlled algorithm selection — the injected hash MUST match
pub.nameAlg. - Rejects
pub.nameAlg === TPM_ALG_NULLwithTPM_STRUCT.NAME_ALG_NULLbecause a canonical name cannot be computed without a digest. - OTel:
tpm_struct.compute_name_duration_ms{name_alg}histogram +tpm_struct.parse_total{kind="name",outcome}counter.
Usage: Basic Example
import { createHash } from "node:crypto";
import {
computeTpmtPublicName,
parseTpmtPublic,
} from "@suaveplan/tpm-struct";
const pub = parseTpmtPublic(pubAreaBytes);
const name = computeTpmtPublicName(pub, {
digest: (b) => new Uint8Array(createHash("sha256").update(b).digest()),
});
// name === 0x000b || sha256(canonical TPMT_PUBLIC bytes)
Usage: Advanced Example
import { sha256, sha384, sha512 } from "@noble/hashes/sha2";
import { sha1 } from "@noble/hashes/legacy";
import {
computeTpmtPublicName,
parseTpmtPublic,
} from "@suaveplan/tpm-struct";
// Route to the correct backend at call time based on the public area's
// nameAlg. The package itself imports no hash code; the caller picks
// the engine.
function hashForNameAlg(nameAlg: string) {
if (nameAlg === "TPM_ALG_SHA1") return { digest: sha1 };
if (nameAlg === "TPM_ALG_SHA256") return { digest: sha256 };
if (nameAlg === "TPM_ALG_SHA384") return { digest: sha384 };
if (nameAlg === "TPM_ALG_SHA512") return { digest: sha512 };
throw new Error(`unsupported nameAlg ${nameAlg}`);
}
const pub = parseTpmtPublic(pubAreaBytes);
const name = computeTpmtPublicName(pub, hashForNameAlg(pub.nameAlg));
API Reference
computeTpmtPublicName(pub: TpmtPublic, hash: HashLike): Uint8Array— return a fresh byte stringnameAlg (2 BE) || hash.digest(serializeTpmtPublic(pub)). ThrowsTpmStructError(NAME_ALG_NULL)onpub.nameAlg === "TPM_ALG_NULL". Re-throws any error raised byhash.digest.HashLike—{ digest(bytes: Uint8Array): Uint8Array }. The digest size MUST match the algorithm (SHA-1 → 20, SHA-256 → 32, etc.).
Implementation Notes
The function delegates to serializeTpmtPublic for the canonical byte
encoding so the parser and the name digest cannot drift independently.
The 2-byte nameAlg prefix is the big-endian wire id (looked up via
nameToAlgId) rather than the symbolic name — this matches the binary
form the TPM emits and that downstream verifiers reproduce.