Initial Commit
This commit is contained in:
102
packages/security-stack/security/tpm-struct/src/public/public.md
Normal file
102
packages/security-stack/security/tpm-struct/src/public/public.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# `public/public` — `TPMT_PUBLIC` parser
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - serialize — `serializeTpmtPublic` re-emits a parsed…
|
||||
- [serialize](./serialize.md) — `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](./serialize.md) — `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](./serialize.md) — `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](./serialize.md) — `serializeTpmtPublic` re-emits a parsed `TpmtPublic` value as the TCG-canonical byte string per Library Part 2 §12.2. I…
|
||||
- [serialize](./serialize.md) — `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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user