first commit
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
# dsse
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Implements the Dead Simple Signing Envelope (DSSE) binding used by
|
||||
in-toto/SLSA attestations. Provides the Pre-Authentication Encoding (PAE)
|
||||
function that callers sign, an envelope constructor that wraps a provenance
|
||||
statement with supplied signatures, and a verification entry point that
|
||||
checks every signature against a set of trusted ed25519 keys. The module
|
||||
deliberately does not sign — Suaveplan signing keys live only inside the
|
||||
publish pipeline, never in consumer bundles — but it provides everything
|
||||
needed for attestation producers and verifiers to interoperate.
|
||||
|
||||
## Features
|
||||
|
||||
- RFC-aligned `DSSEv1 <typeLen> <type> <payloadLen> <payload>` PAE.
|
||||
- `wrapInDsse` binds a canonicalised `ProvenanceStatement` to a
|
||||
base64-encoded payload plus caller-supplied signatures.
|
||||
- `verifyDsseEnvelope` iterates every signature and reports which trusted
|
||||
key (if any) accepted it, returning a structured `DsseVerificationResult`.
|
||||
- `keyid`-scoped key matching so the same envelope can carry signatures
|
||||
from multiple key material with disjoint trust roots.
|
||||
- Structured `AttestationInvalidError` on empty signature lists or empty
|
||||
key lists.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { encodeDssePae, wrapInDsse } from "@suaveplan/slsa";
|
||||
|
||||
const pae = encodeDssePae(
|
||||
"application/vnd.in-toto+json",
|
||||
new TextEncoder().encode(JSON.stringify(statement))
|
||||
);
|
||||
const signature = ed25519.sign(secretKey, pae);
|
||||
|
||||
const envelope = wrapInDsse(statement, [
|
||||
{ sig: toBase64(signature), keyid: "release-key" },
|
||||
]);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
verifyDsseEnvelope,
|
||||
type TrustedEd25519Key,
|
||||
} from "@suaveplan/slsa";
|
||||
|
||||
const trusted: TrustedEd25519Key[] = [
|
||||
{ publicKey: releaseKey, keyid: "release-key" },
|
||||
{ publicKey: rotationKey, keyid: "release-key-next" },
|
||||
];
|
||||
|
||||
const result = verifyDsseEnvelope(envelope, trusted);
|
||||
if (!result.valid) {
|
||||
const bad = result.records.filter((r) => !r.valid);
|
||||
log.warn({ bad }, "DSSE envelope failed verification");
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `DSSE_PAYLOAD_TYPE` — canonical in-toto payload type.
|
||||
- `encodeDssePae(payloadType, payload): Uint8Array` — exact byte sequence
|
||||
that must be signed.
|
||||
- `wrapInDsse(statement, signatures): DsseEnvelope` — canonicalises the
|
||||
statement, base64-encodes the payload, attaches the signatures. Throws
|
||||
`AttestationInvalidError` on empty `signatures`.
|
||||
- `verifyDsseEnvelope(envelope, keys): DsseVerificationResult` — verifies
|
||||
every signature. Throws `AttestationInvalidError` when `keys` is empty.
|
||||
Returns per-signature `DsseVerificationRecord`s with `keyid`, `keyIndex`,
|
||||
`signatureIndex`, and a boolean `valid`.
|
||||
- `TrustedEd25519Key { publicKey, keyid? }` — public-key bundle.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Signing is intentionally absent from the public API — the DSSE spec
|
||||
separates the PAE construction (standardised here) from the signing
|
||||
algorithm (ed25519 only for now). Consumers that want ECDSA / RSA signing
|
||||
can use `@suaveplan/crypto` directly and pass the resulting `sig` into
|
||||
`wrapInDsse`. The PAE uses the exact space-delimited format from the DSSE
|
||||
spec (`DSSEv1 <len> <type> <len> <body>`) — do not reformat whitespace, as
|
||||
even a stray trailing space changes signed bytes and breaks verifiers.
|
||||
Reference in New Issue
Block a user