87 lines
3.2 KiB
Markdown
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.
|