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
ConstantTimeEqualtype. - 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—trueif and only ifactual.length === expected.lengthandequal(actual, expected)returns true. Returnsfalseon length mismatch without invokingequal.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.