Files
skills/docs/security-stack/security/tpm-struct/src/extra-data/extra-data.md
2026-05-29 08:20:56 +01:00

2.9 KiB

extra-data — TPMS_ATTEST.extraData constant-time match

(no submodules)

Purpose

extraDataMatches compares a TPM-emitted extraData blob (the value the TPM signed alongside the attest body) against the relying-party-recomputed attToBeSigned digest per W3C WebAuthn Level 3 §8.3 step 8. The function is intentionally tiny: it short-circuits on length mismatch (length is public, not a secret) and then delegates the content comparison to a caller-injected constant-time equality function. Keeping the constant- time primitive injectable lets @suaveplan/tpm-struct stay universal- tier (no @suaveplan/crypto-utils runtime dep) while letting consumers wire in whichever hardened equality their crypto stack ships.

Features

  • Length short-circuit (length is not a secret).
  • Pluggable equality function via the ConstantTimeEqual type.
  • Zero-allocation: works directly over the supplied Uint8Arrays.

Usage: Basic Example

import { extraDataMatches } from "@suaveplan/tpm-struct/extra-data";

declare const constantTimeEqual: (a: Uint8Array, b: Uint8Array) => boolean;

if (extraDataMatches(att.extraData, recomputedDigest, constantTimeEqual)) {
    // attToBeSigned matched.
}

Usage: Advanced Example

import { constantTimeEqual } from "@suaveplan/crypto-utils";
import { extraDataMatches } from "@suaveplan/tpm-struct/extra-data";

// Compose with the rest of the WebAuthn `tpm`-attestation chain.
function verifyExtraDataAndName(
    att: { extraData: Uint8Array; attested: { name: Uint8Array } },
    recomputedAttToBeSigned: Uint8Array,
    recomputedName: Uint8Array
): { extraDataOk: boolean; nameOk: boolean } {
    return {
        extraDataOk: extraDataMatches(
            att.extraData,
            recomputedAttToBeSigned,
            constantTimeEqual
        ),
        nameOk: extraDataMatches(
            att.attested.name,
            recomputedName,
            constantTimeEqual
        ),
    };
}

API Reference

  • extraDataMatches(actual: Uint8Array, expected: Uint8Array, equal: ConstantTimeEqual): boolean — true if and only if actual.length === expected.length and equal(actual, expected) returns true. Returns false on length mismatch without invoking equal.
  • ConstantTimeEqual — (a: Uint8Array, b: Uint8Array) => boolean. Implementations MUST NOT short-circuit on first byte mismatch — the wall-time MUST depend only on the longer input's length.

Implementation Notes

The function deliberately does not own a constant-time-equality fallback. A naive for loop short-circuiting on mismatch would leak content equality through timing; a sample implementation that XOR-accumulates every byte is correct but lives in @suaveplan/crypto-utils. By keeping the primitive injectable, this module avoids importing a crypto package from a universal parser while still surfacing the right API shape for the WebAuthn step 8 check.