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

87 lines
3.2 KiB
Markdown

# `name/name` — TPM 2.0 canonical name digest
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```ts
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
```ts
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.