La teoría del las funciones hash criptográficas no son complicadas de entender. Lo difícil es la construcción de estas funciones desde cero. Sin embargo, no debemos preocuparnos por este aspecto de la teoría matemática, ya que la mayoría de las bibliotecas de los lenguajes de programación nos proporcionan una buena selección de funciones para elegir. Las funciones hash criptográficas se ocupan del problema muy práctico de
determinar si un documento o una cadena de texto ha sido alterado por algún
actor. Estas funciones juegan un papel muy importante en el desarrollo de blockchain y
aplicaciones de criptomonedas, por lo que es importante que comprenda bien
la teoría matemática de estas funciones.
Una introduccion a los algorimos de digestion o hashes
Considere este escenario. Supongamos que tú y yo estamos en extremos opuestos de la Vía Láctea y te mando un mensaje. Ahora al recibir el mensaje, quieres estar seguro que el mensaje no ha sido cambiado por algún extraterrestre hostil mientras el mensaje estaba en tránsito. En segundo lugar, deseas asegurarse de que el mensaje realmente lo envié yo y no por un impostor. Piensa además que no puede comunicarse de forma segura
conmigo para averiguar estas dos cuestiones. Los Hashes criptográficos abordan el problema de si el mensaje fue manipulado mientras está en tránsito. En este post examinamos estas funciones. En el post siguiente veremos las firmas digitales, examinaremos el tema de la autenticidad o responderemos a la cuestion: ¿El documento o mensaje realmente ha sido escrito por la persona que pretende ser su autor?
Una forma que puedo utilizar para asegurarle que el mensaje no ha sido manipulado
mientras está en tránsito sería si le enviara una serie de caracteres llamada resumen de mensaje que está relacionado de alguna manera con el mensaje. Suponga que luego puede ingresar el mensaje en una máquina y si esta máquina produce un resumen de mensaje que coincidide con el mensaje a digerir que te he enviado, entonces podrías concluir que el mensaje no ha sido alterado. Si esta máquina produjo una salida de cadena que no coincidió con el resumen del mensaje que le envié, entonces concluiría que el mensaje se cambió mientras estaba en tránsito. Tal máquina se llamaría función hash criptográfica si satisface algunas restricciones que se discutirán en breve.
La salida de la cadena producida por dicha máquina o función hash criptográfica es
llamada hash criptográfico o resumen de mensaje. La máquina que queremos construir es una función matemática o algoritmo matemático. Entonces, ¿cómo construimos tal función?
Funciones hash
Una función hash es una función que toma una cadena de cualquier longitud como entrada y produce una cadena de longitud fija como salida. Como ejemplo simple, considere la función
y = F(x)
que toma una cadena x como entrada y produce una cadena de salida donde cada carácter de la cadena de entrada emite el siguiente carácter como salida:
F ('criptografía') = 'dszqup'
Esta es una función hash muy simple, pero no es una función hash criptográfica
porque no satisface algunas condiciones requeridas.
Una función hash criptográfica es una función hash que tiene cuatro propiedades esenciales:
• La función produce una cadena de salida de longitud fija.
• La función está libre de colisiones.
• La función es irreversible.
• La función se puede calcular de manera eficiente.
Podemos construir funciones hash que produzcan números en lugar de cadenas. Sin embargo, normalmente especificamos funciones hash que emiten cadenas. Blockchain y las aplicaciones de criptomonedas utilizan invariablemente funciones hash criptográficas que producen salidas de cadena hexadecimales.
Propiedad de longitud fija
Este es un requisito simple. Queremos que nuestra función hash criptográfica produzca
resúmenes de mensajes que tienen una longitud fija. Idealmente, queremos resúmenes de mensajes que sean pequeños de tamaño, ya que influye en la eficiencia de la transmisión de datos a través de una red. Cuanto menor sea el tamaño de un resumen de mensaje, menor será la probabilidad de que sea inadvertidamente corrompido en el curso de su transmisión. Son comunes los hashes de mensajes de 128 bits, 256 bits y 512 bits de longitud.
propiedad de libre colision
Debe quedar claro que si tenemos dos mensajes diferentes, entonces queremos que nuestra function hash produzca dos resúmenes o hashes diferentes, es decir, si x e y son dos diferentes mensajes y
q = H(p)
es nuestra función hash criptográfica, entonces requerimos que
x! = y implica (simple) H(x)! = H(y)
Se produciría una colisión cuando
x! = Y pero H(x) = H(y).
Se dice una función hash esta libre de colisiones si la probabilidad de una colisión es infinitesimalmente pequeña.
La función hash criptográfica puede tener colisiones, pero es prácticamente imposible de encontrar estas colisiones.
Las funciones hash criptográficas típicas no son expresiones matemáticas ordenadas en
sentido clásico, como
y = sen(x)*cos(x)
son una serie finita y secuencial de sustituciones y transformaciones del texto de entrada.
De alguna manera, nunca podemos estar absolutamente seguros de que la función no contenga ninguna colision y esto es evidente ya que al introducir una cadena arbitraria de entrada de longitud n y entregar una cadena de longitud fija m, si:
m > n
implica que la funcion hash es suprayectiva con lo que el conjunto de origen es mayor que el de destino. Entonces existirán cadenas de entrada en que a través de la función hash resulten en identica salida. Esto es demostrable matemáticamente pero también se demuestra que si diseñamos bien la función hash, la probabilidad de colisión es cercana a cero.
Propiedad de irreversibilidad
Este es un requisito de sentido común; no deberíamos poder derivar un mensaje de su
hash. Una buena función hash criptográfica no es reversible. Esto significa que
dada una salida y de una función hash
y = H(x)
es computacionalmente inviable derivar la entrada x del resumen del mensaje y.
Nuestra función hash discutida anteriormente
F('criptografía') = 'dszqup'
no es una función hash criptográfica porque es reversible.
propiedad de eficiente computacion
Esta es una cualidad deseable, pero no esencial, de una buena función hash criptográfica. Queremos que nuestra función sea capaz de calcular resúmenes de mensajes rápidamente. Esta propiedad es deseable sobre la escalabilidad de las aplicaciones que necesitan calcular resúmenes de mensajes. Considera como ejemplo una aplicación bancaria que debe procesar miles de transacciones por segundo, y cada transacción requiere el cálculo de uno o más resúmenes de mensajes. Para que esta aplicación escale, es esencial que pueda calcular hashes de mensajes muy rapido.
propiedad de Demostrar que un archivo ha sido manipulado
Apliquemos lo que hemos aprendido hasta ahora para demostrar que un archivo no ha sido manipulado. Supongamos que tenemos un archivo de texto y una función hash criptográfica. Podemos calcular el hash del mensaje de este archivo si vemos el contenido de este archivo como un largo flujo de caracteres. Entonces, si posteriormente se nos presenta un nuevo archivo y tiene el mismo hash de mensaje que el archivo anterior, podemos concluir que estos dos archivos son iguales ya que los resúmenes de mensajes (hashes criptográficos) de ambos archivos son iguales. Además, si los hashes de mensajes difieren, podemos concluir que los dos archivos no son iguales.
Secure Hash Algorithm 256 (SHA-256)
Las funciones de hash criptográficas son sorprendentemente difíciles de diseñar. SHA-256, SHA-512 y RIPEMD-160 son tres funciones de hash criptográficas de uso común. Vamos a hechar un vistazo a las funciones hash SHA-256 y RIPEMD-160 que se utilizan ampliamente en aplicaciones blockchain.
SHA-256 es un acrónimo de algoritmo de hash seguro 256. El 256 indica que el hash del mensaje (o hash criptográfico) que emite esta función tiene una longitud de 256 bits.
La Figura de abajo muestra cómo se genera un resumen de mensaje SHA-256 para un mensaje o archivo.

El archivo o mensaje se divide en bloques consecutivos que tienen un tamaño de 512 bits. Si el mensaje o archivo no es exactamente el mod 512 (divisible por 512), luego se agrega relleno al final del mensaje o del archivo (un 1 bit seguido de cero bits) de modo que 512 divide la longitud del mensaje o archivo sin resto.
A continuación, construimos un IV aleatorio de 256 bits (vector de inicialización) utilizando un generador de números pseudoaleatorios. Esta es solo una secuencia pseudoaleatoria de 256 bits. El propósito del IV es aumentar la aleatoriedad del proceso de generación de hash.
Concatenamos este IV con el primer bloque de 512 bits del mensaje o archivo. los
El bloque de 768 bits resultante se introduce en el algoritmo hash seguro. Este algoritmo es una función de codificación y compresión que genera una cadena de 256 bits. El algoritmo es una serie de de sustituciones y permutaciones del bloque de 768 bits.
A continuación, tomamos el segundo bloque de mensajes de 512 bits y lo concatenamos con la anterior salida de 256 bits. Esta cadena de 768 bits se introduce en la misma funcion de codificación y compresión y se produce una salida de 256 bits. Procedemos de esta manera, consumiendo total de bloques de mensajes. La salida final de 256 bits es el resumen del mensaje (o hash criptográfico) producido por el algoritmo SHA-256.
El algoritmo SHA-512 es conceptualmente similar excepto que el tamaño del bloque del mensaje tiene 1024 bits de longitud y la función de codificación consta de 80 rondas. Teóricamente cuanto mayor sea el número de pasos involucrados en el proceso de codificación, mayor será la seguridad criptográfica de la función.
El corazón de SHA-256 es la función de codificación y compresión que se aplica a cada bloque de 768 bits. Para SHA-256, esta función consta de 64 rondas.
Cada ronda toma un bloque de 768 bits que se codifica con desplazamiento de bits y lógica de operaciones de bits (OR, XOR, AND) y luego se alimenta a la siguiente ronda. La ultima ronda produce una salida de 256 bits que se concatena con el siguiente bloque de 512 bits del mensaje, y luego las 64 rondas se aplican nuevamente a este bloque de 768 bits. Este proceso se repite hasta que se procesa el último bloque del mensaje.
Un ejemplo de sha256 con python
Python proporciona una implementación para SHA-256, así como otros algoritmos criptográficos hash en su módulo hashlib. El siguiente ejemplo de código de Python muestra la generación de hashes de mensajes codificados en hexadecimal SHA-256 para cadenas:
def getSHA256MD(inputStr):
m = hashlib.sha256()
# convert the input string into a sequence of bytes
strAsBytes = str.encode(inputStr)
m.update(strAsBytes)
# return the message digest as a hexadecimal string
return m.hexdigest()
ret = getSHA256MD('the lazy brown fox jumped over the sleeping dog')
print(ret)
El resultado de ejecutar este programa es el hash SHA-256:
1845c1824b7710df04f1307ea1618857c16e891278eb9dc7edba809915581283
ripemd160
RIPEMD es un acrónimo de RACE Integrity Primitives Evaluation Message Digest. Este algoritmo hash criptográfico puede emitir hashes de mensajes que son 128, 160, 256 y 320
bits de longitud. RIPEMD entró en el dominio público en 1996. RIPEMD-160 encuentra uso en la criptomoneda Bitcoin, así como muchas otras aplicaciones blockchain.
El algoritmo para generar un hash RIPEMD-160 es conceptualmente similar al
proceso que genera un hash SHA-256. RIPEMD-160 divide el mensaje de entrada en
una secuencia consecutiva de bloques de 512 bits. El mensaje se rellena una secuencia de bits para garantizar que sea completamente divisible por 512. Luego, se agrega al mensaje una cadena de 64 bits que codifica la longitud del mensaje. El algoritmo usa cinco
Registros de 32 bits que contienen los resultados intermedios y el resumen del mensaje final. Para iniciar el proceso de generación de hash, estos cinco registros de 32 bits se inicializan con algunos valores fijos. El primer bloque de 512 bits luego se muta mediante una secuencia de diez rondas donde cada ronda consta de una secuencia de 16 pasos de permutaciones y sustituciones que actúan sobre el bloque de mensajes y los valores de registro. La salida de estas diez rondas es un valor de 160 bits que llena los cinco registros de 32 bits. Estos valores de registro luego se ingresan con el siguiente bloque, y el proceso se repite hasta que el último bloque del mensaje haya sido procesado
El valor final de 160 bits en los cinco registros es el resumen del mensaje.
El siguiente código de Python genera un valor hash RIPEMD-160 para una cadena. La salida está codificada como una cadena hexadecimal:
import hashlib
def getRIPEMD160(inputStr):
r = hashlib.new('ripemd160')
strAsBytes = str.encode(inputStr)
r.update(strAsBytes)
return r.hexdigest()
ret = getRIPEMD160('the lazy brown fox jumped over the sleeping dog')
print(ret)
Códigos de autenticación de mensajes
En las secciones anteriores, hemos discutido cómo se pueden usar los hash criptográficos.
para determinar la integridad de un mensaje, es decir, para probar si un mensaje ha sido
manipulado. Los hashes criptográficos no se pueden utilizar para probar la autenticidad. La Autenticidad se preocupa por demostrar que el remitente de un mensaje es a quien él o ella pretende ser. Los códigos de autenticación de mensajes (MAC) brindan garantías de la integridad del mensaje y además, proporciona garantías limitadas de autenticidad. Los algoritmos del código de autenticación de mensajes se basan en claves de cifrado simétricas compartidas. Así es como funcionan los códigos de autenticación de mensajes. Un grupo de dos o más personas comparten una clave de cifrado simétrica secreta. Una persona que quiere enviar un mensaje usa un algoritmo MAC para calcular un valor MAC para este mensaje. Luego envía el mensaje junto con el valor MAC a un destinatario. El destinatario calcula el valor MAC del mensaje usando el algoritmo MAC y la clave secreta. Si este valor MAC calculado es igual al valor MAC entregado por el originador, entonces concluimos que el mensaje no ha sido manipulado. Además, el destinatario puede estar seguro de que el mensaje fue enviado por alguien que tiene la clave secreta.
La Figura de abajo explica cómo se utilizan los códigos de autenticación de mensajes.

Los códigos MAC proporcionan solo una garantía limitada de autenticación. Si el mensaje se recibe junto con un código MAC, solo podemos concluir que el mensaje fue enviado por
una persona que tiene la clave de cifrado simétrica. En particular, una persona puede pretender enviar un mensaje como alguna otra persona que tiene la clave secreta, y el
individuo suplantado no podrá repudiar esta afirmación. Como veremos en un post posterior las firmas digitales resuelven el rompecabezas de la integridad y la autenticación. Puedes notar una diferencia esencial entre los algoritmos hash criptográficos como como algoritmos SHA-256 y MAC. Cualquiera puede calcular el resumen de mensajes SHA-512 de un mensaje. Por el contrario, se requiere una clave secreta compartida para calcular el valor MAC de un mensaje.
Debe tener en cuenta que el mensaje no tiene que estar encriptado. Hay muchos casos de uso en los que se utiliza un código MAC junto con texto sin formato. Por ejemplo, operando los archivos del sistema que no están cifrados, pero pueden tener valores MAC adjuntos para probar la integridad de los archivos. Otro caso de uso destacado se refiere a la transmisión de archivos a través de una red cuando queremos tener la seguridad de que el archivo no fue comprometido durante la transmisión.