Files
suaveplan/packages/security-stack/security/tpm-struct/src/public/public.md
2026-05-27 23:47:53 +01:00

4.6 KiB

public/public — TPMT_PUBLIC parser

(no submodules)

- serialize — `serializeTpmtPublic` re-emits a parsed…
  • serialize — serializeTpmtPublic re-emits a parsed TpmtPublic value as the TCG-canonical byte string per Library Part 2 §12.2. I…

  • serialize — serializeTpmtPublic re-emits a parsed…

  • serialize — serializeTpmtPublic re-emits a parsed TpmtPublic value as the TCG-canonical byte string per Library Part 2 §12.2. I…

  • serialize — serializeTpmtPublic re-emits a parsed…

  • serialize — serializeTpmtPublic re-emits a parsed TpmtPublic value as the TCG-canonical byte string per Library Part 2 §12.2. I…

  • serialize — serializeTpmtPublic re-emits a parsed…

  • serialize — serializeTpmtPublic re-emits a parsed TpmtPublic value as the TCG-canonical byte string per Library Part 2 §12.2. I…

  • serialize — serializeTpmtPublic re-emits a parsed TpmtPublic value as the TCG-canonical byte string per Library Part 2 §12.2. I…

Purpose

parseTpmtPublic decodes a TCG TPM 2.0 TPMT_PUBLIC byte string per Library Part 2 §12.2 into a discriminated-union value keyed on the TPMI_ALG_PUBLIC type field. The four supported branches — TPM_ALG_RSA, TPM_ALG_ECC, TPM_ALG_KEYEDHASH, TPM_ALG_SYMCIPHER — each decode their own TPMS_*_PARMS parameter substructure and matching TPMU_PUBLIC_ID unique slot. The parser is the central seam between the on-the-wire byte format and every downstream consumer of TPM public areas: computeTpmtPublicName re-emits the bytes through serializeTpmtPublic and hashes them for the canonical Part 1 §16 name digest, while the WebAuthn tpm-format verifier validates the pubArea against the credential public key in authData.attestedCredentialData.

Features

  • Full §12.2 field order enforcement (type → nameAlg → objectAttributes → authPolicy → parameters → unique).
  • RSA-2048/3072/4096 with default-exponent (0 → 65537) normalisation per §12.2.3.5 plus a rawExponent slot preserved for round-trip fidelity.
  • ECC P-256/P-384/P-521/BN-256/BN-638/SM2-256 with curveID lookup via the §11.2.5 registry; unknown curves raise CURVE_UNSUPPORTED.
  • KEYEDHASH NULL / HMAC / XOR schemes per §12.2.3.3.
  • SYMCIPHER with full TPMT_SYM_DEF_OBJECT decoding per §12.2.3.4.
  • objectAttributes decoded into raw uint32 + typed flag struct; reserved bits non-zero raise OBJECT_ATTRIBUTES_RESERVED_NONZERO.
  • Boundary Zod schema (TpmtPublicSchema) validates the constructed result before returning — malformed parser output raises MALFORMED.
  • OTel histograms (tpm_struct.parse_tpmt_public_duration_ms{type,outcome})
    • counters (tpm_struct.parse_total{kind="public",outcome} and tpm_struct.parse_errors_total{code}).

Usage: Basic Example

import { parseTpmtPublic } from "@suaveplan/tpm-struct/public";

const pub = parseTpmtPublic(pubAreaBytes);
if (pub.type === "TPM_ALG_RSA") {
    console.log(pub.parameters.keyBits, pub.parameters.exponent);
    console.log(pub.unique.modulus.byteLength);
}

Usage: Advanced Example

import { createHash } from "node:crypto";
import {
    computeTpmtPublicName,
    parseTpmtPublic,
} from "@suaveplan/tpm-struct";

const pub = parseTpmtPublic(pubAreaBytes);
if (pub.nameAlg === "TPM_ALG_SHA256" && pub.type === "TPM_ALG_ECC") {
    const name = computeTpmtPublicName(pub, {
        digest: (b) => new Uint8Array(createHash("sha256").update(b).digest()),
    });
    // `name` is `0x000b || sha256(serializeTpmtPublic(pub))` — feed this
    // into the WebAuthn `tpm`-attestation name-match step (W3C WebAuthn
    // L3 §8.3 step 5).
}

API Reference

  • parseTpmtPublic(bytes: Uint8Array): TpmtPublic — returns a discriminated-union value. Throws TpmStructError with one of ALG_UNSUPPORTED, CURVE_UNSUPPORTED, OBJECT_ATTRIBUTES_RESERVED_NONZERO, PARAMETERS_INVALID, TRUNCATED, or MALFORMED.
  • TpmtPublic — discriminator union over type. Each branch carries nameAlg, objectAttributes, authPolicy, parameters, unique.

Implementation Notes

The parser is deliberately straight-line: every field decode advances the underlying TpmBufferReader's cursor by exactly the bytes named in the spec. assertEnd() is called at the end of every successful parse so a trailing byte is reported as TRUNCATED rather than silently dropped. The exponent-zero normalisation rule is preserved alongside rawExponent because re-serialisation must emit the original bytes so the §16 name digest stays stable.