3.0 KiB
JSON General serialization (multi-signature)
(no submodules)
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;signaturesmust 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-b64signers.requireAll: truecauses the first failing index to throwJWS.MULTI_SIG_FAILEDwitherror.context.failedIndex.
Usage: Basic Example
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
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): GeneralJwsgeneralVerify(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.