Reutilizando el estado HMAC para múltiples mensajes

4

Tengo una clave secreta k y el mensaje m . Envío m || HMAC(m, k) a otra parte, que puede verificar la integridad de m, asumiendo que conocen k .

Supongamos que tengo varios mensajes m1 , m2 , etc. ¿Cuál de las dos construcciones siguientes es mejor desde una perspectiva de seguridad global?

m1 || HMAC(m1, k) || m2 || HMAC(m1 || m2, k) || ...

o

m1 || HMAC(m1, k) || m2 || HMAC(m2, k) || ...

Obviamente, no necesitaré concatenar realmente m1 || m2 para generar HMAC(m1 || m2, k) . Simplemente continúo usando el estado del HMAC a medida que se firman cada vez más mensajes. Así que el primer modo de operación es en realidad más eficiente y lo que preferiría usar. En el segundo caso, tengo que restablecer el estado HMAC antes del siguiente mensaje.

En lo que respecta al receptor, la primera autenticación de mensajes falla, ese es el final de la conversación. Por lo tanto, no gano nada al tener un HMAC válido para m2 si no se pudo autenticar m1 .

¿Es una de las construcciones inherentemente más segura que la otra? ¿Podría el primer modo de operación filtrar alguna información sobre la clave (en teoría)?

    
pregunta VokinLoksar 06.10.2012 - 17:48
fuente

2 respuestas

4

Realmente depende de qué función de seguridad quieres lograr. Cada par " m , HMAC ( k , m ) proporciona integridad en el siguiente sentido: el adversario no podrá forjar un par declarado válido por el receptor, que el remitente no computó en primer lugar.

Puede que quieras propiedades adicionales, sin embargo. Por ejemplo, tiene una secuencia de mensajes, y desea evitar que el adversario la altere, es decir, que elimine los mensajes por completo, o que inserte duplicados de los antiguos, o que los vuelva a ordenar. Esta es una preocupación válida, y generalmente se trata con un número de secuencia . Es decir, cada mensaje se envía como " m , HMAC ( k , s || m )" donde s es un número de secuencia de tamaño fijo (por ejemplo, codificado sobre 64 bits), que comienza en 0 para el primer mensaje y luego se incrementa en cada mensaje (con un número de secuencia de 64 bits, tiene bastante espacio, no vuelve a 0 hasta "mucho tiempo"). Así es como se hace en SSL , por ejemplo (cada "registro" de SSL tiene su propio MAC y el cómputo de MAC incluye el número de secuencia). Tenga en cuenta que el número de secuencia es implícito: se utiliza en el cómputo de MAC, pero no necesita ser transmitido a través del cable, por lo que no aumenta el tamaño de lo que se debe transmitir.

Otras construcciones son posibles, como usar el valor HMAC anterior (usted envía " m , HMAC ( k , z || m ) "donde z es la salida HMAC enviada con el mensaje anterior). Su propuesta también funciona, pero solo porque el MAC es realmente HMAC, que es robusto y que permite capturar su estado interno con relativa facilidad (aunque algunas implementaciones no lo hagan fácil). De hecho, muchos algoritmos MAC requieren un IV no repetitivo y no lograrán la seguridad adecuada con su método. Esta es la razón por la que le recomiendo que use números de secuencia, ya que este es un método estándar y bien estudiado que tiene la ventaja adicional de que se aplica más fácilmente a otros algoritmos MAC (y agilidad de algoritmo es una buena característica tener).

(La capacidad de HMAC para operar de una manera sin IV es la función que lo convierte en el mejor algoritmo genérico de MAC. Si todavía tiene una IV, ya que sus mensajes no solo tienen MAC, sino también encriptado, se prefieren los algoritmos encriptación autenticada .)

    
respondido por el Thomas Pornin 06.10.2012 - 19:35
fuente
3

El enfoque sobre el que está preguntando también se conoce como ataque de extensión de longitud , y los HMAC son explícitamente no susceptibles de este tipo de ataques.

Dado HMAC(K, m) = H((K ⊕ opad) ∥ H((K ⊕ ipad) ∥ m)) , lo mejor que puedes hacer es precomputar

Ko  = K ⊕ opad
Ki  = K ⊕ ipad
m'0 = Ki ∥ m0
m'i = m'i-1 ∥ mi

HMAC(K, mi) = H(Ko ∥ H(m'i))

que en realidad no te guarda nada, excepto algunos XOR y anexos. Aún tiene que calcular dos hashes por mensaje, que es la parte computacionalmente costosa. Además de eso, tienes que hacerlo en cantidades cada vez mayores de datos. La verificación de MAC también se ve afectada.

Una cosa que también he notado en su descripción es que simplemente está concatenando mensajes sucesivos y sus MAC. A menos que esas longitudes de mensajes sean fijas, debería estar haciendo algo como

LEN(m0) ∥ m0 ∥ HMAC(K, m0) ∥ LEN(m1) ∥ m1 ∥ HMAC(K, m1) ∥ ...

donde la longitud se almacena con una codificación de tamaño fijo no ambigua (por ejemplo, un entero de 64 bits sin signo en orden big-endian).

Actualización: "Mejor desde una perspectiva de seguridad general" depende completamente de las necesidades de su sistema. Si necesita cada mensaje sucesivo debe ser autenticado por el mensaje anterior, entonces HMAC(H, mi) no es adecuado. Si cada mensaje se puede verificar de manera segura independientemente de cualquier otro mensaje, entonces no hay razón para usar algo más complicado que HMAC(H, mi) .

    
respondido por el Stephen Touset 06.10.2012 - 18:27
fuente

Lea otras preguntas en las etiquetas