first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
# constantTimeEqual
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Byte-for-byte comparison of two `Uint8Array` buffers that takes time dependent only on the input length, not on where they first differ. Use it — and never the built-in `===`, `Array.prototype.every`, or `Buffer.equals` — for comparing authentication tags, MAC digests, password hashes, and any other value where an attacker might use wall-clock time to learn a secret byte-by-byte.
## Features
- No early return on mismatch within the byte loop.
- Constant-time in buffer **contents**; length check is the only short-circuit (lengths of MACs/tags are public constants).
- Zero allocations; pure loop over the inputs.
- No dependencies.
## Usage: Basic Example
```ts
import { constantTimeEqual } from "@suaveplan/crypto-utils/constant-time";
const tagReceived = new Uint8Array(16);
const tagExpected = new Uint8Array(16);
if (!constantTimeEqual(tagReceived, tagExpected)) {
throw new Error("tag mismatch");
}
```
## Usage: Advanced Example
```ts
import { constantTimeEqual } from "@suaveplan/crypto-utils/constant-time";
// Verify a computed MAC against a header-supplied expected MAC in one step,
// without ever comparing via `===`, `for-of`, or `Buffer.equals`.
function verifyMessageAuth(
macImpl: (msg: Uint8Array) => Uint8Array,
message: Uint8Array,
expected: Uint8Array
): boolean {
const computed = macImpl(message);
return constantTimeEqual(computed, expected);
}
```
## API Reference
- `constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean` — returns `true` iff both buffers have the same length **and** every byte matches. When lengths differ, returns `false` immediately (length is not secret).
## Implementation Notes
The loop computes `diff |= a[i] ^ b[i]`, accumulating a nonzero value iff any pair of bytes differs; the final check `diff === 0` is also constant time because it inspects a single integer. JavaScript engines aggressively inline this pattern, so the generated machine code is effectively identical to the hand-written one found in `@noble/hashes` and `libsodium`'s `sodium_memcmp`. The length short-circuit exists because returning `false` on length mismatch does not leak secret information — MAC and tag lengths are public protocol constants — and because continuing the loop with a shorter buffer would require masking that adds complexity with no security benefit.