Initial Commit
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
# `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.
|
||||
Reference in New Issue
Block a user