-
-
Notifications
You must be signed in to change notification settings - Fork 1
csp
How to run leviathan-crypto under a strict Content-Security-Policy (CSP). The library compiles WebAssembly and, for the parallel pool, spawns Web Workers. Both are governed by CSP. This page lists exactly which directives the library requires, which it does not, and how the requirements differ across browser engines.
The library touches three CSP-relevant browser primitives, and nothing else.
WebAssembly compilation. Every module is compiled from bytes with WebAssembly.compile / compileStreaming and instantiated with WebAssembly.instantiate. Both steps require 'wasm-unsafe-eval' in script-src. There is no eval and no new Function anywhere in the library, so 'unsafe-eval' is never needed.
Web Workers. SealStreamPool spawns workers. The default cipher factories spawn a classic worker from a blob: URL, which requires worker-src blob: (and child-src blob: for engines that lack worker-src). You can override this to spawn a same-origin worker instead, which needs only worker-src 'self'.
Network fetch. Only the URL and Response WasmSource types fetch a .wasm file at load time, which requires connect-src for that origin. The embedded, ArrayBuffer, Uint8Array, and WebAssembly.Module sources make no network request.
Every claim on this page is backed by an end-to-end test that runs on Chromium, Firefox, and WebKit under a real CSP. See test/e2e/loader_csp.spec.ts, test/e2e/loader_csp_negative.spec.ts, and test/e2e/pool_csp.spec.ts.
| Directive | Required when | Why |
|---|---|---|
script-src 'wasm-unsafe-eval' |
Always | Compiling and instantiating any WASM module. May live on default-src instead. |
script-src 'self' (or a hash/nonce for your bundle) |
Always | Loading the library's own JavaScript modules. |
worker-src blob: |
SealStreamPool with the default factory |
Spawning the pool worker from a blob: URL (Chromium and Firefox). |
child-src blob: |
SealStreamPool with the default factory |
WebKit and older engines have no worker-src and fall back to child-src. |
worker-src 'self' |
SealStreamPool with the same-origin worker override |
Spawning the pool worker from a served URL instead of a blob:. Required for WebKit under a strict CSP. |
connect-src <wasm-origin> |
URL or Response WasmSource only |
Fetching the .wasm binary. Not needed for embedded or in-memory sources. |
Important
'wasm-unsafe-eval' is mandatory on every engine, not a copied-in habit. With the directive absent, the library cannot initialize. Chromium and Firefox reject the compile step; WebKit lets compilation through but rejects instantiation with "Refused to create a WebAssembly object". Either way init() throws. 'wasm-unsafe-eval' grants WebAssembly compilation without granting eval, so it is strictly narrower than 'unsafe-eval'. Use it.
The library never injects styles, loads images or fonts, uses eval, or reads from data: URLs. None of the following are required on its behalf.
-
'unsafe-eval'. The library compiles WASM, it does not runevalornew Function. -
'unsafe-inline'. No inline scripts and no inline styles. -
style-src,img-src,font-src. No stylesheets, images, or fonts.
If your application needs any of these, that is your application's requirement, not the library's. The worked example below separates the two.
Pick the smallest policy that matches how you load the library. Each is shown as an HTTP response header; the meta tag form follows.
Embedded WASM, no pool. The default and simplest setup. The gzip+base64 binary ships in the bundle, so there is no network fetch and no worker.
Content-Security-Policy: default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; object-src 'none'
Embedded WASM with SealStreamPool (Chromium and Firefox). Adds the blob: worker sources for the default pool factory.
Content-Security-Policy: default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; worker-src blob:; child-src blob:; object-src 'none'
Embedded WASM with SealStreamPool (all engines, including WebKit). Uses the same-origin worker override instead of a blob:. See Strict CSP without blob:.
Content-Security-Policy: default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self'; object-src 'none'
URL-loaded WASM, no pool. Adds connect-src for the origin serving the .wasm files. Replace 'self' with the binary's origin if it differs.
Content-Security-Policy: default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'self'; object-src 'none'
The same policy works as a <meta> tag for static hosting without header control.
<meta http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; object-src 'none'">Note
A <meta> CSP ignores frame-ancestors, sandbox, and report directives. Those only work as an HTTP header. The script and worker directives the library needs are all honored in a meta tag.
SealStreamPool compiles its WASM modules on the main thread and posts the compiled WebAssembly.Module objects to its workers, so the main thread always needs 'wasm-unsafe-eval' regardless of how the worker is spawned. The worker spawn itself is the part worker-src governs.
The default factory (createPoolWorker on SerpentCipher, XChaCha20Cipher, and the AES suite) spawns a classic worker from a blob: URL. The behavior under a restrictive CSP splits by engine, verified across all three in test/e2e/pool_csp.spec.ts.
-
Chromium and Firefox.
worker-src blob:admits the worker and the pool round-trips. The blob worker inherits the document CSP, so the'wasm-unsafe-eval'grant covers the worker's own instantiation step too. -
WebKit and Safari. The
blob:worker resource is refused under a restrictive CSP even withworker-src blob:andchild-src blob:present, though it works with no CSP at all. The pool cannot start. WebKit must use the same-origin worker override.
Caution
If you ship SealStreamPool to Safari users under a CSP, the default blob: factory will fail there. Use the same-origin worker override below. It works on all three engines, so it is the portable choice for any pool deployment behind a strict CSP.
For environments that forbid blob: entirely, or that must support WebKit under CSP, override createPoolWorker to spawn a same-origin worker. The override is a plain spread over the cipher suite; do not change formatEnum, hkdfInfo, or any size field, or you break wire interop.
import { XChaCha20Cipher } from 'leviathan-crypto'
import type { CipherSuite } from 'leviathan-crypto'
// Serve the shipped worker from your own origin, or bundle your own.
const CspXChaCha20Cipher: CipherSuite = {
...XChaCha20Cipher,
createPoolWorker: () => new Worker(
new URL('./xchacha20-pool-worker.js', import.meta.url),
{ type: 'module' },
),
}
const pool = await SealStreamPool.create(CspXChaCha20Cipher, key, { wasm, workers: 2 })This needs worker-src 'self' and no blob:.
Content-Security-Policy: default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self'; object-src 'none'
Note
A same-origin worker takes its CSP from its own HTTP response, not from the document. The main thread still needs 'wasm-unsafe-eval' to compile the modules it posts to the worker. See ciphersuite.md for the full override pattern and the hybrid-suite case.
A real consumer application ships a policy in this shape. Most of it is the application's own surface; only two directives exist for the library. This version uses worker-src 'self' with the same-origin worker override, so the pool works on Chromium, Firefox, and WebKit alike. The table separates the library directives from the application's.
default-src 'none';
script-src 'wasm-unsafe-eval' 'sha256-KS5USFs9rlShBDRR+9rICpSFOiWcg1vcLwvfjcbvwxk=';
style-src 'unsafe-inline';
connect-src 'self' wss: ws://localhost:* ws://127.0.0.1:*;
worker-src 'self';
img-src 'self' data: blob:;
font-src 'none';
base-uri 'none';
object-src 'none';
form-action 'none';
frame-ancestors 'none'
| Directive | Owner | Note |
|---|---|---|
script-src 'wasm-unsafe-eval' |
Library | WASM compilation. Mandatory. |
script-src 'sha256-...' |
Application | Hash of the app's own bundled script. The library loads inside that bundle, so no separate 'self' is needed here. Apps that load the library as a separate module need 'self' or its own hash. |
worker-src 'self' |
Library |
SealStreamPool with the same-origin worker override. Works on all three engines, no blob: needed. |
style-src 'unsafe-inline' |
Application | The library injects no styles. |
connect-src 'self' wss: ws://... |
Application | WebSockets. The library needs connect-src only for URL/Response WasmSources. |
img-src 'self' data: blob: |
Application | The library loads no images. |
default-src, font-src, base-uri, object-src, form-action, frame-ancestors
|
Application | General hardening, unrelated to the library. |
| Document | Description |
|---|---|
| index | Project Documentation index |
| init | the public init() API and WasmSource types |
| loader | WASM binary loading strategies and dispatch |
| cdn | loading from a CDN with no bundler |
| ciphersuite |
CipherSuite interface and the createPoolWorker override |
| examples | code examples for every primitive |
- Sign Tools
-
SignatureSuite
- format-byte catalog, hybrid composite encodings, custom suite contract
- Serpent-256 TypeScript | WASM
-
Serpent,SerpentCtr,SerpentCbc,SerpentGenerator
-
- ChaCha20 TypeScript | WASM
-
ChaCha20,Poly1305,ChaCha20Poly1305,XChaCha20Poly1305,ChaCha20Generator
-
- AES TypeScript | WASM
-
AES,AESCbc,AESCtr,AESGCM,AESGCMSIV,AESGenerator
-
- ML-DSA TypeScript | WASM
- pure (FIPS 204):
MlDsa44,MlDsa65,MlDsa87 - pure-mode suites:
MlDsa44Suite,MlDsa65Suite,MlDsa87Suite - prehash suites:
MlDsa44PreHashSuite,MlDsa65PreHashSuite,MlDsa87PreHashSuite
- pure (FIPS 204):
- SLH-DSA TypeScript | WASM
- pure (FIPS 205):
SlhDsa128f,SlhDsa192f,SlhDsa256f - pure-mode suites:
SlhDsa128fSuite,SlhDsa192fSuite,SlhDsa256fSuite - prehash suites:
SlhDsa128fPreHashSuite,SlhDsa192fPreHashSuite,SlhDsa256fPreHashSuite
- pure (FIPS 205):
- Ed25519 TypeScript | WASM
-
Ed25519(pure + Ed25519ph),Ed25519Suite,Ed25519PreHashSuite
-
- ECDSA-P256 TypeScript | WASM
-
EcdsaP256(hedged + RFC 6979),EcdsaP256Suite - DER codec:
ecdsaSignatureToDer,ecdsaSignatureFromDer,encodeEcPrivateKey,decodeEcPrivateKey,pointDecompress
-
- Hybrid composites PQ-only | Classical+PQ
- PQ-only:
MlDsa44SlhDsa128fSuite,MlDsa65SlhDsa192fSuite,MlDsa87SlhDsa256fSuite - Classical+PQ:
MlDsa44Ed25519Suite,MlDsa65Ed25519Suite,MlDsa44EcdsaP256Suite,MlDsa65EcdsaP256Suite
- PQ-only:
- X25519 TypeScript | WASM
-
X25519,KeyAgreementError(RFC 7748)
-
- ML-KEM TypeScript | WASM
-
MlKem512,MlKem768,MlKem1024
-
-
Ratchet (SPQR)
-
KDFChain,ratchetInit,kemRatchetEncap,kemRatchetDecap,RatchetKeypair,SkippedKeyStore
-
- Hashing overview
- SHA-2 TypeScript | WASM
-
SHA256,SHA384,SHA512,SHA224,SHA512_224,SHA512_256 -
HMAC_SHA256,HMAC_SHA384,HMAC_SHA512,HKDF_SHA256,HKDF_SHA512
-
- SHA-3 TypeScript | WASM
-
SHA3_224,SHA3_256,SHA3_384,SHA3_512,SHAKE128,SHAKE256
-
- BLAKE3 TypeScript | WASM
-
BLAKE3,BLAKE3Stream,BLAKE3KeyedHash,BLAKE3KeyedHashStream -
BLAKE3DeriveKey,BLAKE3DeriveKeyStream,BLAKE3OutputReader,BLAKE3Hash
-
-
KMAC
-
CSHAKE128,CSHAKE256,KMAC128,KMAC256,KMACXOF128,KMACXOF256
-
-
Merkle
-
MerkleVerifier,MerkleLog -
SignedLog,Sha256Tree,Blake3Tree,MemoryStorage
-
-
Fortuna CSPRNG
-
Fortuna,SerpentGenerator,ChaCha20Generator,AESGenerator,SHA256Hash,SHA3_256Hash,BLAKE3Hash
-
- Utils TypeScript | WASM
-
constantTimeEqual,randomBytes,wipe, encoding helpers
-
-
TypeScript interfaces
-
Hash,KeyedHash,Blockcipher,Streamcipher,AEAD,Generator,HashFn
-