od, y la importancia de los saltos de línea

Cuando uno se dedica al negocio de la computación, a veces se nos olvida que lo que nosotros vemos no es exactamente lo que las computadoras procesan. Lo que para nosotros son frases, imágenes o sonido, las computadoras interpretan como señales: análogas (como cuando hablamos por micrófono) y digitales (casi todo lo demás), también llamadas discretas.

Este punto salió a colación hace apenas unos minutos, mientras trabajaba en una rutina de firmado. Para quienes no lo conozcan, el proceso de firmado digital funciona de la siguiente manera:

– Obtener los datos a firmar
– Calcular una suma (también conocido como hash) de los datos — un valor único y (supuestamente) irrepetible que identifica dichos datos.
– Cifrar esta suma obtenida, con alguna llave privada.

El proceso es probablemente mejor explicado por Wikipedia, pero el concepto es sencillo. Particularmente para aquellos que entienden criptografía de llave pública… pero creo que me desvío del tema. Ligeramente.

El problema de este tipo de firmados es que las sumas son extremadamente sensibles a cambios en los datos. Para ejemplo, vean los resultados de dos cadenas ligeramente diferentes. La cadena “hola”, calculando su MD5 (un tipo de suma), nos entrega:

916f4c31aaa35d6b867dae9a7f54270d

La cadena “hola.” (nótese: idéntica, excepto por el punto al final) nos entrega un MD5 completamente diferente:

296366cea89e11cdc7999e42463575fe

De hecho, esta es la (supuesta y desde hace años demostrativamente falsa, en algunos casos) fortaleza de las sumas de seguridad. La idea que una combinación de datos diferente nos entregue, siempre, necesariamente, resultados diferentes, es la raison d’être de las sumas de seguridad.

Bueno, pues todo suena fabuloso, excepto cuando no te sale. Resulta que el proceso de firmado que estabamos realizando (un proyecto del cual hablaré más después) no estaba funcionando como debe. Y después de varios días de estarme peleando mentalmente con el problema (‘debe ser un problema con el algoritmo’, pensaba) me decidí a adentrarme en el problema a fondo.

Revisé el RFC respectivo; busqué soluciones alternativas; visité blogs extraños en busca de respuestas. Nadie supo darme razón de lo ocurrido, aunque para ser franco no *consulté* con nadie, más bien busqué y no encontré. De pronto, y por pura suerte, encontré una alusión al estándar de codificación (UTF-8, en caso de que alguien esté interesado), y eso me hizo pensar — los estándares de codificación solo son diferentes representaciones binarias de lo mismo. Binarias… binarias…

Seguramente algún problema había con la representación binaria. Si cambiamos una letra, las sumas cambias – es evidente, porque cambiamos el bloque de bits. Una codificación diferente seguramente causaría un efecto desastroso en las sumas. Y me dí a la tarea de investigar como buscar la representación binaria de algo, en linux (siendo este mi plataforma de preferencia para lo que estamos haciendo).

Me topé con un viejo amigo: hexdump. Este comando permite la salida hexadecimal de información. Nada del otro mundo, y aparentemente no encontré nada. Pero persistió la búsqueda. Me fuí a topar con este post (casualmente, una mezcla de problemas con MD5 y visualización binaria). Y discuten un punto sencillo: la salida de una función de cálculo MD5 y la salida del comando clásico de linux, md5sum, no son las mismas. Pero… ¿porque?

Un vistazo con od (un comando de salida octal, así como de otros formatos – según su manpage) dió la respuesta:

io@maquina:~$ echo “hola” | od -c
0000000 h o l a n
0000005

Nótese el molesto, maligno y nada bien recibido salto de línea, al final de “hola”. Yo no lo puse. ¿Quién diablos se atreve a modificar mi cadena?

El sistema, aparentemente. Linux es muy servicial, y se dispone a agregar saltos de línea a cualquier “echo” que yo realice. De hecho, debo especificar explícitamente que no se den estos saltos. Y eso hace, por supuesto, toda la diferencia a la hora de calcular una suma – esos bits adicionales para señalizar el salto de línea son tomados en cuenta para el cálculo.

Basta comparar las salidas:

io@maquina:~$ echo “hola” | md5sum
916f4c31aaa35d6b867dae9a7f54270d –
io@maquina:~$ echo -n “hola” | md5sum
4d186321c1a7f0f354b297e8914ab240 –

Donde, por supuesto, “echo -n” especifica a echo que NO incluya un salto de línea al final.

Regreso a mis actividades, por el momento. Pero el día de hoy no se me olvida… y en el futuro, tendré más cuidado con estas salidas. Detalles que uno va aprendiendo poco a poco… 😉