Files
2026-05-27 23:47:53 +01:00

3.0 KiB

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

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

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.