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,69 @@
# JSON General serialization (multi-signature)
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
RFC 7515 §3.2 + §7.2.1 JSON-general JWS — `{ payload, signatures: JwsSignature[] }`. Each entry in `signatures` carries its own `protected` / `header` / `signature` so the same payload can be co-signed by N parties. Common use cases: cross-tenant message attestation, multi-party authorisation, signed installer manifests where multiple maintainers' signatures must all attach to the same artifact.
## Features
- `generalDecode` — Zod-validated decode; `signatures` must be non-empty.
- `generalVerify` — verifies every signer via a per-signer key resolver; returns `{ verifiedIndices, failedIndices }`.
- `generalSign` — signs the same payload under N signers; rejects mixed-`b64` signers.
- `requireAll: true` causes the first failing index to throw `JWS.MULTI_SIG_FAILED` with `error.context.failedIndex`.
## Usage: Basic Example
```ts
import { generalSign, generalVerify } from "@suaveplan/jws";
const general = await generalSign("hello", [
{ key: secret, alg: "HS256" },
{ key: rsaPriv, alg: "RS256" },
]);
const result = await generalVerify(
general,
async (i) => (i === 0 ? secret : rsaPub),
{ algorithms: ["HS256", "RS256"] }
);
// result.verifiedIndices === [0, 1]
```
## Usage: Advanced Example
```ts
import { generalVerify, JwsError } from "@suaveplan/jws";
try {
await generalVerify(
general,
async (i, protectedHdr) => keyStoreByIssuer.get(protectedHdr["iss"] as string),
{
algorithms: ["HS256", "RS256", "ES256"],
requireAll: true,
}
);
} catch (err) {
if (err instanceof JwsError && err.code === "JWS.MULTI_SIG_FAILED") {
const failedIndex = (err.context as { failedIndex: number }).failedIndex;
// err.cause === underlying JwsError for that signer.
}
}
```
## API Reference
- `generalDecode(json: unknown): GeneralJws`
- `generalVerify(json: unknown, keyResolver, opts: GeneralVerifyOptions): Promise<GeneralVerifyResult>`
- `keyResolver: (signerIndex, protectedHeader, unprotectedHeader?) => Promise<unknown>`
- `GeneralVerifyOptions extends VerifyOptions { requireAll?: boolean }`
- `GeneralVerifyResult = { verifiedIndices, failedIndices, payloadString }`
- `generalSign(payload, signers: GeneralSigner[]): Promise<GeneralJws>`
- `GeneralSigner = { key, alg, protectedHeader?, unprotectedHeader? }`
## Implementation Notes
The default `requireAll` value is `false` — more flexible for use cases like "any one of N trust roots verifies." Callers needing the stricter "every signer must verify" semantics opt in via `requireAll: true`. Mixed `b64` settings across signers are NOT supported because each signer's signing input would differ in interpretation and the receiver-side verifier cannot know which interpretation to apply — `generalSign` rejects mixed-`b64` arrangements with `JWS.MALFORMED`. The payload segment is computed from the first signer's b64 setting and then asserted byte-identical across the rest.