# `public/serialize` — `TPMT_PUBLIC` canonical serialiser ## Purpose `serializeTpmtPublic` re-emits a parsed `TpmtPublic` value as the TCG-canonical byte string per Library Part 2 §12.2. It is the round-trip partner of `parseTpmtPublic`: for any input the parser accepts, `serialize(parse(bytes))` returns the same bytes. This property is load-bearing — `computeTpmtPublicName` per Part 1 §16 hashes the canonical bytes to produce the TPM name digest, and any mismatch between the parser and the serialiser would silently produce a wrong name, which in turn would silently fail every downstream attestation check. ## Features - Byte-identical round-trip across RSA, ECC, KEYEDHASH, and SYMCIPHER branches. - Preserves `rawExponent` (default-exponent normalisation is a parse-time convenience; the wire bytes are emitted unchanged). - Re-writes every `TPM2B_*` size prefix from the decoded payload length — the serialiser never trusts a stored size value. - Schema-validated input via the public API: only outputs of `parseTpmtPublic` (which are validated) can reach the serialiser. ## Usage: Basic Example ```ts import { parseTpmtPublic, serializeTpmtPublic, } from "@suaveplan/tpm-struct/public"; const pub = parseTpmtPublic(bytes); const reSerialised = serializeTpmtPublic(pub); // reSerialised equals bytes byte-for-byte. ``` ## Usage: Advanced Example ```ts import { parseTpmtPublic, serializeTpmtPublic, } from "@suaveplan/tpm-struct/public"; // Re-emit pub-area bytes through the canonical serialiser before signing // or hashing. This is the canonical witness for TCG TPM 2.0 Library // Part 1 §16 name computation. function canonicalPubArea(rawPubArea: Uint8Array): Uint8Array { return serializeTpmtPublic(parseTpmtPublic(rawPubArea)); } // Property test pattern used by `property.test.ts`: import fc from "fast-check"; fc.assert( fc.property(fc.uint8Array({ maxLength: 600 }), (bytes) => { // Parsing might throw; that is expected for invalid inputs. // The serialiser is exercised only on parse-success paths. let parsed; try { parsed = parseTpmtPublic(bytes); } catch { return true; } return serializeTpmtPublic(parsed).join() === bytes.join(); }), { numRuns: 100 } ); ``` ## API Reference - **`serializeTpmtPublic(pub: TpmtPublic): Uint8Array`** — returns a fresh byte string. Throws `TpmStructError(PARAMETERS_INVALID)` if a parameter substructure is internally inconsistent (e.g. non-NULL RSA scheme without `hashAlg`). ## Implementation Notes The serialiser dispatches on `pub.type` and walks the §12.2 field order. The symmetric / scheme / KDF sub-writers each consult their algorithm discriminator to decide whether trailing fields are emitted (e.g. an RSA scheme of `TPM_ALG_NULL` emits only the 2-byte scheme id and no hashAlg). This mirrors how the parser reads those substructures and is what makes the round-trip property hold across the full input space.