Files
skills/docs/security-stack/security/tpm-struct/src/name/name.md
2026-05-29 08:20:56 +01:00

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 HashLike interface 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_NULL with TPM_STRUCT.NAME_ALG_NULL because 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 string nameAlg (2 BE) || hash.digest(serializeTpmtPublic(pub)). Throws TpmStructError(NAME_ALG_NULL) on pub.nameAlg === "TPM_ALG_NULL". Re-throws any error raised by hash.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.