2. 1
ESCENARIO
3
2
FALLOS EN CIFRADOS
FALLOS QUE DESMONTAN
UN SISTEMA COMPLETO
FALLOS EN LA PROTECCIÓN
6
FALLOS CON CRIPTOGRAFÍA
DE CLAVE PÚBLICA
5
FALLOS EN FIRMAS
4
DE LA INTEGRIDAD
7
SIDE CHANNEL
8
CONCLUSIÓN
4. Criptografía simple y atractiva
¿128 ó 256 bits?
» Disponible para todo el público
» No es complicado entender lo básico.
» [Ya casi] nadie implementa sus algoritmos.
» Disponemos de muchas librerías de bajo nivel.
ESCENARIO
OpenSSL
Java crypto API
Microsoft CryptoAPI
5. Pero la realidad es otra
» Extremadamente frágil.
Especialmente la criptografía de clave pública.
» Revisar el código cuesta mucho más que desarrollarlo.
ESCENARIO
(además lo debe revisar otro)
» Cuando falla algo… falla todo.
6. Para no implementar criptografía
SSL para las comunicaciones
Solución simple
GPG para el resto
Si no puedes, usa
una librería de GPGME, keyzcar, u
otras comerciales
alto nivel:
ESCENARIO
No uses directa-
OpenSSL, Crypto++,
mente las de Java crypto, BouncyCastle,
bajo nivel: .NET System.Security.Cryptography
7. Piensa mal y acertarás
» Confusiones evidentes
uso de MAC para firmar
“descifrar” una firma para comprobarla
confusión de términos
» Seguridad basada en usar SALTs
» Argumentos que defienden lo no estándar
ESCENARIO
seguridad por oscuridad
la clave es muy grande
lo leí en un blog
8. Piensa mal y acertarás
» Cifrado de varios bloques con RSA
» Insistencia en mantener los IVs secretos
» Uso de diagramas que no vienen del estándar
ESCENARIO
» Y una de las mejores:
Criptografía y Javascript ¡no tiene sentido!
Dispones SSL en el cliente y en el servidor
10. fallos que
2
desmontan un
sistema completo
la fortaleza de una cadena es la de su eslabón más débil
11. FALLOS QUE DESMONTAN UN SISTEMA COMPLETO
Compatiblidad hacia atrás
» Tenemos un esquema antiguo
una cookie, hash SHA1
con un cifrado CBC, sin integridad
lo mejoramos: HMAC, integridad
» Lo implantamos no adecuadamente
se detecta una cookie antigua
se migra al nuevo formato ¡!
» Seguir usando ambos esquemas
desgraciadamente demasiado común
12. FALLOS QUE DESMONTAN UN SISTEMA COMPLETO
PRNGs no adecuados
no uses:
random (python)
java.util.Random (java) el “por defecto”
System.Random (.NET)
no es suficiente
usa:
random.SystemRandom (python)
java.security.SecureRandom (java)
System.Security.Cryptography.RandomNumberGenerator (.NET)
nunca:
time() ^ getpid() y sus hermanos
13. FALLOS QUE DESMONTAN UN SISTEMA COMPLETO
PRNGs no adecuados
» Son la fortaleza de los cifrados de flujo
indispensables creación de claves!
para IVs, contadores, PIDs, …
un sistema
» El caso OpenSSL y Debian, ¡return 4!
criptográfico entropía reducida a menos de 15bits
claves comprometidas
peligro de MITM
tablas precalculadas RSA, DSA
14. FALLOS QUE DESMONTAN UN SISTEMA COMPLETO
PRNGs no adecuados
» Relacionado, mismo concepto:
el ataque DNS de Kaminsky
acertar un valor con 16 bits de entropía
» Con un servidor recién arrancado
poca entropía disponible
repetición de IVs, valores de desafios...
15. FALLOS QUE DESMONTAN UN SISTEMA COMPLETO
PRNGs no adecuados
» Mifare Classic
reconstruyeron el circuito a base de fotografías microscópicas.
» Su RNG
Semilla 16 bits, basado en un LFSR.
Valor derivado del tiempo de lectura desde el encendido.
16. PRNGs no adecuados
crypto1 Vs. crapto1
FALLOS QUE DESMONTAN UN SISTEMA COMPLETO
18. Modos de encadenamiento
» ¿ECB? Sin realimentación
» El típico conocido CBC
» OFB, CFB, CTR
convierten de bloque a uno flujo
FALLOS CIFRANDO
sin integridad, se pueden cambiar bits
no reusar: IVs, realimentación, contadores...
» CCM, EAX, GCM, OCB
CCM == Counter with CBC-MAC
cifrado autenticación
tampoco se debe reusar IVs
19. fallos en la pro-
4
tección de la
integridad
colega, éste no es mi coche
20. FALLOS EN LA PROTECCIÓN DE LA INTEGRIDA
FALLOS EN LA PROTECCIÓN DE LA INTEGRIDAD
Hashing
»Mismo ejemplo, una cookie:
SHA1(data) => replay attacks
SHA1(key || data) => basado en oscuridad
»Usa HMAC!
» Problema:
granularidad, parámetros configurables
21. FALLOS EN LA PROTECCIÓN DE LA INTEGRIDA
Hashing
<SignatureMethod Algorithm=”…xmldsig#hmac-sha1”>
<HMACOutputLength>
160
</HMACOutputLength>
</SignatureMethod>
Estándar XMLDsig. ¿y por qué
Disponible desde alto nivel.
no existe
únicamente
SHA1?
22. FALLOS EN LA PROTECCIÓN DE LA INTEGRIDA
Dar más información de la cuenta
» plaintext -> HMAC -> relleno -> cifrado CBC
» El servidor devuelve diferentes errores
padding_incorrect
integrity_failure
» ¿CBC? empezamos por el final
bruteforce en el último byte
si es correcto -> integrity_failure por la boca
iteramos hacia atrás muere el pez
23. fallos en
firmas
5
¿no te han dicho que nunca firmes algo sin leerlo primero?
24. Un ejemplo real con una firma RSA
¿dónde está
el error?
1º
Se calcula una simple exponenciación
FALLOS EN FRIMAS
2º
- caso correcto:
verificamos todos los datos (relleno incluído)
- pero Nintendo hace:
return (0 == strncmp(userHash, myHash, 20));
26. Un ejemplo real con una firma RSA
return (0 == memcmp(userHash, myHash, 20));
creamos 256 mensajes ligera-
mente distintos, seguro que al
menos 1 hash empieza por x00.
FALLOS EN FRIMAS
saltarnos la comprobación permite:
1. software, IOS y menu de sistema
2. cambiar gran parte del bootloader
27. La importancia del relleno
» usando RSA directamente permitimos:
sig(a*b) = (siga * sigb) mod n
FALLOS EN FRIMAS
» ataques de parejas de texto plano/cifrado específicas.
» comprobad cada byte, incluyendo el relleno.
» puede ser usado en tu contra para buscar colisiones
en las firmas.
28. fallos usando
6
criptografía de
clave pública
a veces muchos bits nunca serán suficientes
29. FALLOS USANDO CRIPTOGRAFÍA DE CLAVE PÚBLICA
¿DSA conociendo k?
» ¡Debian! Conociendo la salida del PRNG
» Firma DSA (r, s):
r = gk mod p mod q
Revela s = k-1 (H(m) + x*r) mod q
la clave
privada » Con k conocido:
x = ((s*k) – H(m)) * r-1 mod q
» En realidad sólo con parte de los bits de
k sería posible con suficientes firmas
30. FALLOS USANDO CRIPTOGRAFÍA DE CLAVE PÚBLICA
¿Cifrar usando la clave privada?
dos firmas = clave pública
» escenario: RSA, verificar actualizaciones
mantenemos la clave publica secreta
usada para descifrar actualizaciones
» no tiene sentido
no podemos mantener secreta la pública
for e in [3, 5, 7, … 65537]:
n ~= gcd(sig1e–m1, sig2e–m2)
if m1e mod n == sig1:
break
31. FALLOS USANDO CRIPTOGRAFÍA DE CLAVE PÚBLICA
Uso de valores no adecuados
» Son problemas matemáticos que
asumen ciertas condiciones
casos particulares
claves débiles
cambios en algunos parámetros
no permitir que sean cero
exponentes pequeños
32. 7
side channel
la implementación es el enemigo del diseño
33. ¿Side channel has dicho?
» Hasta ahora hemos pensando en:
Servidores remotos
Nuestro ordenador
no
operamos en
una caja negra » Eventos que filtran información:
SIDE CHANNEL
estados de la cache de la CPU
ideal búfer de predicción de saltos
datos en la pila
uso de recursos
planificación
35. ¿Qué más podemos medir?
» Principalmente
temporización
consumo de potencias
radiación EM
» Incluso
SIDE CHANNEL
temperatura
sonidos
patrones de acceso a discos
36. ¿Entonces?
necesitamos ser paranoicos
SIDE CHANNEL
» Veamos un par de ejemplos de ataques
» Pensemos en posibles protecciones
37. Ataque de temporización a RSA
t_producto > t_cuadrado
» Exponenciaciones modulares
s=1;while(y) {
if (y&1)
s = (s*x) mod n; y>>=1;
SIDE CHANNEL
x = (x*x) mod n;
}
» El tiempo de cálculo depende del valor de la clave
38. Ej. Ataque de temporización a RSA
Si combinamos la temporización
con un análisis de potencia consumida.
SIDE CHANNEL
39. Análisis de potencia
» SPA (simple), observar varias medidas
ver el path de ejecución
conocer de antemano el resultado de una comparación
» DPA (diferencial), muchas medidas
SIDE CHANNEL
ataques estadísticos
diferencias de medias
correlaciones
relación entre potencia y datos
41. Inyección de fallos
» provocando un fallo temporal
usando láser
power glitching
clock glitching
SIDE CHANNEL
» podemos:
introducir un fallo en un cálculo
variación el camino de ejecución
42. Ej. Inyección de fallos en RSA-CRT
» Implementación eficiente en memoria
exponenciaciones modulares de modulos más pequeños
recombinación
Con un fallo en la exponenciación
obtenemos una firma una defectuosa
SIDE CHANNEL
» RSA firma un mensaje mediante
s = md (mod n)
43. Ej. Inyección de fallos en RSA-CRT
» Sin embargo RSA-CRT hace:
s1 = mdq (mod q)
s2 = mdp (mod p)
s = a*s1 + b*s2 (mod n) = md (mod n)
a := 0 (mod p), a := 1 (mod q)
b := 1 (mod p), b := 0 (mod q)
SIDE CHANNEL
» Usando una firma defectuosa:
s–s’ = a*s1 – a*s1’ := 0 (mod q)
s–s’ = b*s2 – b*s2’ :≠ 0 (mod p)
44. Ej. Inyección de fallos en RSA-CRT
» Teniendo s1, s2 (defectuosa), pub(e, n):
sage: q = gcd(s1-s2,n)
sage: p = n / q
sage: G1 = IntegerModRing(lcm(q-1,p-1))
SIDE CHANNEL
sage: private_key = G1(e)^-1
sage: G2 = IntegerModRing(n)
sage: message = G2(encrypted)^G2(private_key)
45. ¿Cómo protegernos?
» Muy difícil si programamos en un lenguaje de alto nivel
» Mucho cuidado con optimizaciones de los compiladores
SIDE CHANNEL
» Necesitamos librerías pensadas para resistir este tipo
de ataques
46. Ejemplo
def cmp(s1, s2):
total = 0
for a, b in zip(s1, s2)
total += (a != b)
return not total
SIDE CHANNEL
» ¿y si las cadenas no tienen la misma longitud?
» ¿y si la longitud es 0?
47. Ejemplo
» Analizando la línea total += ( a !=b )
if (a != b)
tmp = 1
else
tmp = 0
SIDE CHANNEL
total += tmp
» Fuga según el tiempo de ejecución
Una línea de código en alto nivel no tiene por qué ser atómica
48. Ejemplo
» Podríamos cambiarlo por
if len(userMsg) != len(correctValue)
return false
result = 0
for x, y in zip(userMsg, correctValue)
result |= ord(x)^ord(y)
return result
SIDE CHANNEL
Usamos xor en lugar de !=
Fuga por tiempo sobre la longitud correcta
49. Ejemplo
» Solución:
si es un valor criptográfico no importa mucho
(teóricamente tiene una longitud conocida)
si es un passwd: calcular el hash antes de comparar
SIDE CHANNEL
» Pero deberíamos seguir dudando de algunos factores:
¿y si x, y negativos?
¿realmente conocemos como funciona internamente zip()?
¿hace previamente una comparación de la longitud?
50. A otros niveles
» Cache timming attack
dependiente de la microarquitectura
ver artículo de djb sobre openssl AES-256
» ¿nos puede jugar una mala pasado el compilador?
optimizaciones fuera
auditar el código compilado
SIDE CHANNEL
» ¿podríamos aplicar estos conceptos via red?
con 1000 medidas podemos detectar
20 ns en LAN
30 ms en WAN
51. Fugas al acceder a claves
» Usa exclusivamente punteros a la claves
SIDE CHANNEL
» Evita estar copiando claves de un sitio a otro
52. Fugas al ejecutar saltos
» Evitar saltos en
decisiones importantes.
» Que ambos caminos:
SIDE CHANNEL
tarden lo mismo
misma estructura de
instrucciones
53. Fugas al comprobar la integridad
» Cuidado al comprobar la paridad
» Podríamos estar revelando la clave
SIDE CHANNEL
» El comportamiento de los algoritmos ha de ser
invariable con los datos procesados
54. Fugas al comprobar la integridad
public static boolean checkParity( byte[]key, int offset){
for (int i = 0; i < DES_KEY_LEN; i++){
byte keyByte = key[i + offset];
int count = 0;
while (keyByte != 0){
// loop till no ‘1’ bits left
if ((keyByte & 0x01) != 0)
count++;
keyByte >>>= 1; // shift right}
SIDE CHANNEL
if ((count & 1) == 0)
return false; // not odd
}
return true; // all bytes were odd
}
55. Fugas al comprobar la integridad
» Si sabemos que:
va de LSB a MSB
SIDE CHANNEL
comprobar que es un “1” tarda más que un “0”
» Podemos obtener la clave bit a bit
56. Fugas al comprobar la integridad
SIDE CHANNEL
¿solución?
usar una tabla de paridad
consultarla en un orden aleatorio
57. Fugas al comprobar la integridad
static byte odd_parity[]= {
// each table entry represents odd parity of index
1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, ..., 248, 248,
251, 251, 253, 253, 254, 254};
public static boolean checkParity( byte[]key, int offset){
int r = random.nextInt() & 7; // random number 0..7
for ( int i=0, j = r; i<8; i++, j = (j+1)&7 ){
if (key[j] != odd_parity[key[j+offset]])
SIDE CHANNEL
return false; // parity incorrect
}
return true; // all bytes were odd
}
58. Fugas cuando accedemos a datos
» Evitad lecturas secuenciales --> usar offset aleatorio
» Ejemplo negativo:
memcpy( pin, buffer, 4 );
SIDE CHANNEL
» Ejemplo más adecuado:
for(int i = 0, j = (rand() & 3);
i < 4; i++, j = ((j+1) & 3))
buffer[j] = pin[j];
59. Fugas al verificar a datos
» Usar strncmp() es un error lógico
pero memcmp es secuencial
if ( strcmp( givenPasswd, storedPasswd ) != 0 )
return -1; // combo fail
SIDE CHANNEL
if ( memcmp( givenPasswd, storedPasswd ) != 0 )
return -1; // time leak fail
» Evitad todo lo secuencial
60. Fugas al verificar a datos
» Posible implementación:
char* c1 = givenPasswd;
char* c2 = storedPasswd;
char error = 0;
for (; *c1 != 0 && *c2 != 0; c1++, c2++ )
error |= *c1 ^ *c2; // collect diff in error
SIDE CHANNEL
if (error | *c1 | *c2) // fail if any not zero
return -1;
return 0;
» Se podría mejorar usando un offset aleatorio.
61. Fugas al sobreescribir datos
escribir un 0 ≠ escribir un 1
» No hacerlo secuencialmente
» No borrar datos sobreescribiendo ceros
SIDE CHANNEL
» CMOS consume en los cambios de estado
» Borrad con un random(), limpiad con ceros después
62. Defendernos de inyecciones de fallos
» Una inyección
de fallos puede
hasta revelar
una clave.
SIDE CHANNEL
» Pueden ser medidas imple-
mentadas en hardware pero
también muchas son lógicas.
63. El caso por defecto
switch (state) {
case STATE_INIT: processInit(apdu); break;
case STATE_PERSO: processPerso(apdu); break;
case STATE_ISSUED: processIssued(apdu); break;
case STATE_LOCKED: processLocked(apdu); break;
default: fail();
}
SIDE CHANNEL
Mismo concepto:
result = no result = yes
if ( equal ) Vs. if ( notequal)
result = yes result = no
64. Fallos en el flujo de ejecución
» Verificaciones del flujo
shadow stack/PC
comprobar el estado después de los saltos
» Uso de valores no triviales
SIDE CHANNEL
los valores booleanos con fácilmente corrompibles
escoger valores con distancia hamming máxima
enmascaramiento en las medidas de potencia
true = 0xc3
false = 0x3c
65. Fallos en los saltos
» No usar booleanos para comprobar saltos
» Comprobar siempre el valor para acceder a esa zona
if (conditionalValue == (byte)0xA5)
// then part
SIDE CHANNEL
else if (conditionalValue == (byte)0xC3)
// else part
66. Fallos en los saltos
» En situaciones decisivas, haced varias veces usan-
do una comparación complementaria:
// within then part
SIDE CHANNEL
if (conditionalValue != (byte)0xA5)
fail();
if (~conditionalValue != (byte)0x5A)
fail();
67. Respuesta a inyección de fallos
static final byte[] CONSTANT = { 0x0F, 0x1E, 0x2D,
0x3C, 0x4B, 0x5A,0x69, 0x78 };
static byte[] copied = new byte[CONSTANT.length];
public void main( Strings[] s ){
System.arraycopy( CONSTANT, 0, copied, 0, CONSTANT.
LENGTH );
SIDE CHANNEL
private void check(){ // call this method repeteadly
for (int i = 0; i < CONSTANT.LENGTH; i++)
if (CONSTANT [i] != copied[i])
fail();
}
68. Respuesta a inyección de fallos
» En caso de:
ataques repetidos
SIDE CHANNEL
datos suficientemente sensibles
» Incluso con el borrado de claves privadas
69. Necesitamos un límite
» Podríamos ser paranoicos hasta el infinito
» Tenemos que plantearnos
SIDE CHANNEL
¿hay acceso físico?
¿hay negocio atacando el sistema?
¿hay otras zonas más débiles?
¿necesito esta inversión en seguridad?
71. Resumen
» La criptografía es frágil.
Cuando falla, falla del todo.
SSL para comunicaciones
» No implementes la tuya propia: GPG para el resto
Remodela tu arquitectura
SIDE CHANNEL
» Si de verdad no puedes usa una librería de alto nivel
cryptlib, keyzcar
» Que el código sea revisado por terceros 10x