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,147 @@
# @suaveplan/jws - RFC 7515 JSON Web Signature (verify + sign). Compact, JSON-flattened, and JSON-general (multi-signature) serializations; HS/RS/PS/ES/EdDSA algorithms; JWK + x5c + kid key resolution; crit allowlist enforcement; detached payload (§A.5); RFC 7797 b64 header support.
## Summary
RFC 7515 **JSON Web Signature** (verify + sign). Pure TypeScript over `Uint8Array` + `DataView` plus the workspace crypto primitives (`@suaveplan/crypto-rsa`, `@suaveplan/crypto-curve`, `@suaveplan/crypto-hash`, `@suaveplan/crypto-utils`) and `@suaveplan/x509` for `x5c` chain validation. Zero browser globals, zero Node built-ins, zero external JOSE/JWT/jws-npm dependencies.
<!-- begin:auto-docs -->
## Documentation tree
- [builtin](./src/algorithms/builtin.md) — Seeds the default `AlgorithmRegistry` with the 13 built-in algorithm implementations required by RFC 7518 §3 and RFC 80…
- [ecdsa](./src/algorithms/ecdsa.md) — RFC 7518 §3.4 ECDSA signing and verification over NIST P-256, P-384, and P-521. The signing input is hashed with the cu…
- [eddsa](./src/algorithms/eddsa.md) — RFC 8037 §3.1 EdDSA over Ed25519. The signing input is signed directly by Ed25519 (no caller-side pre-hash — Ed25519 ha…
- [hmac](./src/algorithms/hmac.md) — RFC 7518 §3.2 HMAC-with-SHA-2 signing and verification. The signing input is HMAC'd with the caller-supplied symmetric…
- [none](./src/algorithms/none.md) — RFC 7518 §3.6 `"none"` algorithm. The `none` value declares "no integrity protection," which is virtually never what a…
- [registry](./src/algorithms/registry.md) — In-package mapping from `alg` identifier to the concrete `AlgorithmImpl` that knows how to verify and (optionally) sign…
- [rsa-pss](./src/algorithms/rsa-pss.md) — RFC 7518 §3.5 RSASSA-PSS signing and verification. The signing input is hashed, EMSA-PSS-encoded with a random salt, an…
- [rsa](./src/algorithms/rsa.md) — RFC 7518 §3.3 RSASSA-PKCS1-v1_5 signing and verification. The signing input is hashed with SHA-256/SHA-384/SHA-512 resp…
- [base64url](./src/base64url/base64url.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [base64url](./src/base64url/base64url.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [detached](./src/detached/detached.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [detached](./src/detached/detached.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [errors](./src/errors/error-codes.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [error-codes](./src/errors/error-codes.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [header](./src/header/header.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [b64](./src/header/b64.md) — RFC 7797 §3 implements the `b64` header parameter, allowing JWS producers to embed unencoded payloads directly into the…
- [crit](./src/header/crit.md) — RFC 7515 §4.1.11 enforcement: every entry in `header.crit` MUST be in the caller's `acceptableCriticalHeaders` allowlis…
- [header](./src/header/header.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [jwk](./src/keys/jwk.md) — Parse an RFC 7517 §4 / RFC 7518 §6 JSON Web Key into the typed key shape expected by the JWS algorithm impl for the dec…
- [resolver](./src/keys/resolver.md) — Caller-supplied key-resolution function consulted when `header.kid` is present and no key has been supplied via `opts.k…
- [x5c](./src/keys/x5c.md) — Decode an RFC 7515 §4.1.6 `x5c` certificate chain, delegate trust-path validation to `@suaveplan/x509.validateChain`, a…
- [observability](./src/observability/metrics.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [metrics](./src/observability/metrics.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [schemas](./src/schemas/schemas.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [schemas](./src/schemas/schemas.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [compact](./src/serialization/compact/compact.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [compact](./src/serialization/compact/compact.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [flat](./src/serialization/flat/flat.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [flat](./src/serialization/flat/flat.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [general](./src/serialization/general/general.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [general](./src/serialization/general/general.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [sign](./src/sign/sign-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [sign-pipeline](./src/sign/sign-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [verify](./src/verify/verify-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [verify-pipeline](./src/verify/verify-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
<!-- end:auto-docs --> - [base64url](./src/base64url/base64url.md) — <!-- begin:auto-docs --> _(no submodules)_- [detached](./src/detached/detached.md) — <!-- begin:auto-docs --> _(no submodules)_- [detached](./src/detached/detached.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/error-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/errors/error-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [header](./src/header/header.md) — <!-- begin:auto-docs --> _(no submodules)_- [b64](./src/header/b64.md) — RFC 7797 §3 implements the `b64` header parameter, allowing JWS producers to embed unencoded payloads directly into the…
- [crit](./src/header/crit.md) — RFC 7515 §4.1.11 enforcement: every entry in `header.crit` MUST be in the caller's `acceptableCriticalHeaders` allowlis…
- [header](./src/header/header.md) — <!-- begin:auto-docs --> _(no submodules)_- [jwk](./src/keys/jwk.md) — Parse an RFC 7517 §4 / RFC 7518 §6 JSON Web Key into the typed key shape expected by the JWS algorithm impl for the dec…
- [resolver](./src/keys/resolver.md) — Caller-supplied key-resolution function consulted when `header.kid` is present and no key has been supplied via `opts.k…
- [x5c](./src/keys/x5c.md) — Decode an RFC 7515 §4.1.6 `x5c` certificate chain, delegate trust-path validation to `@suaveplan/x509.validateChain`, a…
- [observability](./src/observability/metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [metrics](./src/observability/metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [schemas](./src/schemas/schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [schemas](./src/schemas/schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [compact](./src/serialization/compact/compact.md) — <!-- begin:auto-docs --> _(no submodules)_- [compact](./src/serialization/compact/compact.md) — <!-- begin:auto-docs --> _(no submodules)_- [flat](./src/serialization/flat/flat.md) — <!-- begin:auto-docs --> _(no submodules)_- [flat](./src/serialization/flat/flat.md) — <!-- begin:auto-docs --> _(no submodules)_- [general](./src/serialization/general/general.md) — <!-- begin:auto-docs --> _(no submodules)_- [general](./src/serialization/general/general.md) — <!-- begin:auto-docs --> _(no submodules)_- [sign](./src/sign/sign-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [sign-pipeline](./src/sign/sign-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [verify](./src/verify/verify-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [verify-pipeline](./src/verify/verify-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [base64url](./src/base64url/base64url.md) — <!-- begin:auto-docs --> _(no submodules)_- [detached](./src/detached/detached.md) — <!-- begin:auto-docs --> _(no submodules)_- [detached](./src/detached/detached.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/error-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/errors/error-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [header](./src/header/header.md) — <!-- begin:auto-docs --> _(no submodules)_- [b64](./src/header/b64.md) — RFC 7797 §3 implements the `b64` header parameter, allowing JWS producers to embed unencoded payloads directly into the…
- [crit](./src/header/crit.md) — RFC 7515 §4.1.11 enforcement: every entry in `header.crit` MUST be in the caller's `acceptableCriticalHeaders` allowlis…
- [header](./src/header/header.md) — <!-- begin:auto-docs --> _(no submodules)_- [jwk](./src/keys/jwk.md) — Parse an RFC 7517 §4 / RFC 7518 §6 JSON Web Key into the typed key shape expected by the JWS algorithm impl for the dec…
- [resolver](./src/keys/resolver.md) — Caller-supplied key-resolution function consulted when `header.kid` is present and no key has been supplied via `opts.k…
- [x5c](./src/keys/x5c.md) — Decode an RFC 7515 §4.1.6 `x5c` certificate chain, delegate trust-path validation to `@suaveplan/x509.validateChain`, a…
- [observability](./src/observability/metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [metrics](./src/observability/metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [schemas](./src/schemas/schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [schemas](./src/schemas/schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [compact](./src/serialization/compact/compact.md) — <!-- begin:auto-docs --> _(no submodules)_- [compact](./src/serialization/compact/compact.md) — <!-- begin:auto-docs --> _(no submodules)_- [flat](./src/serialization/flat/flat.md) — <!-- begin:auto-docs --> _(no submodules)_- [flat](./src/serialization/flat/flat.md) — <!-- begin:auto-docs --> _(no submodules)_- [general](./src/serialization/general/general.md) — <!-- begin:auto-docs --> _(no submodules)_- [general](./src/serialization/general/general.md) — <!-- begin:auto-docs --> _(no submodules)_- [sign](./src/sign/sign-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [sign-pipeline](./src/sign/sign-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [verify](./src/verify/verify-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [verify-pipeline](./src/verify/verify-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [base64url](./src/base64url/base64url.md) — <!-- begin:auto-docs --> _(no submodules)_- [detached](./src/detached/detached.md) — <!-- begin:auto-docs --> _(no submodules)_- [detached](./src/detached/detached.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/error-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/errors/error-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [header](./src/header/header.md) — <!-- begin:auto-docs --> _(no submodules)_- [b64](./src/header/b64.md) — RFC 7797 §3 implements the `b64` header parameter, allowing JWS producers to embed unencoded payloads directly into the…
- [crit](./src/header/crit.md) — RFC 7515 §4.1.11 enforcement: every entry in `header.crit` MUST be in the caller's `acceptableCriticalHeaders` allowlis…
- [header](./src/header/header.md) — <!-- begin:auto-docs --> _(no submodules)_- [jwk](./src/keys/jwk.md) — Parse an RFC 7517 §4 / RFC 7518 §6 JSON Web Key into the typed key shape expected by the JWS algorithm impl for the dec…
- [resolver](./src/keys/resolver.md) — Caller-supplied key-resolution function consulted when `header.kid` is present and no key has been supplied via `opts.k…
- [x5c](./src/keys/x5c.md) — Decode an RFC 7515 §4.1.6 `x5c` certificate chain, delegate trust-path validation to `@suaveplan/x509.validateChain`, a…
- [observability](./src/observability/metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [metrics](./src/observability/metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [schemas](./src/schemas/schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [schemas](./src/schemas/schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [compact](./src/serialization/compact/compact.md) — <!-- begin:auto-docs --> _(no submodules)_- [compact](./src/serialization/compact/compact.md) — <!-- begin:auto-docs --> _(no submodules)_- [flat](./src/serialization/flat/flat.md) — <!-- begin:auto-docs --> _(no submodules)_- [flat](./src/serialization/flat/flat.md) — <!-- begin:auto-docs --> _(no submodules)_- [general](./src/serialization/general/general.md) — <!-- begin:auto-docs --> _(no submodules)_- [general](./src/serialization/general/general.md) — <!-- begin:auto-docs --> _(no submodules)_- [sign](./src/sign/sign-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [sign-pipeline](./src/sign/sign-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [verify](./src/verify/verify-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [verify-pipeline](./src/verify/verify-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [base64url](./src/base64url/base64url.md) — <!-- begin:auto-docs --> _(no submodules)_- [detached](./src/detached/detached.md) — <!-- begin:auto-docs --> _(no submodules)_ - [detached](./src/detached/detached.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/error-codes.md) — <!-- begin:auto-docs --> _(no submodules)_ - [error-codes](./src/errors/error-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [header](./src/header/header.md) — <!-- begin:auto-docs --> _(no submodules)_ - [b64](./src/header/b64.md) — RFC 7797 §3 implements the `b64` header parameter, allowing JWS producers to embed unencoded payloads directly into the…
- [crit](./src/header/crit.md) — RFC 7515 §4.1.11 enforcement: every entry in `header.crit` MUST be in the caller's `acceptableCriticalHeaders` allowlis…
- [header](./src/header/header.md) — <!-- begin:auto-docs --> _(no submodules)_- [jwk](./src/keys/jwk.md) — Parse an RFC 7517 §4 / RFC 7518 §6 JSON Web Key into the typed key shape expected by the JWS algorithm impl for the dec…
- [resolver](./src/keys/resolver.md) — Caller-supplied key-resolution function consulted when `header.kid` is present and no key has been supplied via `opts.k…
- [x5c](./src/keys/x5c.md) — Decode an RFC 7515 §4.1.6 `x5c` certificate chain, delegate trust-path validation to `@suaveplan/x509.validateChain`, a…
- [observability](./src/observability/metrics.md) — <!-- begin:auto-docs --> _(no submodules)_ - [metrics](./src/observability/metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [schemas](./src/schemas/schemas.md) — <!-- begin:auto-docs --> _(no submodules)_ - [schemas](./src/schemas/schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [compact](./src/serialization/compact/compact.md) — <!-- begin:auto-docs --> _(no submodules)_ - [compact](./src/serialization/compact/compact.md) — <!-- begin:auto-docs --> _(no submodules)_- [flat](./src/serialization/flat/flat.md) — <!-- begin:auto-docs --> _(no submodules)_ - [flat](./src/serialization/flat/flat.md) — <!-- begin:auto-docs --> _(no submodules)_- [general](./src/serialization/general/general.md) — <!-- begin:auto-docs --> _(no submodules)_ - [general](./src/serialization/general/general.md) — <!-- begin:auto-docs --> _(no submodules)_- [sign](./src/sign/sign-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_ - [sign-pipeline](./src/sign/sign-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_- [verify](./src/verify/verify-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_ - [verify-pipeline](./src/verify/verify-pipeline.md) — <!-- begin:auto-docs --> _(no submodules)_
## Install
```sh
bun add @suaveplan/jws
```
## Quick start
```ts
import { compactSign, compactVerify } from "@suaveplan/jws";
const secret = new TextEncoder().encode("a-shared-32-byte-secret-pls-rotate");
const token = await compactSign("hello", secret, "HS256");
const result = await compactVerify(token, secret, { algorithms: ["HS256"] });
// result.payloadString === "hello"
```
## Capabilities
| Feature | Status |
| -------------------------------- | ----------------------------------- |
| RFC 7515 §3.1 compact form | ✓ |
| RFC 7515 §7.2.2 JSON-flattened | ✓ |
| RFC 7515 §7.2.1 JSON-general | ✓ (multi-signature) |
| RFC 7515 §A.5 detached payload | ✓ |
| RFC 7797 `b64: false` header | ✓ |
| HS256 / HS384 / HS512 | ✓ (constant-time tag compare) |
| RS256 / RS384 / RS512 | ✓ (`@suaveplan/crypto-rsa`) |
| PS256 / PS384 / PS512 | ✓ (hash-length salt) |
| ES256 / ES384 / ES512 | ✓ (`@suaveplan/crypto-curve`; DER rejected) |
| EdDSA (Ed25519) | ✓ (RFC 8037) |
| `alg: "none"` | ✓ (dual-flag opt-in only) |
| Embedded `jwk` header | ✓ (`allowEmbeddedJwk: true`) |
| Embedded `x5c` chain | ✓ (`@suaveplan/x509.validateChain`) |
| `kid` resolver | ✓ (caller-supplied async fn) |
| `crit` allowlist | ✓ |
| AbortSignal end-to-end | ✓ |
| Zod boundary at every entry | ✓ |
| OpenTelemetry histograms+counters| ✓ |
## Security defaults
- **Algorithm allowlist is mandatory at verify time.** There is no default — callers MUST pass `opts.algorithms`. Tokens whose `alg` is not on the list are rejected before any cryptography runs (defence against the JWT "alg confusion" CVE class).
- **`alg: "none"` requires dual-flag opt-in.** Both `algorithms: ["none"]` AND `acceptNone: true` are required.
- **`crit` parameters must be allowlisted.** Per RFC 7515 §4.1.11.
- **ECDSA signatures must be fixed-length `R||S`.** DER-encoded signatures in the JOSE position throw `JWS.SIG_FORMAT_INVALID`.
- **HMAC tag comparison is constant-time.** Via `@suaveplan/crypto-utils.constantTimeEqual`.
- **PSS salt length is fixed at the hash output length.** Per RFC 7518 §3.5.
## Modules
| Module | Doc |
| --- | --- |
| `base64url` | [`src/base64url/base64url.md`](src/base64url/base64url.md) |
| Compact serialization | [`src/serialization/compact/compact.md`](src/serialization/compact/compact.md) |
| Flat serialization | [`src/serialization/flat/flat.md`](src/serialization/flat/flat.md) |
| General serialization | [`src/serialization/general/general.md`](src/serialization/general/general.md) |
| Detached payload | [`src/detached/detached.md`](src/detached/detached.md) |
| Header parse / serialize | [`src/header/header.md`](src/header/header.md) |
| `crit` enforcement | [`src/header/crit.md`](src/header/crit.md) |
| RFC 7797 `b64` | [`src/header/b64.md`](src/header/b64.md) |
| Algorithm registry | [`src/algorithms/registry.md`](src/algorithms/registry.md) |
| Built-in seeding | [`src/algorithms/builtin.md`](src/algorithms/builtin.md) |
| HMAC (HS256/384/512) | [`src/algorithms/hmac.md`](src/algorithms/hmac.md) |
| RSA-PKCS1-v1\_5 (RS\*) | [`src/algorithms/rsa.md`](src/algorithms/rsa.md) |
| RSA-PSS (PS\*) | [`src/algorithms/rsa-pss.md`](src/algorithms/rsa-pss.md) |
| ECDSA (ES\*) | [`src/algorithms/ecdsa.md`](src/algorithms/ecdsa.md) |
| EdDSA (Ed25519) | [`src/algorithms/eddsa.md`](src/algorithms/eddsa.md) |
| `alg: "none"` | [`src/algorithms/none.md`](src/algorithms/none.md) |
| JWK → key | [`src/keys/jwk.md`](src/keys/jwk.md) |
| `x5c` chain | [`src/keys/x5c.md`](src/keys/x5c.md) |
| `KeyResolver` | [`src/keys/resolver.md`](src/keys/resolver.md) |
| Verify pipeline | [`src/verify/verify-pipeline.md`](src/verify/verify-pipeline.md) |
| Sign pipeline | [`src/sign/sign-pipeline.md`](src/sign/sign-pipeline.md) |
| Error registry | [`src/errors/error-codes.md`](src/errors/error-codes.md) |
| Schemas | [`src/schemas/schemas.md`](src/schemas/schemas.md) |
| Metrics | [`src/observability/metrics.md`](src/observability/metrics.md) |
## License
MIT.