Ir al contenido

Firmar y verificar la evidencia

🟡 Parcial — Firma ECDSA-P256+DSSE+in-toto hecha; backend enchufable (local + Scaleway KMS); anclaje de identidad keyless (Sigstore) pendiente.

Toda salida del motor sei — el bundle de evidencia, el reconstruct del ciclo ISO 23894, el Anexo IV y los informes de conformidad — queda firmada antes de escribirse a disco. La firma es un requisito explícito del EU AI Act: el Anexo IV §2(g) exige que los registros de evaluación estén “fechados y firmados”. La integridad criptográfica hace que cualquier modificación posterior sea detectable.


Esquema de firma: ECDSA-P256 (RFC 6979) + DSSE + in-toto

Sección titulada «Esquema de firma: ECDSA-P256 (RFC 6979) + DSSE + in-toto»

Cada artefacto .sei/* lleva un fichero hermano .sig que contiene un sobre DSSE (Dead Simple Signing Envelope) con un Statement in-toto firmado con ECDSA sobre la curva NIST P-256 (SHA-256). Las firmas son deterministas (RFC 6979): la misma clave sobre el mismo contenido produce siempre el mismo valor.

El flujo de firma es:

  1. El artefacto JSON se serializa en forma canónica JCS (RFC 8785) — bytes estables e idempotentes con independencia del orden de claves.
  2. Se construye un Statement in-toto cuyo subject es el digest SHA-256 del JSON canónico. Esto vincula la firma al contenido exacto del artefacto.
  3. El Statement se convierte en el payload del sobre DSSE. El sobre aplica el esquema Pre-Authentication Encoding (PAE) antes de firmar.
  4. Se firma con el crate p256 (RustCrypto). La firma se almacena en formato crudo r‖s (64 bytes) codificado en base64 dentro del sobre.
  5. El sobre serializado se escribe en <artefacto>.sig.

El keyid del sobre es hex(SHA-256(pubkey SEC1 sin comprimir, 65 B)): identifica la clave pública (0x04 ‖ X ‖ Y) sin transportarla entera y permite anclar la verificación a la clave esperada.

bundle.json ← evidencia del ciclo (gate, controles, risk analysis…)
bundle.json.sig ← sobre DSSE · Statement in-toto · firmado ECDSA-P256

Lo mismo aplica a reconstruct.json y conformance/<slug>.json.

El firmante es enchufable vía la variable de entorno SEI_SIGNING_BACKEND:

  • local (por defecto) — clave ECDSA-P256 local. El escalar privado se lee de SEI_SIGNING_KEY (32 bytes en hex, 64 caracteres); si no está configurado, se usa un escalar de desarrollo fijo y conocido que solo aporta integridad/tamper-evidence, no no-repudio.
  • scaleway-kms — la firma se delega al Scaleway Key Manager (algoritmo EC-P256-SHA256); la clave privada nunca sale del KMS. La pubkey verificadora se obtiene del KMS en formato SEC1 sin comprimir (65 B).

sei pubkey imprime la pubkey verificadora del firmante configurado (SEC1 sin comprimir, hex de 130 caracteres). El cliente la exporta y la registra en la nube por conexión (ScmConnection.signerPubkey), de modo que la nube verifica cada evidencia contra la clave esperada de ESA conexión.


sei verify lee .sei/bundle.json y .sei/bundle.json.sig y comprueba cuatro condiciones en cadena:

  1. El sobre es un JSON DSSE válido con payloadType: "application/vnd.in-toto+json".
  2. El keyid del sobre coincide con hex(SHA-256(pubkey SEC1)) de la clave configurada en el entorno (misma clave que firmó).
  3. La firma ECDSA-P256 (raw r‖s) sobre el PAE es válida.
  4. El subject.sha256 del Statement in-toto coincide con el digest del bundle dado.

Si todas pasan, el comando imprime sei verify: firma VÁLIDA y devuelve exit 0. Si cualquier condición falla, devuelve exit 1 con un mensaje descriptivo.

Verificar la integridad del bundle de evidencia
sei verify --repo /ruta/al/proyecto

Salida esperada tras un sei run correcto:

sei verify: firma VÁLIDA

sei verify es el verificador autoritativo. La nube (plano cloud) implementa la misma lógica en TypeScript con @noble/curves (P-256) y verifica por conexión contra la signerPubkey registrada para esa ScmConnection, de forma fail-loud: si la conexión no tiene pubkey configurada, la nube no verifica en silencio sino que falla. Aun así, el resultado definitivo siempre es el del binario sei; la verificación en la nube es de conveniencia y DEBE coincidir.


Escenario¿Detectado por sei verify?
Bundle modificado a mano tras sei runSí — digest SHA-256 del subject no coincide
.sig reemplazado por otro válido de otro bundleSí — el subject apunta al bundle original
Firma forjada con clave diferenteSí — keyid no coincide con la clave configurada
Firma válida sobre contenido íntegroNo es un problema — resultado: VÁLIDA

La firma no cubre la integridad del historial git: sei reconstruct —que replaya los commits del bundle— es el mecanismo para verificar el linaje temporal.


El anclaje de identidad keyless (Sigstore / Fulcio / Rekor) está pendiente. Este mecanismo asociaría la clave pública a una identidad OIDC verificada (p. ej. una cuenta de CI/CD) y depositaría el log de transparencia en Rekor, lo que eliminaría la dependencia de que el verificador conozca la clave pública de antemano.

La guarda de la crate refleja este estado: el módulo seigarrena-sign nombra explícitamente “ancla de identidad keyless pendiente” en su documentación interna.

Para más detalle sobre los artefactos .sei/* y su estructura de sobre, consulta Referencia: artefactos .sei/*. Para el contexto sobre incompletitudes actuales del motor, consulta Estado e incompletitudes.