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,54 @@
# aead
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Wraps `@suaveplan/crypto.aeadEncrypt` / `aeadDecrypt` (XChaCha20-Poly1305) into helpers that match the v1 chunk wire format, where the ciphertext and Poly1305 tag are stored in separate fields rather than as the noble-style concatenated `ciphertext || tag` buffer. Also provides the deterministic per-chunk nonce derivation used by the chunked packer.
## Features
- `aeadSeal(key, nonce, plaintext)` — encrypts and returns `{ ciphertext, aeadTag }` as detached `Uint8Array` instances.
- `aeadOpen(key, nonce, ciphertext, aeadTag)` — re-joins the two and decrypts; throws on tag mismatch.
- `deriveChunkNonce(baseNonce, chunkId)` — `baseNonce[0..4] XOR chunkId-as-u32-BE`, deterministic and unique per chunk.
- All inputs validated; failures wrapped as `PackerError(PACKER.ENCRYPTION_FAILED)`.
## Usage: Basic Example
```ts
import { aeadSeal, aeadOpen, deriveChunkNonce } from "@suaveplan/packer/chunked";
const fileKey = new Uint8Array(32); // caller-supplied
const baseNonce = crypto.getRandomValues(new Uint8Array(24));
const nonce = deriveChunkNonce(baseNonce, 0);
const { ciphertext, aeadTag } = aeadSeal(fileKey, nonce, plaintext);
const back = aeadOpen(fileKey, nonce, ciphertext, aeadTag);
```
## Usage: Advanced Example
```ts
import { aeadSeal, deriveChunkNonce } from "@suaveplan/packer/chunked";
// Seal each chunk with a derived nonce so the manifest only stores baseNonce.
function sealAll(plaintexts: Uint8Array[], fileKey: Uint8Array, baseNonce: Uint8Array) {
return plaintexts.map((p, i) => {
const nonce = deriveChunkNonce(baseNonce, i);
const { ciphertext, aeadTag } = aeadSeal(fileKey, nonce, p);
return { ciphertext, aeadTag, nonce };
});
}
```
## API Reference
- **`AeadSealResult`** — `{ ciphertext: Uint8Array; aeadTag: Uint8Array }`.
- **`aeadSeal(key, nonce, plaintext): AeadSealResult`** — `key` 32 B, `nonce` 24 B. Returns ciphertext (same length as plaintext) and 16-byte tag.
- **`aeadOpen(key, nonce, ciphertext, aeadTag): Uint8Array`** — returns plaintext. Throws `PackerError(PACKER.ENCRYPTION_FAILED)` on tag mismatch or invalid lengths.
- **`deriveChunkNonce(baseNonce, chunkId): Uint8Array`** — 24-byte nonce, deterministic per `(baseNonce, chunkId)` pair.
- **Constants:** `AEAD_KEY_LEN = 32`, `AEAD_NONCE_LEN = 24`, `AEAD_TAG_LEN = 16`, `FILE_ID_LEN = 32`.
## Implementation Notes
The seal helper detaches the returned `ciphertext` and `aeadTag` from the underlying combined buffer with `new Uint8Array(view)` so callers can mutate one without aliasing the other. Nonce derivation XORs only the first 32 bits to keep the rest of `baseNonce` intact — the manifest's `baseNonce` plus a `chunkId` is sufficient to reconstruct any chunk's nonce. The tag length is fixed at 16 (Poly1305); `AEAD_TAG_LEN` is exported for callers writing their own decoders.