Un control fallido sin cambiar los datos
Una pipeline de IA recibe dos configuraciones: una compacta y otra con saltos y espacios, además de distinto orden de campos. Al analizarlas obtiene el mismo objeto, pero hashes distintos. ¿Es un cambio? Depende de si identificamos el archivo exacto o los datos representados. Confundir ambas preguntas produce falsas alarmas o elimina diferencias importantes.
¿Cómo elegimos una identidad coherente con el control? Los ejemplos reproducibles cubren serialización JSON, convertir datos en texto, y SHA-256 sobre bytes. También muestran por qué coincidir con un hash de manifiesto no demuestra automáticamente quién produjo un archivo. No atacamos sistemas reales ni certificamos infraestructura: documentos sintéticos y biblioteca estándar Python.
Tres niveles que conviene nombrar
En bytes, dos archivos son iguales si coincide cada byte en su posición. En datos, nuestros objetos JSON coinciden tras analizar si tienen las mismas claves y valores, sin dar significado al orden de propiedades. La aplicación puede definir otras equivalencias, pero debe especificarlas: versión entera 1 y cadena “1” no son intercambiables en nuestro contrato. Parecerse visualmente no basta.
SHA-256 transforma bytes en un digest de 256 bits, normalmente 64 cifras hexadecimales. Es determinista: mismos bytes, mismo digest. Hashes iguales no prueban matemáticamente entradas idénticas porque hay más entradas que digests; el experimento compara también los bytes. Hashes distintos prueban bytes distintos, no significados aplicativos distintos.
Dos archivos, un objeto
El objeto tiene name café y version entera 1. La primera serialización escribe name y version sin espacios, codificando é directamente en UTF-8. La segunda invierte el orden, añade indentación y usa un escape JSON para el carácter no ASCII. Al analizar se reconstruye el mismo objeto. Los archivos ocupan 28 y 41 bytes y sus digests difieren. El resultado conserva texto, bytes hexadecimales y SHA-256 completo, sin depender de hashes abreviados.
Un representante estable en un dominio declarado
Para comparar datos y no formato definimos C local: ordenar claves, usar separadores compactos, conservar cadenas y codificar UTF-8. Admitimos objetos con claves ASCII, cadenas Unicode codificables, listas y enteros entre −(2^53−1) y 2^53−1. Rechazamos float, booleanos y otros tipos. El contrato limitado permite entender el resultado; no es una biblioteca general de canonicalización JSON.
C produce los mismos 28 bytes y digest para ambos objetos. Formato y orden dejan de afectar al dominio elegido. Esto no legitima cualquier transformación previa al hash. Eliminar un campo significativo o convertir versiones a cadenas podría ocultar cambios importantes. Una representación estable debe conservar las distinciones que exige la aplicación.
Cadenas que parecen iguales
Escribamos café con é como punto U+00E9 o con e seguida del acento combinante U+0301. Pueden verse iguales, pero son cadenas distintas. El documento compuesto ocupa 28 bytes y el descompuesto 29; C conserva la diferencia y cambian los digests. El código muestra que normalizar explícitamente a Unicode NFC puede igualarlas, pero no lo aplica silenciosamente en C. Cambiar caracteres de un identificador puede alterar el dato protegido.
| Par | Mismo objeto | Mismos bytes | Mismo digest |
|---|---|---|---|
| raw1 / raw2 | T | F | F |
| C(raw1) / C(raw2) | T | T | T |
| compuesto / descompuesto | F | F | F |
T significa verdadero y F falso. C(raw1) abrevia analizar y aplicar C; opera sobre datos, no texto bruto. Las filas son comparaciones ejecutadas de archivos concretos, no porcentajes ni probabilidades de colisión. La figura separa tamaños en bytes y resultados lógicos.

La referencia JCS y el límite del código
RFC 8785 describe JSON Canonicalization Scheme, JCS, como documento informativo de junio de 2020. Sus reglas cubren números, propiedades y cadenas: no aplica normalización Unicode y ordena claves por unidades UTF-16. sort_keys de Python no prueba conformidad general. El ejemplo fuera de contrato U+E000 y U+10000 tiene distinto orden por puntos y UTF-16. Tampoco implementamos serialización numérica ECMAScript. C es una convención local, no “JCS en una línea”.
Un hash de referencia necesita confianza
Al cambiar version de 1 a 2 cambia el digest y falla frente al original fiable. Si quien sustituye el documento puede sustituir también un manifiesto no fiable con el nuevo digest, la comparación pasa. Eso verifica el código. No es colisión ni debilidad matemática de SHA-256: cambió la referencia. El hash solo no vincula contenido con identidad o momento de aprobación.
Una firma o código de autenticación puede responder otras preguntas con claves y políticas apropiadas; no los implementamos ni presentamos esto como auditoría criptográfica. Un archivo auténtico puede contener datos erróneos. Integridad frente a referencia, procedencia y corrección necesitan evidencias distintas. Canonicalizar hace repetible una comparación acordada, sin decidir qué fuente merece confianza.
Aplicación concreta y límites
Este flujo editorial registra realmente SHA-256 y compara materiales descargados del sitio con archivos preparados. Comprueba esas transferencias; no es un producto de seguridad EL-AI ni prueba de revisión científica humana. Para conservar un PNG o ZIP exacto, transformarlo antes desviaría el objetivo: buscamos identidad de bytes. Para configuraciones JSON independientes del formato hay que definir y versionar el contrato.
El experimento no ejecuta una batería JCS, mide throughput ni implementa un protocolo completo de entrada hostil. Rechaza tipos antes de serializar y registra esos rechazos sin fingir soporte. Ordenar m claves requiere orden m log m comparaciones, además de leer y escribir contenido; influye también la longitud de cadenas. No inferimos rendimiento de esa descripción algorítmica.
Respuesta: decidir primero qué igualdad necesitamos
Los hashes difieren porque cambian los bytes aunque el objeto analizado sea el mismo. Una representación estable elimina diferencias de formato acordadas conservando diferencias de datos; no crea procedencia ni sustituye referencia fiable. Antes de comprobar un checksum hay que decidir qué debe permanecer igual y quién garantiza el valor esperado. Ese contrato junto al cálculo hace útil verificar.
Fuentes y código ejecutado
RFC 8785 — JSON Canonicalization Scheme, §§3.1–3.2, 5.
import json,hashlib
a={'name':'café','version':1}
b={'version':1,'name':'café'}
x=json.dumps(a,ensure_ascii=False,separators=(',',':')).encode('utf8')
y=json.dumps(b,ensure_ascii=True,indent=2).encode('utf8')
assert json.loads(x)==json.loads(y)
assert x!=y and hashlib.sha256(x).digest()!=hashlib.sha256(y).digest()
print(len(x),len(y)) # 28 41
Código, datos e instrucciones · JSON. Cálculos didácticos ejecutados con Python 3.14.0; figuras con Matplotlib 3.11.2. Análisis asistido por IA, sin afirmar revisión por pares ni humana. Portada original ImageGen ilustrativa: no documenta personas, sedes ni instalaciones de EL-AI. Fuentes consultadas el 21 de octubre de 2026.

