Skip to content

Sign & verify evidence

🟡 Partial — ECDSA-P256+DSSE+in-toto signing done; pluggable backend (local + Scaleway KMS); keyless identity anchoring (Sigstore) pending.

Every output of the sei engine — the evidence bundle, the ISO 23894 cycle reconstruct, the Annex IV, and conformance reports — is signed before being written to disk. Signing is an explicit requirement of the EU AI Act: Annex IV §2(g) requires that evaluation records be “dated and signed”. Cryptographic integrity makes any subsequent modification detectable.


Signing scheme: ECDSA-P256 (RFC 6979) + DSSE + in-toto

Section titled “Signing scheme: ECDSA-P256 (RFC 6979) + DSSE + in-toto”

Each .sei/* artifact has a sibling .sig file containing a DSSE envelope (Dead Simple Signing Envelope) with an in-toto Statement signed with ECDSA over the NIST P-256 curve (SHA-256). Signatures are deterministic (RFC 6979): the same key over the same content always yields the same value.

The signing flow is:

  1. The artifact JSON is serialized in JCS canonical form (RFC 8785) — stable, idempotent bytes regardless of key ordering.
  2. An in-toto Statement is constructed whose subject is the SHA-256 digest of the canonical JSON. This binds the signature to the exact artifact content.
  3. The Statement becomes the payload of the DSSE envelope. The envelope applies Pre-Authentication Encoding (PAE) before signing.
  4. Signed with the p256 crate (RustCrypto). The signature is stored as raw r‖s (64 bytes) base64-encoded inside the envelope.
  5. The serialized envelope is written to <artifact>.sig.

The envelope’s keyid is hex(SHA-256(uncompressed SEC1 pubkey, 65 B)): it identifies the public key (0x04 ‖ X ‖ Y) without transporting the whole key and anchors verification to the expected key.

bundle.json ← evidence bundle (gate, controls, risk analysis…)
bundle.json.sig ← DSSE envelope · in-toto Statement · ECDSA-P256 signed

The same applies to reconstruct.json and conformance/<slug>.json.

The signer is pluggable via the SEI_SIGNING_BACKEND environment variable:

  • local (default) — local ECDSA-P256 key. The private scalar is read from SEI_SIGNING_KEY (32 bytes in hex, 64 characters); if not set, a fixed, known development scalar is used — it provides integrity/tamper-evidence only, not non-repudiation.
  • scaleway-kms — signing is delegated to the Scaleway Key Manager (algorithm EC-P256-SHA256); the private key never leaves the KMS. The verifying pubkey is obtained from the KMS in uncompressed SEC1 form (65 B).

sei pubkey prints the verifying pubkey of the configured signer (uncompressed SEC1, 130-character hex). The customer exports it and registers it per connection in the cloud (ScmConnection.signerPubkey), so the cloud verifies each piece of evidence against the expected key of THAT connection.


sei verify reads .sei/bundle.json and .sei/bundle.json.sig and checks four conditions in sequence:

  1. The envelope is a valid DSSE JSON with payloadType: "application/vnd.in-toto+json".
  2. The envelope’s keyid matches hex(SHA-256(SEC1 pubkey)) of the key configured in the environment (the same key that signed).
  3. The ECDSA-P256 signature (raw r‖s) over the PAE is valid.
  4. The Statement’s subject.sha256 matches the digest of the given bundle.

If all pass, the command prints sei verify: firma VÁLIDA and returns exit 0. If any condition fails, it returns exit 1 with a descriptive message.

Verify the integrity of the evidence bundle
sei verify --repo /path/to/project

Expected output after a successful sei run:

sei verify: firma VÁLIDA

sei verify is the authoritative verifier. The cloud plane implements the same logic in TypeScript with @noble/curves (P-256) and verifies per connection against the signerPubkey registered for that ScmConnection, in a fail-loud manner: if the connection has no configured pubkey, the cloud does not verify silently but fails. Even so, the definitive result is always from the sei binary; cloud verification is a convenience and MUST match.


ScenarioDetected by sei verify?
Bundle manually modified after sei runYes — SHA-256 digest of the subject does not match
.sig replaced by a valid one from another bundleYes — subject points to the original bundle
Forged signature with a different keyYes — keyid does not match the configured key
Valid signature over intact contentNot a problem — result: VALID

The signature does not cover git history integrity: sei reconstruct — which replays bundle commits — is the mechanism for verifying temporal lineage.


Keyless identity anchoring (Sigstore / Fulcio / Rekor) is pending. This mechanism would associate the public key with a verified OIDC identity (e.g. a CI/CD account) and deposit a transparency log entry in Rekor, eliminating the dependency on the verifier knowing the public key in advance.

The crate guard reflects this state: the seigarrena-sign module explicitly names “keyless identity anchoring pending” in its internal documentation.

For more detail on .sei/* artifacts and their envelope structure, see Reference: .sei/* artifacts. For context on current engine incompleteness, see State & incompleteness.