import type { FilesPlugin } from "../index.js"; /** * A master key (KEK) for {@link encryption}. Either a Web Crypto * {@link CryptoKey} usable for `encrypt`/`decrypt`, or raw AES key bytes of 16, * 24, or 32 bytes (e.g. a secret pulled from an environment variable, imported * for you on first use). */ export type EncryptionKey = CryptoKey | Uint8Array | ArrayBuffer; /** * Generate a fresh 256-bit AES-GCM master key for {@link encryption}. The key * is extractable, so you can `crypto.subtle.exportKey("raw", key)` it to persist * in a secret manager and re-import it (or pass the raw bytes back to * {@link encryption}) later. */ export declare const generateEncryptionKey: () => Promise; /** * Envelope-encrypt object bodies at rest. On `upload` a fresh per-object data * key (DEK) encrypts the body with AES-256-GCM; the master key (KEK) you pass in * encrypts ("wraps") that DEK, and both the wrapped DEK and the IVs ride along in * the object's `metadata`. On `download` the DEK is unwrapped and the body * decrypted — transparently, in the right order, for `upload([...])` / * `download([...])` bulk calls too. * * Provider-agnostic: it uses only the Web Crypto API (no native deps) and the * `metadata` the SDK already round-trips, so it works on any adapter that * supports metadata. * * Place it **last** in the plugin array so it's the innermost layer — anything * that needs to see plaintext (compression, validation, virus scanning) must run * before it: `plugins: [compression(), encryption(key)]`. The exceptions are * `failover()` and `tiering()`: their secondary / cold backends are driven * outside the rest of the onion, so they go **after** it * (`[encryption(key), tiering(...)]`) — placed before it, whatever they route * to another backend would be stored unencrypted. * * Trade-offs, by design: * - **Buffers the whole body** to compute the GCM tag, so it's unsuitable for * unknown-length streams. * - **Resumable uploads (`control`) throw** — every upload seals the body * under a fresh random data key, so a session resumed in another process * would splice two different ciphertexts into an object that can't be * decrypted. `files.capabilities` reports `resumable` as `false` to match; * `multipart: true` still splits a large body within one call. * - **Range downloads throw** — a slice of a GCM ciphertext can't be decrypted. * - **`url()` / `signedUploadUrl()` throw** — presigned URLs bypass the plugin, * handing out ciphertext or letting a client store plaintext. * `files.capabilities` reports `signedUrl.supported`, * `signedUpload.supported`, and `rangeRead` as `false` to match, so the * `files-sdk/api` gateway proxies uploads and downloads through the * instance. * - **`head` / `list` never decrypt** — they return metadata only, reporting * the plaintext size with the internal fields hidden; read the plaintext * with `download()`. On adapters whose `list()` returns no metadata (S3 and * the S3-compatibles), `list()` items report the stored (ciphertext) size. * - **`copy` / `move` just work** — the wrapped DEK travels with the object. * - Objects without this plugin's marker (pre-existing or written elsewhere) * **pass through** on read, so it's safe to enable on a mixed bucket. * * Threat model: this protects **confidentiality at rest** — a party who reads * raw provider bytes (or metadata) learns nothing about the plaintext beyond * its length. Only the body is encrypted: the object's key, `contentType`, * and your own `metadata` are stored as-is. Any tampering with the * ciphertext, the wrapped DEK, the IVs, or the declared size fails loudly at * decrypt time. It is **not** integrity binding between an envelope and its * key: an attacker with raw provider *write* access can copy one object's * whole envelope (ciphertext + `fsenc_*` metadata) onto another key, and a * later download of that key will decrypt cleanly to the other object's * plaintext. Binding envelopes to keys * (à la KMS encryption context) would break the server-side `copy`/`move` * and key-aliasing plugin compositions above by design — if cross-object * splicing is in your threat model, isolate tenants with separate KEKs and * lock down provider write access. * * @param key the master key — a {@link CryptoKey} or raw 16/24/32 key bytes. * @example * ```ts * import { createFiles } from "files-sdk"; * import { s3 } from "files-sdk/s3"; * import { encryption, generateEncryptionKey } from "files-sdk/encryption"; * * const files = createFiles({ * adapter: s3({ bucket: "uploads" }), * plugins: [encryption(await generateEncryptionKey())], * }); * * await files.upload("secret.txt", "hello"); // stored encrypted * await (await files.download("secret.txt")).text(); // "hello" * ``` */ export declare const encryption: (key: EncryptionKey) => FilesPlugin; //# sourceMappingURL=index.d.ts.map