Initial Commit
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# chunk-verifier
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Verifies the structural validity and ed25519 server signature of a v1 chunk **without** the file decryption key. Used by mesh peers that forward chunks they cannot themselves read, by drive's chunks-plugin during the receive-side gate (sig-verify before persistence), and by any consumer that wants to reject forged chunks before engaging the AEAD path.
|
||||
|
||||
## Features
|
||||
|
||||
- `ChunkVerifier(serverPubKey)` — single-server-key instance; reusable across many chunks.
|
||||
- `verify(chunkBytes): VerifyResult` — returns `{ ok: true, metadata }` or `{ ok: false, code, message }`. Never throws on verifiable-but-bad input; only throws on programmer error (wrong-length pubkey).
|
||||
- Performs the full PACKER-CHUNK-* invariant chain (version, flags, payloadLen) before signature check.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { ChunkVerifier } from "@suaveplan/packer/chunked";
|
||||
|
||||
const verifier = new ChunkVerifier(serverPubKey);
|
||||
const result = verifier.verify(receivedChunkBytes);
|
||||
if (!result.ok) {
|
||||
console.warn("rejected chunk:", result.code, result.message);
|
||||
} else {
|
||||
persistChunk(receivedChunkBytes, result.metadata);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { ChunkVerifier, PACKER_CODES } from "@suaveplan/packer/chunked";
|
||||
|
||||
const verifier = new ChunkVerifier(serverPubKey);
|
||||
async function gate(stream: AsyncIterable<Uint8Array>) {
|
||||
let valid = 0;
|
||||
let badSig = 0;
|
||||
let badFormat = 0;
|
||||
for await (const raw of stream) {
|
||||
const r = verifier.verify(raw);
|
||||
if (r.ok) { valid += 1; continue; }
|
||||
if (r.code === PACKER_CODES.VERIFICATION_FAILED) badSig += 1;
|
||||
else badFormat += 1;
|
||||
}
|
||||
return { valid, badSig, badFormat };
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- **`interface VerifyOk { ok: true; metadata: ChunkV1Metadata }`**
|
||||
- **`interface VerifyFail { ok: false; code: PackerErrorCode; message: string }`**
|
||||
- **`type VerifyResult = VerifyOk | VerifyFail`**
|
||||
- **`class ChunkVerifier`**
|
||||
- `constructor(serverPubKey: Uint8Array)` — throws `PackerError(PACKER.INVALID_FORMAT)` if `serverPubKey.byteLength !== 32`.
|
||||
- `verify(chunkBytes: Uint8Array): VerifyResult` — never throws on bad chunks; rejected chunks come back as `VerifyFail`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`verify` first runs `parseChunk`, which surfaces structural failures (`INVALID_FORMAT`, `VERSION_UNSUPPORTED`, `FLAGS_RESERVED_NONZERO`, `PAYLOAD_LEN_INVALID`) as `PackerError` — these are caught and folded into `VerifyFail`. Only after the structural gate is the ed25519 signature checked against the canonical signing payload `metadata[0..100] ‖ ciphertext[0..payloadLen]`. Cost: one `parseChunk` (zero-copy), one 100-byte `encodeHeader` allocation, one signing-payload concatenation (allocates `100 + payloadLen` bytes), one `ed25519.verify`.
|
||||
Reference in New Issue
Block a user