first commit
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
# encrypt-stream
|
||||
|
||||
## Purpose
|
||||
|
||||
The encryption pipeline that transforms a plaintext file into a stream of signed encrypted chunks ready for persistence. It owns the chunk-level nonce derivation, the AEAD ciphertext + tag split, the per-chunk metadata population, and the immediate signing pass. Together these steps form the deterministic transform from `{ plaintext, fileId, fileKey, baseNonce, chunkSize, serverPrivKey }` to a stream of `EncodedChunk` objects.
|
||||
|
||||
## Features
|
||||
|
||||
- `encryptChunks(options): Generator<EncodedChunk>` — yields one chunk per `chunkSize - HEADER_SIZE` payload window, signed and ready to persist.
|
||||
- `deriveNonce(baseNonce, chunkId): Uint8Array` — XORs the chunk index into the high-order bytes of the 24-byte XChaCha20 nonce; rejects non-24-byte inputs with `CHUNKS.METADATA_INVALID`.
|
||||
- `TAG_LENGTH` constant (16) for the AEAD tag size.
|
||||
- Synchronous generator — no async overhead, allocations bounded per chunk.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { randomPrivateKey } from "@suaveplan/crypto-curve/ed25519";
|
||||
import { encryptChunks } from "@suaveplan/drive-plugin-chunks";
|
||||
|
||||
const chunks = Array.from(
|
||||
encryptChunks({
|
||||
plaintext: new TextEncoder().encode("hello"),
|
||||
fileId: new Uint8Array(32),
|
||||
fileKey: new Uint8Array(32).fill(7),
|
||||
baseNonce: new Uint8Array(24).fill(11),
|
||||
chunkSize: 1024,
|
||||
serverPrivKey: randomPrivateKey(),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { deriveNonce, encryptChunks, TAG_LENGTH } from "@suaveplan/drive-plugin-chunks";
|
||||
|
||||
for (const chunk of encryptChunks({ plaintext, fileId, fileKey, baseNonce, chunkSize: 4096, serverPrivKey })) {
|
||||
await storage.set(`chunks:${idHex}:${chunk.metadata.chunkId}`, chunk.rawBytes);
|
||||
}
|
||||
|
||||
// Reconstruct the per-chunk nonce on the receiver
|
||||
const nonce = deriveNonce(baseNonce, chunkIdReceived);
|
||||
console.log(TAG_LENGTH); // 16
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `encryptChunks(options: EncryptOptions): Generator<EncodedChunk>` — `EncryptOptions` carries `plaintext`, `fileId`, `fileKey`, `baseNonce` (24 bytes), `chunkSize`, `serverPrivKey`. Yields `{ rawBytes, metadata }` per chunk.
|
||||
- `deriveNonce(baseNonce: Uint8Array, chunkId: number): Uint8Array` — XORs `chunkId` into bytes 0–3.
|
||||
- `TAG_LENGTH: 16` — AEAD tag size.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`encryptChunks` writes the signature into the raw buffer at `[METADATA_SIZE, HEADER_SIZE)` immediately after computing it, so consumers can stream chunks through transports without ever holding a full file plaintext in memory. The XOR-based nonce derivation is sound for chunk indices ≤ 2³² because XChaCha20-Poly1305 takes a 24-byte nonce: under fixed `baseNonce` no two chunks of the same file ever share a nonce.
|
||||
Reference in New Issue
Block a user