first commit
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# primitives/crypto-auth
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`cryptoAuth` and `cryptoAuthVerify` expose libsodium's `crypto_auth` and `crypto_auth_verify`, which implement a BLAKE2b-based message authentication code (MAC). Unlike HMAC-SHA256, this MAC is built on BLAKE2b — a fast, parallel, modern hash function with a wider security margin than HMAC-MD5 or HMAC-SHA1, and comparable to HMAC-SHA256/512.
|
||||
|
||||
MACs are used to verify that a message was produced by a party holding the shared key and has not been tampered with in transit. They differ from signatures in that both parties must hold the same secret key; there is no separate verification key.
|
||||
|
||||
## Features
|
||||
|
||||
- BLAKE2b-based MAC via `crypto_auth` — 32-byte output, 32-byte key.
|
||||
- Constant-time verification via `crypto_auth_verify` — resistant to timing-based MAC forgery.
|
||||
- `cryptoAuthVerify` returns `false` (never throws) for invalid MACs.
|
||||
- Async — calls `sodiumReady()` internally.
|
||||
- Deterministic — same key + message always produces the same MAC.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { cryptoAuth, cryptoAuthVerify } from "@suaveplan/crypto-sodium/primitives/auth";
|
||||
|
||||
const key = crypto.getRandomValues(new Uint8Array(32));
|
||||
const message = new TextEncoder().encode("verify this");
|
||||
|
||||
const mac = await cryptoAuth(key, message);
|
||||
const valid = await cryptoAuthVerify(key, message, mac);
|
||||
console.log(valid); // true
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
Authenticating API request payloads with a shared secret:
|
||||
|
||||
```ts
|
||||
import { cryptoAuth, cryptoAuthVerify } from "@suaveplan/crypto-sodium/primitives/auth";
|
||||
|
||||
const sharedKey = crypto.getRandomValues(new Uint8Array(32));
|
||||
|
||||
async function signPayload(payload: Uint8Array): Promise<{ payload: Uint8Array; mac: Uint8Array }> {
|
||||
const mac = await cryptoAuth(sharedKey, payload);
|
||||
return { payload, mac };
|
||||
}
|
||||
|
||||
async function verifyPayload(payload: Uint8Array, mac: Uint8Array): Promise<boolean> {
|
||||
return cryptoAuthVerify(sharedKey, payload, mac);
|
||||
}
|
||||
|
||||
const data = new TextEncoder().encode('{"action":"transfer","amount":100}');
|
||||
const signed = await signPayload(data);
|
||||
const ok = await verifyPayload(signed.payload, signed.mac);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `cryptoAuth(key: Uint8Array, message: Uint8Array): Promise<Uint8Array>`
|
||||
|
||||
Computes a 32-byte BLAKE2b MAC over `message` with `key`.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `key` — 32-byte authentication key.
|
||||
- `message` — Arbitrary-length message bytes.
|
||||
|
||||
**Returns:** `Promise<Uint8Array>` — 32-byte MAC.
|
||||
|
||||
### `cryptoAuthVerify(key: Uint8Array, message: Uint8Array, mac: Uint8Array): Promise<boolean>`
|
||||
|
||||
Verifies a MAC in constant time.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `key` — 32-byte authentication key.
|
||||
- `message` — The message that was authenticated.
|
||||
- `mac` — 32-byte MAC to verify.
|
||||
|
||||
**Returns:** `Promise<boolean>` — `true` if valid, `false` otherwise. Never throws.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`crypto_auth_verify` from libsodium uses constant-time comparison to prevent timing side channels that would otherwise allow an attacker to iteratively learn the correct MAC byte by byte. The `try/catch` in `cryptoAuthVerify` catches structurally malformed inputs (e.g., wrong-length MAC) and maps them to `false` for consistency.
|
||||
|
||||
Both functions are async solely because they call `sodiumReady()` — the underlying sodium operations are synchronous once the WASM is initialised.
|
||||
Reference in New Issue
Block a user