elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Recuerda que debes registrarte en el foro para poder participar (preguntar y responder)


+  Foro de elhacker.net
|-+  Programación
| |-+  Desarrollo Web (Moderador: #!drvy)
| | |-+  (Solucionado) Duda implementando "HMAC-based One-time Password Algorithm"
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: (Solucionado) Duda implementando "HMAC-based One-time Password Algorithm"  (Leído 3,211 veces)
AlbertoBSD
Programador y
Moderador Global
***
Desconectado Desconectado

Mensajes: 3.696


🏴 Libertad!!!!!


Ver Perfil WWW
(Solucionado) Duda implementando "HMAC-based One-time Password Algorithm"
« en: 21 Diciembre 2019, 00:02 am »

Bueno, lo pongo aqui por que no se si corresponde mas a seguridad o PHP, pero su implementación ayuda mucho en el desarrollo WEB. Que algun moderador lo mueva a donde sea mas conveniente.

Estoy implementando el algoritmo para HOTP (HMAC-based One-time Password Algorithm) En su modalidad basada en el tiempo de UNIX

Actualemente tengo lo siguiente:

Código
  1. /*
  2. dd if=/dev/urandom of=./key.dat bs=1024 count=1
  3. */
  4. $key = file_get_contents("./key.dat");
  5. $ct = floor(time()/30);
  6. echo hash_hmac("sha1",$ct,$key)."\n";
  7.  

El código actualmente genera un hash distinto cada 30 segundos exactos mi duda es como llegar a los 6 u 8 dígitos que muestra la aplicaciones de Authenticator como la de google.

Nota para los que me digan que no utilize sha1, lo utilize por que wikipedia indica que es el default, pero espero que pueda ser configurable para utilizar sha256 y alguno de la misma familia.

https://en.wikipedia.org/wiki/HMAC-based_One-time_Password_algorithm

Saludos.



Solucionado gracias a las observaciones de MinusFour.

La funcion quedo de la siguiente manera:

Código
  1. function hotp($key,$c) {
  2. $mask = ['f' => '7','e' => '6','d' => '5','c' => '4','b' => '3','a' => '2','9' => '1','8' => '0'];
  3. $hash_value_str = hash_hmac("sha1",pack("H*",sprintf("%016x",$c)),$key);
  4. $offset = intval($hash_value_str[39],16)*2;
  5. $extract = substr($hash_value_str,$offset,8);
  6. if(isset($mask[$extract[0]])) {
  7. $extract[0] = $mask[$extract[0]];
  8. }
  9. $value = intval($extract,16);
  10. return ($value % 1000000);
  11. }

Y para utilizarlo solo basta llamarla:

Código
  1. $ct = floor(time()/30);
  2. $key = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
  3. printf("%06d\n",hotp($key,$ct));
  4.  

Esto arroja los 6 dígitos que deben de coincidir con la aplicación de Autenticador que tengamos instalada en el móvil.

Para este ejemplo se puede configurar manualmente como se muestra en una las imágenes.



« Última modificación: 23 Diciembre 2019, 00:17 am por AlbertoBSD » En línea

MinusFour
Moderador Global
***
Desconectado Desconectado

Mensajes: 5.529


I'm fourth.


Ver Perfil WWW
Re: Duda implementando "HMAC-based One-time Password Algorithm"
« Respuesta #1 en: 21 Diciembre 2019, 01:06 am »

Necesitas truncar el HMAC, como dice el RFC. Necesitas extraer 31 bits del HMAC. Lo que dice el RFC es que primero obtengas el offset del cual vas a agarrar los 31 bits. Este offset, son los últimos 4 bits del HMAC. El número que obtengas lo multiplicas por 8 para obtener el inicio del offset. Para obtener el final del offset, solo le sumas 32. De ese offset, solo tomas los 31 bits menos significativos (no tomas el bit más significativo). Lo último es tomar los últimos d digitos de la representación numérica de esos 31 bits. Es un número entre 0 y 2147483648.

Este es un ejemplo HMAC sacado del RFC:

Código:
   -------------------------------------------------------------
   | Byte Number                                               |
   -------------------------------------------------------------
   |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|
   -------------------------------------------------------------
   | Byte Value                                                |
   -------------------------------------------------------------
   |1f|86|98|69|0e|02|ca|16|61|85|50|ef|7f|19|da|8e|94|5b|55|5a|
   -------------------------------***********----------------++|

Los últimos 4 bits son: "A"(10). Entonces, tomas los bits de 80 a 112, que son:

Código:
50 EF 7F 19

El primer bit es 0 así que no importa, pero aquí le aplicas una mascara al bit más significativo (50 & 7F = 50). Este es tu número de 31 bits:

Código:
1357872921

En el RFC usan de ejemplo que solo toman 6 digitos. Usan modulo con una potencia de 10 para truncar el número.

Código:
We then take this number modulo 1,000,000 (10^6) to generate the 6-
   digit HOTP value 872921 decimal.

Y ya esta, ese es tu HOTP:

https://tools.ietf.org/html/rfc4226


En línea

AlbertoBSD
Programador y
Moderador Global
***
Desconectado Desconectado

Mensajes: 3.696


🏴 Libertad!!!!!


Ver Perfil WWW
Re: Duda implementando "HMAC-based One-time Password Algorithm"
« Respuesta #2 en: 21 Diciembre 2019, 04:22 am »

 ;-) ;-) ;-) Muchas gracias.

He hecho una implementacion algo vaga (Ahorita no esta optimizada ni nada) y aunque llego al mismo resultado del ejemplo que muestras si meto el hash indicado del ejemplo, y si, produce el Valor de 6 dígitos del ejemplo.

Código:
$hash_value_str = "1f8698690e02ca16618550ef7f19da8e945b555a";

Código
  1. <?php
  2. /* dd if=/dev/urandom of=./key.dat bs=1024 count=1*/
  3. include_once("Base2n.php");
  4. $base32 = new Base2n(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', FALSE, TRUE, TRUE);
  5. $mask = ['f' => '7',
  6. 'e' => '6',
  7. 'd' => '5',
  8. 'c' => '4',
  9. 'b' => '3',
  10. 'a' => '2',
  11. '9' => '1',
  12. '8' => '0'];
  13. //$key = file_get_contents("./key.dat");
  14. $key = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
  15. $ct = floor(time()/30);
  16. $hash_value_str = hash_hmac("sha1",$ct,$key);
  17. //$hash_value_str = "1f8698690e02ca16618550ef7f19da8e945b555a";
  18. echo "$hash_value_str\n";
  19. echo strlen($hash_value_str)."\n";
  20. echo $hash_value_str[39]."\n";
  21. $offset = intval($hash_value_str[39],16)*2;
  22. echo "offset: $offset\n";
  23. $extract = substr($hash_value_str,$offset,8);
  24. echo "extract: $extract\n";
  25. if(isset($mask[$extract[0]])) {
  26. $extract[0] = $mask[$extract[0]];
  27. }
  28. echo "extract: $extract\n";
  29. $value = intval($extract,16);
  30. echo "Valor: $value\n";
  31. echo "Digtos: ". ($value % 1000000) ."\n";
  32. echo $base32->encode($key) ."\n";
  33. ?>
  34.  

El detalle es que paso la salida en Base32 del valor $key a la aplicación del Google Autenticador y los números que ahí produce no coinciden con los que arroja el programa cuando metod los valores de $key y el valor $ct en  base a time()/30.

Voy a ver que puedo estar haciendo mal. Tome una implementacion de base32 que se encuentra en el siguiente link, por si alguien lo quiere probar.

https://github.com/ademarre/binary-to-text-php/blob/master/Base2n.php

Saludos!

En línea

MinusFour
Moderador Global
***
Desconectado Desconectado

Mensajes: 5.529


I'm fourth.


Ver Perfil WWW
Re: Duda implementando "HMAC-based One-time Password Algorithm"
« Respuesta #3 en: 21 Diciembre 2019, 05:42 am »

La llave que se hashea me imagino que debe ser en su forma base32. El secreto que se comparte debe ser el mismo.
En línea

AlbertoBSD
Programador y
Moderador Global
***
Desconectado Desconectado

Mensajes: 3.696


🏴 Libertad!!!!!


Ver Perfil WWW
Re: Duda implementando "HMAC-based One-time Password Algorithm"
« Respuesta #4 en: 22 Diciembre 2019, 04:13 am »

Te refieres a algo como utilizar el valor en formato base32 para realizar el hasheo y demas?

He modificado la funcion para poder testeaar varios valores al mismo tiempo:

Código
  1. function hotp($key,$c) {
  2. $mask = ['f' => '7','e' => '6','d' => '5','c' => '4','b' => '3','a' => '2','9' => '1','8' => '0'];
  3. echo "key: $key\n";
  4. echo "c: $c\n";
  5. $hash_value_str = hash_hmac("sha1",$c,$key);
  6. echo "hash: $hash_value_str\n";
  7. echo "4bits (hex): ".$hash_value_str[39]."\n";
  8. $offset = intval($hash_value_str[39],16)*2;
  9. $extract = substr($hash_value_str,$offset,8);
  10. echo "extract: $extract\n";
  11. if(isset($mask[$extract[0]])) {
  12. echo $extract[0]."& 7 =";
  13. $extract[0] = $mask[$extract[0]];
  14. echo $extract[0]."\n";
  15. echo "new extract: $extract\n";
  16. }
  17. $value = intval($extract,16);
  18. echo "Value: $value\n";
  19. return ($value % 1000000);
  20. }

Y ahora solo tengo que hacer:

Código
  1. $base32 = new Base2n(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', FALSE, TRUE, TRUE);
  2. $ct = floor(time()/30);
  3. $key = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
  4. echo "Key in base32: ".$base32->encode($key)."\n";
  5.  
  6. printf("%06d\n",hotp($key,$ct));
  7. printf("%06d\n",hotp($base32->encode($key),$ct));
  8.  

Tiene muchos Echo la funcion que se pueden comentar, estan ahi para de momento validar que la funcion esta haciendo bien  los pasos del algoritmo.

Alguna salida es:

Código:
Key in base32: IFAUCQKBIFAUCQKBIFAUCQKBIFAUCQKBIFAUCQKBIFAUCQKBIFAQ====
key: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
c: 52566234
hash: 428fc537d16c867ee7e1b4b3cc432c7e288d5769
4bits (hex): 9
extract: e1b4b3cc
e& 7 =6
new extract: 61b4b3cc
Value: 1639232460
232460
key: IFAUCQKBIFAUCQKBIFAUCQKBIFAUCQKBIFAUCQKBIFAUCQKBIFAQ====
c: 52566234
hash: a49ac9470db4a542a36b42d28b7e089e11123f8a
4bits (hex): a
extract: 42d28b7e
Value: 1121094526
094526

Ahora como comentas he probado la llave en su formas de AAAAA y en sus forma en base32.

El google Authenticator lo configuro de la siguiente manera:

Primero agrego la llave en su formato base32


Y obtienes los valores



Pero no coincide con lo que arroja el programa, tengo la duda de saber si el valor $C es correcto, lo obtengo del time/30:

Código
  1. $ct = floor(time()/30);

Saludos!

MOD: Imagenes adaptadas a lo permitido.
« Última modificación: 22 Diciembre 2019, 20:22 pm por MCKSys Argentina » En línea

animanegra

Desconectado Desconectado

Mensajes: 287



Ver Perfil
Re: Duda implementando "HMAC-based One-time Password Algorithm"
« Respuesta #5 en: 22 Diciembre 2019, 12:54 pm »

Yo lo implementé ya en php y en html/javascript para después con cordoba portarlo a android. La version apk, lee los QRs y obtiene el TOTP de forma standar con md5 sha1 sha3 etc....
En el caso de google authenticartor, en el qr no viene el method pero es sha1. Yo en mi cliente tengo puesto el md5 por defecto.
La parte del servidor pues eso, genera el TOTP para depsués utilizarlo.

Creo que igual te puede ayudar, no lo se. Si tienes problemas echa un vistazo...

https://github.com/4nimanegra/OTPanimanegra

Está aun a medias (no es producto, es muy feo y tal), pero funcional. Testeado por ejemplo con protonmail. Algun dia, lo terminare porque no le queda nada, o no. ^^

Ahora dudo si la ultima version estaba en el github... :\ Le hecho un vistazo y si no la subo a mas tardar para mañana.
« Última modificación: 22 Diciembre 2019, 12:56 pm por animanegra » En línea


42
No contesto mensajes por privado, si tienes alguna pregunta, consulta o petición plantéala en el foro para que se aproveche toda la comunidad.
AlbertoBSD
Programador y
Moderador Global
***
Desconectado Desconectado

Mensajes: 3.696


🏴 Libertad!!!!!


Ver Perfil WWW
Re: Duda implementando "HMAC-based One-time Password Algorithm"
« Respuesta #6 en: 22 Diciembre 2019, 13:40 pm »

Que tal gracias por responder, acabo de ver el link en tu pagina de github y el código que muestras solo extrae los primeros 6 caracteres del hash devuelto por hash_hmac sin importar si son letras o no.

Creo que no esta actualizado.

Sobre lo que comentas del tipo de hash pense que era solo con sha1 pero me da gusto que sea configurable por aquello de las colisiones en sha1 y en md5 yo prefiero usar sha256, pero realmente con el limite de 30 segundos la mayoría de los hash son validos.

Saludos
« Última modificación: 22 Diciembre 2019, 23:12 pm por AlbertoBSD » En línea

MinusFour
Moderador Global
***
Desconectado Desconectado

Mensajes: 5.529


I'm fourth.


Ver Perfil WWW
Re: Duda implementando "HMAC-based One-time Password Algorithm"
« Respuesta #7 en: 22 Diciembre 2019, 16:54 pm »

Creo que el culpable aquí es diferentes metodos de hash_hmac. Tu contador parece estar correcto, lo único que pudiera ser en ese aspecto es que los relojes no estén sincronizados.

Ahora, yo creo que el formato es el verdadero problema. En el RFC de TOTP ellos usan bytes para el secreto y el mensaje. Por ejemplo, si el secreto en ASCII es '12345678901234567890', el hexadecimal de este es:

Código:
31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30

Y por ejemplo, si el contador fuera 1, lo que ellos hacen es hacer 0 padding hasta tener 8 bytes.

Código:
00 00 00 00 00 00 00 01

En el ejemplo que ponen en su RFC, ellos usan Java y convierten el string hexadecimal a bytes. Para este preciso ejemplo, con Sha-1 y el secreto que mencione, ellos obtienen:

Código:

  +-------------+--------------+------------------+----------+--------+
  |  Time (sec) |   UTC Time   | Value of T (hex) |   TOTP   |  Mode  |
  +-------------+--------------+------------------+----------+--------+
  |      59     |  1970-01-01  | 0000000000000001 | 94287082 |  SHA1  |
  |             |   00:00:59   |                  |          |        |
  +-------------+--------------+------------------+----------+--------+

Y simplemente, no doy con ese TOTP sin importar que valores use en el hmac. Intente con strings en hexadecimal y ASCII (con pack). Simplemente, no obtengo esos números. Lo voy a probar con node, porque la libreria crypto en node si me deja usar buffers.

El RFC de TOTP por si te sirve:

https://tools.ietf.org/html/rfc6238

Edit: Ok, vale... pude hacerlo funcionar ya. Tanto el secreto como el contador parece que hash_hmac los toma como ASCII. No puedes enviar un entero como mensaje. Me imagino que PHP simplemente lo convierte a ASCII (1 vendría siendo 31 en hex). El contador también tiene que tener padding hasta que tengas 8 bytes. Un string en hexadecimal necesitaría tener 16 caracteres. Tomas el contador, lo conviertes a un string en hexadecimal y le agregas 0 hasta tener 16 caracteres.

Ejemplo:

3167928 es 3056B8 en hexadecimal. Le necesitas agregar otros 5 bytes (10 caracteres).

Código:
00000000003056B8

Y usas pack a ese string:

Código
  1. pack('H*', '00000000003056B8');

Los 0s son importantes...

En cuanto a tu secreto, es necesario que conviertas de Base32 a ASCII.
« Última modificación: 22 Diciembre 2019, 18:53 pm por MinusFour » En línea

AlbertoBSD
Programador y
Moderador Global
***
Desconectado Desconectado

Mensajes: 3.696


🏴 Libertad!!!!!


Ver Perfil WWW
Re: Duda implementando "HMAC-based One-time Password Algorithm"
« Respuesta #8 en: 23 Diciembre 2019, 00:03 am »

Excelente ya funciono, fijate que es las pocas cosas que no me gusta de PHP cuando quieres que un valor se mantenga en su forma (int), se toma las libertades de usalo como string y viceversa.

Muchas gracia por tomarte la molestia y hecharle un ojo.  ;-) ;-) ;-)

La función quedo de la siguiente manera:

Código
  1. function hotp($key,$c) {
  2. $mask = ['f' => '7','e' => '6','d' => '5','c' => '4','b' => '3','a' => '2','9' => '1','8' => '0'];
  3. $hash_value_str = hash_hmac("sha1",pack("H*",sprintf("%016x",$c)),$key);
  4. $offset = intval($hash_value_str[39],16)*2;
  5. $extract = substr($hash_value_str,$offset,8);
  6. if(isset($mask[$extract[0]])) {
  7. $extract[0] = $mask[$extract[0]];
  8. }
  9. $value = intval($extract,16);
  10. return ($value % 1000000);
  11. }

Y los valores ya coinciden con los que arrojan las aplicaciones de authenticator.

Saludos
En línea

Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines