Ir al contenido

Verificación de evidencias sin conexión

Un paquete de evidencias es un archivo ZIP exportado desde el panel. Contiene los registros de consentimiento, los archivos necesarios para comprobarlos y los scripts que ejecutan las comprobaciones. Nada de lo descrito en esta página necesita una conexión de red ni un servidor de HashEntry.

Esta página indica qué acredita cada comprobación. Ninguna comprobación por sí sola hace que un registro sea “a prueba de manipulaciones”; en conjunto, permiten que un tercero detecte cambios y vincule un registro a un momento en el tiempo.


ArchivoContenido
consent_data.jsonLos registros exportados. Cada registro tiene un bloque proof con la carga útil sobre la que se calculó el hash.
manifest.json, metadata.jsonIdentificador del paquete, versión de la especificación, número de registros, package_hash.
checksums.sha256SHA-256 de cada archivo del paquete.
timestamps.tsv + *.tsrTokens de sello de tiempo RFC 3161 (tsa_consent_<id>.tsr, tsa_epoch_<id>.tsr, package.tsr) y lo que cubre cada uno.
merkle_proofs.tsvPruebas de inclusión Merkle (para los registros sellados en el tiempo por lotes).
verify.sh, verify.ps1, README_*.txt, proof_verification.txtScripts de verificación e instrucciones.

Los paquetes son de dos tipos: sin firmar (solo sumas de comprobación) y firmados (el hash del paquete lleva además un sello de tiempo RFC 3161, package.tsr).

Ventana de terminal
# Linux / macOS
chmod +x verify.sh && ./verify.sh
Ventana de terminal
# Windows
powershell -ExecutionPolicy Bypass -File .\verify.ps1

Los scripts comprueban las sumas de comprobación de los archivos, el manifiesto, los sellos de tiempo y las pruebas Merkle. No recalculan el hash de prueba, porque para ello se necesita la clave secreta del responsable del tratamiento (véase más abajo).


Ventana de terminal
sha256sum -c checksums.sha256 # macOS: shasum -a 256 -c checksums.sha256
sha256sum consent_data.json # debe ser igual a "package_hash" en manifest.json

Esto muestra que los archivos son los enumerados en el paquete. Por sí solo no muestra quién creó el paquete ni cuándo: cualquiera puede recalcular las sumas de comprobación después de modificar un archivo. Para eso sirve el sello de tiempo de un paquete firmado.

Cada registro tiene un proof_hash. Para los registros actuales (hash_version: 2):

proof_hash = HMAC-SHA256( clave = clave secreta del tenant, mensaje = canonical_json(payload) ) → hexadecimal en minúsculas

La carga útil tiene siempre estas once claves (un valor ausente es null):

v (= 2), site_id, document_hash, consent_type, decision, categories, timestamp,
pii_digest, signer_digest, custom_document_hash, signature_hash
  • timestamp está en UTC con precisión de segundos, YYYY-MM-DDTHH:MM:SS+00:00, y es igual a la hora de creación del registro.
  • Los datos personales no entran directamente en el hash. Entran a través de resúmenes (digests) con sal: pii_digest = SHA-256( pii_salt || canonical_json({ip_address, user_agent, visitor_id}) ), y signer_digest = SHA-256( pii_salt || canonical_json(campos de evidencia del firmante) ) (o null cuando no hay firma ni declaración de identidad). Cuando un registro se anonimiza, los campos personales y la sal se eliminan, pero los resúmenes permanecen, de modo que el hash de prueba puede seguir recalculándose mientras que los resúmenes ya no pueden vincularse a una dirección IP o de correo electrónico.

JSON canónico (HashEntry Canonical JSON v2; no es RFC 8785):

  1. UTF-8, sin espacios en blanco entre tokens.
  2. Claves de objeto ordenadas por orden de bytes UTF-8, en todos los niveles.
  3. Se conserva el orden de los arrays.
  4. Un objeto vacío y un array vacío se escriben ambos como [].
  5. En las cadenas solo se escapan ", \ y los caracteres de control U+0000–U+001F; / y los caracteres no ASCII no se escapan.
  6. Los números son únicamente enteros.
  7. true, false, null en minúsculas.

Quién puede comprobarlo. El hash de prueba es un hash con clave. Recalcularlo requiere la clave secreta del tenant, por lo que puede comprobarlo el responsable del tratamiento (que posee la clave) o un perito al que el responsable entregue la clave. Sin la clave, un tercero no puede recalcularlo, y tampoco lo necesita: las comprobaciones de sello de tiempo y Merkle descritas más abajo operan sobre el propio valor proof_hash y muestran que ese valor existía en el momento indicado.

Ventana de terminal
# Con la clave: recalcular el hash de prueba de un registro (carga útil canónica en payload.json, sin salto de línea final)
openssl dgst -sha256 -hmac "$TENANT_SECRET_KEY" payload.json

Los registros heredados (hash_version: 1) utilizan SHA-256( json(payload con las claves de nivel superior ordenadas) || clave ) y contienen datos personales directamente en la carga útil.

timestamps.tsv tiene una fila por token:

<file.tsr> <kind> <stamped data> <provider> <level at issuance>

Los datos sellados (<stamped data>) son una cadena hexadecimal en minúsculas: el proof_hash de un registro, una raíz Merkle o el package_hash. La autoridad de sellado de tiempo (TSA) firmó el SHA-256 de los caracteres ASCII de esa cadena, sin salto de línea final:

Ventana de terminal
printf '%s' "<stamped data>" > data.txt
openssl ts -verify -data data.txt -in <file.tsr> -CAfile <tsa_root.pem>
  • El certificado raíz de la TSA no está en el paquete. Obténgalo de la propia autoridad de sellado de tiempo; una raíz suministrada por la parte cuyos datos se están comprobando no probaría nada.
  • Los tokens de algunas autoridades no superan la comprobación ESSCertID de openssl ts -verify. En ese caso, verifique la firma y la cadena en dos pasos:
Ventana de terminal
openssl ts -reply -in <file.tsr> -token_out -out token.der
openssl cms -verify -inform DER -in token.der -CAfile <tsa_root.pem> \
-purpose timestampsign -binary -out tstinfo.der
openssl ts -reply -in <file.tsr> -text # compare "Message data" con el SHA-256 de los datos sellados

Una fila cuyo nivel es simulated es un sello de tiempo de prueba: no tiene archivo .tsr ni valor probatorio.

Un token válido muestra que el valor sellado existía, a más tardar, en el momento que figura en el token, según esa autoridad de sellado de tiempo. Que los sellos de tiempo de una autoridad determinada gocen de una presunción legal (por ejemplo, los sellos cualificados de tiempo electrónicos conforme a eIDAS, o los sellos de tiempo de un proveedor autorizado conforme a la legislación turca) depende de la autoridad y de la jurisdicción.

Cuando los registros se sellan en el tiempo por lotes, un único token cubre la raíz Merkle de un lote y cada registro recibe una prueba de inclusión. merkle_proofs.tsv:

<consent_id> <leaf> <index> <leaf_count> <root> <epoch.tsr> <path>

El árbol es el esquema Merkle binario SHA-256 propio de HashEntry. No es el árbol de RFC 6962 (Certificate Transparency), y las herramientas de RFC 6962 no lo verificarán:

  • hoja = el proof_hash del registro (64 caracteres hexadecimales en minúsculas), sin ningún hash adicional;
  • nodo = SHA-256 sobre los 128 caracteres ASCII hex(left) || hex(right): las cadenas hexadecimales, no los bytes sin procesar;
  • una capa con un número impar de nodos duplica su último nodo;
  • no hay prefijo de separación de dominio entre hojas y nodos.

Para verificar una ruta, comience con cur = leaf y aplique cada paso:

L:<h> → cur = sha256(<h> || cur)
R:<h> → cur = sha256(cur || <h>)
Ventana de terminal
# un paso, p. ej. "R:<h>"
cur=$(printf '%s%s' "$cur" "$h" | sha256sum | cut -d' ' -f1)

El resultado debe ser igual a <root>. Compruebe también que 0 <= index < leaf_count y que el número de pasos es igual a la profundidad del árbol para leaf_count: la regla de duplicación no vincula el tamaño del árbol a la raíz, por lo que estos valores forman parte de la prueba. Por último, verifique <epoch.tsr> frente a <root> como en la sección 3: eso es lo que prueba cuándo existía la raíz y, por tanto, el hash de prueba del registro.


Qué acreditan las comprobaciones en conjunto

Sección titulada «Qué acreditan las comprobaciones en conjunto»
ComprobaciónAcredita
Sumas de comprobaciónLos archivos del paquete son coherentes entre sí.
Hash de prueba (con la clave)El contenido del registro coincide con el hash calculado cuando se escribió.
Token RFC 3161El valor sellado existía, a más tardar, en el momento del token, según esa autoridad.
Prueba Merkle + token de épocaEl hash de prueba del registro formaba parte de un lote cuya raíz existía en ese momento.

Lo que no acreditan: quién era la persona (véase identity_assurance en Aprobación y firma de documentos), ni que el registro se creara correctamente en primer lugar. Los registros sin token de sello de tiempo se apoyan únicamente en el hash de prueba y, por tanto, en el secreto de la clave.