CAPÍTULO 67 · PARTE VI

Firmas digitales y verificación

Explica qué demuestra una firma válida, cómo se construye la signature base y por qué identidad, autorización, frescura y efectos requieren gates adicionales.

Nivel N2 · Estado published

Una firma digital responde una pregunta más estrecha de lo que su nombre sugiere. Dado un mensaje convertido en bytes, una firma, unos parámetros y una public key, el verificador decide si esa combinación es válida bajo un scheme concreto. Ese resultado puede sostener integridad y origen criptográfico bajo la key, pero no identifica automáticamente a una persona, no prueba intención, no concede autorización y no vuelve reciente el mensaje.

La diferencia es operacional. Un artefacto firmado por una build key comprometida puede verificar correctamente y seguir siendo hostil. Una orden firmada puede repetirse dos veces. Un kid puede seleccionar la key de otro tenant. Un documento visualmente idéntico puede producir bytes distintos, y dos objetos distintos pueden confundirse si el framing es ambiguo. La firma sólo es tan precisa como la signature base y tan útil como la policy que interpreta su resultado.

El contrato mínimo

Un signature scheme expone, conceptualmente:

M es una secuencia exacta de octetos, no «una factura», «un JSON» o «el binario» como abstracción. El significado aparece cuando el protocolo define cómo construir esos octetos y cómo ligar la key a un principal y propósito.

Correctness exige que una firma generada honestamente con sk sea aceptada por la pk correspondiente. Security exige algo más: sin sk, un adversario no debe producir una forgery aceptable aunque haya observado o solicitado firmas legítimas. La formulación habitual es existential unforgeability under chosen-message attack. No promete que el adversario no copie una firma ya emitida ni que el mensaje firmado sea verdadero.

La public key distribuye capacidad de verificar. Eso permite que muchos actores comprueben la misma evidencia sin recibir sk. También amplía el número de componentes que pueden interpretar mal true.

El contrato del consumidor debe escribirse antes que la llamada criptográfica: entradas aceptadas, significado del resultado y efectos expresamente prohibidos.

Lo que una firma válida no demuestra

Supón que Verify(pk_A, M, sig)=true. El resultado acredita que sig satisface el scheme para M bajo pk_A. Para atribuirlo a Alicia faltan preguntas externas:

  1. ¿Quién ligó pk_A a Alicia y con qué evidencia?
  2. ¿Estaba esa key autorizada para esta operación, tenant y momento?
  3. ¿Controlaba Alicia la key, o un servicio/HSM firmaba por múltiples callers?
  4. ¿La firma corresponde a la representación que la interfaz mostró?
  5. ¿El mensaje es reciente, de un solo uso y destinado a este receptor?
  6. ¿La key estaba comprometida o retirada cuando importa evaluar la firma?

Por eso «non-repudiation» no debe tratarse como propiedad automática de un algoritmo. Una firma puede ser evidencia fuerte dentro de un sistema con identity proofing, control de keys, audit trail, timestamps y policy. No convierte por sí sola una operación de software en consentimiento humano ni resuelve disputas sobre malware, delegación o compromiso.

Tampoco aporta confidencialidad. Mensaje y firma suelen ser públicos. Si se necesitan firma y cifrado, el protocolo debe definir orden, identidades, metadata protegida y failure behavior; concatenar primitives no crea ese contrato.

Firmar bytes, no interpretaciones

Dos parsers pueden asignar significados diferentes a los mismos bytes. Un parser también puede convertir dos encodings distintos en el mismo objeto. Las firmas fallan en ambas fronteras si el protocolo dice «firma el JSON» sin especificar más.

Un objeto puede variar por orden de keys, espacios, Unicode normalization, números, duplicados, line endings o representaciones equivalentes. La solución no es canonicalizar de memoria. El perfil selecciona una representación canónica completa o firma el encoding original exacto y verifica antes de transformarlo.

El framing evita que concatenaciones diferentes produzcan la misma entrada. "ab" || "c" y "a" || "bc" coinciden si sólo se pegan strings. Se firman tipos y longitudes, una estructura autocontenida o un formato normativo que preserve fronteras.

Domain separation liga la firma a su uso. Una signature base robusta puede incluir:

domain || version || algorithm || tenant || operation || key-id || issued-at || expiry || sequence || payload-type || payload-length || payload

No todos los protocolos necesitan esos campos, pero cada omisión es una decisión. Si tenant queda fuera, una firma podría moverse entre espacios. Si operation queda fuera, una approval podría reinterpretarse como execution. Si algorithm o version no están protegidos, aparece algorithm confusion o downgrade.

RFC 9052 materializa esta idea para COSE. Sig_structure incorpora un context string, protected headers, external_aad y payload. La aplicación reconstruye exactamente ToBeSigned al verificar. Un header no protegido no adquiere autenticidad por estar junto a la firma; kid es una pista para encontrar candidates, no una identidad globalmente única. RFC 9052

Las detached signatures aumentan la necesidad de un binding inequívoco. Firma y payload viajan separados, así que el consumidor debe saber qué digest, media type, size y artifact identity corresponden. Verificar el digest de un archivo y ejecutar otro por una race o path substitution rompe el sistema sin romper la firma.

Hashing y schemes con appendix

Los schemes prácticos normalmente no aplican la operación asimétrica a un archivo arbitrario. Construyen un digest y un encoding definido por el scheme. El hash forma parte del contrato: algoritmo, domain, prehash variant y representación no pueden inferirse después.

Una collision genérica no equivale automáticamente a una forgery, pero una hash function inadecuada puede permitir que dos contenidos compartan digest bajo condiciones explotables. La selección sigue el perfil vigente; no se conserva un hash retirado sólo porque la RSA key tenga suficientes bits.

El verifier no debe aceptar libremente cualquier combinación declarada por el atacante. La policy local fija suite y parámetros permitidos. El mensaje no puede ordenar «verifícame con algoritmo none», un hash legacy o una key buscada en un namespace distinto.

RSA-PSS: la primitive no basta

RSA firma con una operación distinta de encryption y con encoding de firma. La frase «descifrar con la public key» borra la diferencia entre RSAVP1, message encoding y el scheme completo.

RFC 8017 define RSASSA-PSS como scheme probabilístico: EMSA-PSS incorpora salt y combina hashes mediante MGF antes de aplicar la RSA signature primitive. Verify recibe public key, mensaje y signature, reconstruye las relaciones del encoding y produce valid/invalid. Salt length, hash y MGF son parámetros, no metadata decorativa. RFC 8017, §8.1

RSASSA-PKCS1-v1_5 permanece por compatibilidad, pero un protocolo nuevo no debería alternar schemes según input no autenticado. Reutilizar la misma RSA key entre encryption y firma también acopla lifecycle y expone cross-protocol risk, como se explicó en el capítulo 66.

ECDSA: un nonce puede revelar la key

ECDSA necesita un valor secreto k por firma. Si k se repite para dos mensajes o es predecible, las ecuaciones pueden revelar la signing key. El fallo no reduce ligeramente la seguridad: puede destruirla.

RFC 6979 define una derivación determinista de k desde private key y hash del mensaje. Las firmas resultantes siguen siendo ECDSA ordinarias; el verifier no necesita saber cómo se obtuvo k. Determinista no significa improvisar k = H(M): el procedimiento usa una construcción criptográfica precisa. RFC 6979

La implementación sigue expuesta a faults y side channels. Una primitive mantenida protege scalar arithmetic, nonce generation, point validation y encoding mejor que código propio. La aceptación también debe decidir canonical encoding cuando un ecosistema admite firmas ECDSA matemáticamente equivalentes; de otro modo, un identificador basado en signature bytes puede ser malleable aunque Verify sea verdadero.

EdDSA: determinismo con variantes concretas

RFC 8032 define Ed25519 y Ed448 como instancias de EdDSA. Las firmas básicas son deterministas, lo que evita depender de randomness fresco por cada firma. Key generation todavía necesita entropía, y determinismo no evita leakage, faults, key reuse entre protocolos ni confusión de variants. RFC 8032

PureEdDSA, prehash variants y contexts no son intercambiables. El protocolo fija exactamente qué variante usa y cómo separa dominios. Un implementation example de un RFC tampoco constituye por sí solo una biblioteca hardened; el propio RFC advierte que su sample code no intenta ser side-channel silent.

FIPS 186-5, final desde 2023, estandariza RSA, ECDSA y EdDSA para generación/verificación; DSA permanece sólo para verificar firmas existentes. Una planning note de 2025 anuncia potential updates, no una revisión final nueva. FIPS 186-5

ML-DSA y la transición post-quantum

FIPS 204 estandariza ML-DSA, una familia de firmas module-lattice diseñada frente a adversarios clásicos y cuánticos conocidos. Define ML-DSA-44, ML-DSA-65 y ML-DSA-87. Los números nombran parameter sets; no son bits de seguridad que puedan compararse por lectura directa con una RSA key. FIPS 204

ML-DSA no es ML-KEM: una firma no establece un shared secret y un KEM no firma. Adoptar ML-DSA cambia public keys, signatures, encodings, buffers, certificates, HSM interfaces, logs y transport limits. FIPS 204 tiene una planning note de 2026 sobre potential updates; sigue siendo final, pero las implementaciones deben seguir errata y perfiles aplicables.

La migración puede requerir firmas clásicas y post-quantum simultáneas. «Acepta cualquiera» reduce seguridad a la rama más débil; «exige ambas» cambia disponibilidad y semantics de rotation. El capítulo 72 desarrollará combiners y transición.

Una firma, varias firmas y autoridad compartida

Un contenedor con dos signatures no implica por sí mismo «aprobación por dos personas». Pueden ser dos keys del mismo principal, una firma del contenido y otra del envelope, una transición de algoritmo o dos approvals independientes. El protocolo define el rol de cada signer, qué bytes firmó y qué regla combina resultados.

Las policies any-of, all-of y k-of-n no son equivalentes. any-of conserva disponibilidad, pero la seguridad efectiva cae si una key débil basta. all-of aumenta dependencia y puede bloquear operación durante rotación. k-of-n necesita un roster auténtico y versionado; de lo contrario, el atacante puede cambiar quiénes cuentan antes de evaluar el umbral.

Varias signatures independientes tampoco son un threshold signature criptográfico. En un threshold scheme, participantes cooperan para producir una sola firma bajo una public key y el protocolo interno protege shares, nonces y complaint handling. En un multisignature container, cada signature suele verificarse por separado. Confundirlos oculta qué evidencia queda disponible y quién puede atribuir una participación.

Una countersignature debe ligar inequívocamente el objeto que confirma. Firmar sólo la signature anterior puede omitir payload o headers relevantes; firmar sólo el payload puede omitir el rol del primer signer. La especificación fija la estructura completa, el orden y el significado: «reviewed», «approved» y «witnessed» no son sinónimos.

La verificación devuelve entonces un conjunto de resultados, no un booleano global prematuro. Primero se comprueba cada firma con su key, suite y signature base; luego se resuelve el rol y estado de cada principal; al final se evalúa la policy versionada. El sistema registra qué regla se aplicó para que una auditoría posterior no reinterprete el mismo envelope bajo una policy nueva.

Verificar es un pipeline

Un verifier profesional no llama a Verify y ejecuta. Sigue gates ordenados:

  1. limitar tamaños y parsear exactamente una estructura;
  2. exigir version, suite y critical fields conocidos;
  3. reconstruir los bytes ToBeSigned sin parser differential;
  4. resolver candidate keys dentro del trust domain correcto;
  5. comprobar purpose, tenant, status y algoritmo de la key;
  6. ejecutar verification con una primitive mantenida;
  7. comprobar audience, operation, expiry, challenge o sequence;
  8. aplicar replay/idempotency state cuando corresponda;
  9. autorizar al principal resultante para el efecto concreto;
  10. producir el efecto una sola vez y registrar evidencia no secreta.

RFC 9421 expone este patrón para HTTP Message Signatures: el verifier selecciona la firma aplicable por policy, comprueba los componentes cubiertos, resuelve key material confiable, exige coherencia del algoritmo, reconstruye la signature base y aplica la verificación. Los requisitos de aplicación pueden imponer edad máxima, expires, unicidad de nonce y componentes obligatorios; si no se cumplen, la validación falla aunque la operación criptográfica aislada sea correcta. RFC 9421, §3.2

Cualquier fallo termina antes del efecto. Un kid desconocido no activa búsqueda global ni descarga una key indicada por el atacante. Una suite no permitida no cae a legacy. Una firma inválida no produce payload parcialmente confiable.

Errores externamente uniformes reducen oracles, pero la observabilidad interna debe distinguir parse failure, key not found, invalid signature, stale message, replay y denied authorization. Los logs no guardan private key ni inputs sensibles completos; sí pueden conservar digest, key-id resuelto, suite, policy version y decision code.

Replay: copiar no es falsificar

Una firma puede ser unforgeable y perfectamente replayable. El attacker no altera un bit: reenvía M, sig. Si M dice «transfiere 100» y no incluye freshness o single-use identity, ambas ejecuciones pueden verificar.

El protocolo liga un challenge impredecible del verifier, sequence monotónico, expiration, request-id o nonce con semántica definida. El receptor mantiene estado para saber qué valor ya consumió. Expiry sin reloj confiable, nonce sin registro o idempotency key fuera de la firma son controles incompletos.

En protocolos challenge-response, la firma debe cubrir challenge, verifier identity, operation y transcript. Firmar sólo el challenge permite que otra operación o receptor lo reutilice. En webhooks y colas, la ventana temporal limita storage pero no sustituye un deduplication key atómico.

Compromiso, tiempo y evidencia histórica

Si una signing key se compromete, el attacker puede producir firmas nuevas indistinguibles matemáticamente de las legítimas. Rotation detiene uso futuro si los verificadores reciben el cambio, pero no responde por sí sola cuándo se creó una firma antigua.

Un timestamp local dentro del mensaje es una declaración del signer. RFC 3161 define una Time-Stamping Authority que firma un token sobre un data imprint y tiempo confiable. Eso puede evidenciar que el imprint existía antes de un momento bajo la policy de la TSA. No prueba que una persona leyó el documento, que el signer estaba autorizado o que el contenido era verdadero. RFC 3161

La validación de larga duración conserva message, signature, algorithm parameters, certificate/path evidence cuando aplique, timestamp, revocation/status evidence y policy versions. Si el hash o scheme envejece, archival renewal encadena evidencia antes de que deje de ser defendible. Certificados, paths y revocation se tratan en el capítulo 69.

Caso trabajado: aprobación cruzada entre tenants

Un servicio recibe {tenant, amount, account, kid, sig}. Busca kid en un cache global, verifica sobre amount || account y ejecuta la transferencia. El mismo kid existe en dos tenants; además, tenant queda fuera de la firma.

El atacante copia una approval válida del tenant A, cambia el campo externo a B y fuerza que el cache seleccione una key compatible. Incluso si la sustitución de key no funciona, puede repetir la misma approval varias veces porque no hay operation-id ni sequence firmado.

La remediación redefine el contrato:

La prueba positiva firma y ejecuta una sola vez. Los negativos cambian un campo firmado, mueven el envelope a otro tenant, repiten request-id, expiran la orden, sustituyen kid, degradan algorithm y usan una key retirada. Cada caso debe fallar en el gate esperado sin producir un efecto parcial.

Método de revisión

Ante cualquier sistema de firmas, pregunta:

  1. ¿Cuál es el scheme exacto, variante y parameter set?
  2. ¿Qué bytes exactos forman ToBeSigned?
  3. ¿Cómo se enmarcan tipos, longitudes y versiones?
  4. ¿Qué domain, protocol, tenant, operation y audience quedan ligados?
  5. ¿Qué metadata permanece fuera de la firma y quién puede cambiarla?
  6. ¿Cómo se resuelve la key y se prueba su binding/purpose?
  7. ¿Qué significa true y qué no significa?
  8. ¿Dónde se evalúan freshness, replay y authorization?
  9. ¿Qué ocurre con unknown algorithms, duplicate fields y noncanonical encodings?
  10. ¿Cómo se protege sk y se audita cada signing request?
  11. ¿Cómo se rota, retira y responde al compromiso?
  12. ¿Qué evidencia permite validar históricamente?
  13. ¿Qué vectors positivos y negativos prueban el contrato completo?

Los tests incluyen official vectors, firma/message/key incorrectos, bit flips, truncation, trailing bytes, duplicate fields, wrong domain, cross-tenant move, replay, stale time, unknown critical header, algorithm mismatch y candidate-key ambiguity. También fijan el byte string esperado de ToBeSigned; comprobar sólo que una biblioteca firma y luego se verifica a sí misma puede ocultar dos implementaciones igualmente equivocadas.

Transferencia al capítulo 68

Una firma autentica bytes bajo una signing key; no establece por sí sola un secreto fresco. El capítulo 68 estudiará key exchange, transcripts, autenticación de ephemeral contributions y forward secrecy.

Síntesis

Una firma válida acredita una relación criptográfica entre bytes, signature, parámetros y public key. Identidad, intención, autorización, tiempo y single-use son claims adicionales.

La signature base es parte del diseño de seguridad. Domain separation, framing, canonicalization y protected context impiden que bytes válidos adquieran otro significado. RSA-PSS, ECDSA, EdDSA y ML-DSA tienen contratos distintos; no se intercambian por etiqueta.

Verification es un pipeline fail-closed. Primero parsea y resuelve trust context; luego verifica; después comprueba freshness y autorización; sólo al final produce un efecto. Copiar una firma no es falsificarla, de modo que replay se controla fuera del security game básico.

Fuentes primarias y documentación técnica