Acerca de las implementaciones de c贸digo abierto de la funci贸n hash GOST R 34.11-2012 y su impacto en la firma electr贸nica de GOST R 34.10-2012

En un momento, la implementaci贸n de algoritmos criptogr谩ficos dom茅sticos en la biblioteca libgcrypt me inspir贸 mucho. Se hizo posible usar estos algoritmos en Kleopatra y en Kmail y GnuPg en general, para considerar la biblioteca libgcrypt como una alternativa a openssl con el motor GOST. Y todo estuvo genial hasta el viernes pasado.

Me pidieron que verificara la firma electr贸nica de GOST R 34.10-2012-256 para un documento creado en Microsoft Office en MS Windows. Y decid铆 comprobarlo en Kleopatra (tengo Linux). Y qu茅 piensas, la firma result贸 ser incorrecta. Las dudas surgieron. Decid铆 comprobar en OpenSSL con el motor GOST . La firma ha sido verificada con 茅xito. Re-firm贸 urgentemente el archivo en Kleopatra y no pas贸 la verificaci贸n en MS Windows. Intentamos firmar y verificar otros archivos, todo estaba bien. La pregunta era 驴cu谩l es el problema? Dado que el hash del documento est谩 involucrado en la firma, se decidi贸 verificar el c谩lculo del hash por diferentes programas. En primer lugar, estaban involucradas implementaciones de c贸digo abierto para stribog :


Y luego hubo un shock! El hash calculado por la "famosa implementaci贸n de Degtyarev" coincidi贸 con el hash calculado en openssl con el GOST del endine, pero no coincidi贸 con el valor de hash calculado usando libgcrypt y libressl.

C贸mo ru_crypt ten铆a raz贸n cuando escribi贸 al principio de su art铆culo :
Inmediatamente le advierto que no verifiqu茅 la correcci贸n de las implementaciones.

Por cierto, el est谩ndar en GOST R 34.10-2012 tambi茅n dice que los ejemplos de control son solo de referencia. Debe entenderse claramente que los casos de prueba no garantizan que diferentes implementaciones den el mismo resultado para todas las ocasiones.

Para calcular los valores hash, se utilizaron las siguientes utilidades:

1) openssl

$ openssl dgst [鈥搈d_gost12_256|-md_gost12_512] <file> 

2) libressl

 $libressl dgst [鈥搒treebog256|streebog512] <file> 

3) libgcrypt

 $gchash [stribog256|stribog512] <file> 

4) La famosa implementaci贸n de Degtyarev

 $gost3411-2012 [-2|-5] <file> 

Aqu铆 tambi茅n hay algo interesante: en la transcripci贸n latina, los stribogs escriben stribog o streebog. Ser铆a bueno llegar a la uniformidad. Y entonces parece que estas son funciones diferentes. Personalmente, prefiero la primera opci贸n: stribog.

Necesitaba un 谩rbitro.

Como 谩rbitro, se decidi贸 utilizar el token PKCS # 11 RUTOKEN EDS-2.0, que admite los est谩ndares criptogr谩ficos rusos GOST R 34.10-2012, GOST R 34.11-2012, VKO GOST R 34.10-2012 (RFC 7836) con una longitud de clave de 256 y 512 bits y est谩 certificado por el FSB de Rusia como un medio de protecci贸n de informaci贸n criptogr谩fica (CPSI) y un medio de firma electr贸nica.

Adem谩s, el token RUTOKEN EDS-2.0 est谩 ampliamente distribuido y muchos certificados de tienda en 茅l para acceder a los servicios estatales y otros portales.
Para calcular el valor hash en el token, utilizaremos el script test_digest.tcl en Tcl :

test_digest.tcl
 #! /usr/bin/env tclsh package require pki lappend auto_path . package require pki::pkcs11 #     PKCS#11 set pkcs11_module "/usr/local/lib64/librtpkcs11ecp_2.0.so" #set pkcs11_module "/usr/local/lib64/libls11sw2016.so" puts "Connect the Token and press Enter" gets stdin yes set handle [pki::pkcs11::loadmodule $pkcs11_module] set slots [pki::pkcs11::listslots $handle] foreach slotinfo $slots { set slotid [lindex $slotinfo 0] set slotlabel [lindex $slotinfo 1] set slotflags [lindex $slotinfo 2] if {[lsearch -exact $slotflags TOKEN_PRESENT] != -1} { set token_slotlabel $slotlabel set token_slotid $slotid #    break } } proc usage {use error} { puts "Copyright(C) Orlov Vladimir (http://soft.lissi.ru) 2019" if {$use == 1} { puts $error puts "Usage:\ndigest <stribog256|stribog512> <file for digest>\n" } } set countcert [llength $argv] if { $countcert != 2 } { usage 1 "Bad usage!" exit } set digest_algo [lindex $argv 0] if {$digest_algo != "stribog256" && $digest_algo != "stribog512"} { usage 1 "Bad usage!" exit } set file [lindex $argv 1] if {![file exists $file]} { usage 1 "File $file not exist" exit } puts "Loading file for digest: $file" set fd [open $file] chan configure $fd -translation binary set cert_user [read $fd] close $fd if {$cert_user == "" } { usage 1 "Bad file: $file" exit } set aa [dict create pkcs11_handle $handle pkcs11_slotid $token_slotid] set digest_hex [pki::pkcs11::digest $digest_algo $cert_user $aa] puts "digest_hex=\n$digest_hex" exit 


驴Cu谩ndo aparece esta discrepancia en la implementaci贸n? Hasta ahora, ha sido posible determinar que esta discrepancia se produce al calcular el hash de los archivos doc creados en MS Office. Adem谩s, el hash de los primeros 143 bytes se considera igual, y cuando se calcula el hash de 144 bytes, los valores son diferentes.

Los primeros 143 bytes en hexadecimal se ven as铆:

 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000002400000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 

Gu谩rdelos en el archivo Doc1_143_hex.txt.

Los primeros 144 bytes en hexadecimal se ven as铆:

 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000002400000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 

Gu谩rdelos en el archivo Doc1_144_hex.txt.

Es conveniente usar el script hex2bin.tcl para traducir de hexadecimal a binario:

 #!/usr/bin/tclsh proc usage {use error} { if {$use == 1} { puts $error puts "Usage:\nhex2bin <file with hex> <file for bin>\n" } } set countcert [llength $argv] if { $countcert != 2 } { usage 1 "Bad usage!" exit } set file [lindex $argv 0] if {![file exists $file]} { usage 1 "File $file not exist" exit } set fd [open $file] chan configure $fd -translation binary set cert_user [read $fd] close $fd if {$cert_user == "" } { usage 1 "Bad file with hex: $file" exit } set cert_user [binary format H* $cert_user] set fd [open [lindex $argv 1] w] chan configure $fd -translation binary puts -nonewline $fd $cert_user close $fd 

Convierta el c贸digo hexadecimal a binario:
 $./hex2bin Doc1_143_hex.txt Doc1_143.bin $./hex2bin Doc1_144_hex.txt Doc1_144.bin $ 

Ahora puede verificar c贸mo se calcula el hash mediante diversas implementaciones:
Primero, considere el hash para el archivo Doc1_143, bin:

 $ ./openssl dgst -md_gost12_256 Doc1_143.bin md_gost12_256(Doc1_143.bin)= e63bd3edc44f9a03fece4198b690a8ae291b973ae61b2a0f512a9a7479431a63 $ ./libressl dgst -streebog256 Doc1_143.bin streebog256(Doc1_143.bin)= e63bd3edc44f9a03fece4198b690a8ae291b973ae61b2a0f512a9a7479431a63 $ ./gchash stribog256 Doc1_143.bin e63bd3edc44f9a03fece4198b690a8ae291b973ae61b2a0f512a9a7479431a63 Doc1_143.bin $ ./gost3411-2012 -2 Doc1_143.bin GOST R 34.11-2012 (Doc1_143.bin) = e63bd3edc44f9a03fece4198b690a8ae291b973ae61b2a0f512a9a7479431a63 $ 

Ha llegado el momento m谩s importante, el momento de la verificaci贸n en un sistema de protecci贸n de informaci贸n criptogr谩fica certificado:

 $ ./test_digest.tcl stribog256 Doc1_143.bin Connect the Token and press Enter Loading file for digest: Doc1_143.bin digest_hex= e63bd3edc44f9a03fece4198b690a8ae291b973ae61b2a0f512a9a7479431a63 $ 

Como puede ver, todo termin贸 para siempre.

Veamos qu茅 sucede con el archivo Doc1_144.bin:

 $ ./openssl dgst -md_gost12_256 Doc1_144.bin md_gost12_256(Doc1_144.bin)= c766085540caaa8953bfcf7a1ba220619cee50d65dc242f82f23ba4b180b18e0 $ ./libressl dgst -streebog256 Doc1_144.bin streebog256(Doc1_144.bin)= 3965c99777eb1b64c783496fe950aa6540bc7baa399a3889995145afbdd76250 $ 

Eso es todo, los valores de los hashes no coinciden. Para la pureza del experimento, verificamos las implementaciones restantes:

 $ ./gchash_1.7.10 stribog256 Doc1_144.bin 3965c99777eb1b64c783496fe950aa6540bc7baa399a3889995145afbdd76250 Doc1_144.bin $ ./gost3411-2012 -2 Doc1_144.bin GOST R 34.11-2012 (Doc1_144.bin) = c766085540caaa8953bfcf7a1ba220619cee50d65dc242f82f23ba4b180b18e0 $ ./test_digest.tcl stribog256 Doc1_144.bin Connect the Token and press Enter Loading file for digest: Doc1_144.bin digest_hex= c766085540caaa8953bfcf7a1ba220619cee50d65dc242f82f23ba4b180b18e0 $ 

El hash calculado por la "famosa implementaci贸n de Degtyarev" coincide con el hash calculado en openssl con el motor GOST, pero no coincide con el valor de hash calculado usando libgcrypt y libressl.

Obtenemos un resultado similar si consideramos el hash stribog512.

Hay una conclusi贸n Si desea que la firma electr贸nica GOST R 34.10-2012 generada por libressl y libgcrypt (u otros) sea compatible con openssl y, lo m谩s importante, con la protecci贸n de la informaci贸n criptogr谩fica certificada en el sistema de certificaci贸n FSB de Rusia, use verificado implementaciones para calcular hashes. Espero que esta publicaci贸n evite muchos malentendidos, y los autores de la implementaci贸n de stribog en libressl, libgrypt y posiblemente otros ayudar谩n a eliminar estas discrepancias. Hoy, debo admitir que, en los productos anteriores, en realidad no se implementa GOST R 34.10-2012, sino algo m谩s. Este es un algoritmo diferente. El ejemplo de prueba dado probablemente ser铆a bueno incluirlo como ejemplo de prueba para GOST R 34.10-2012. Y voy a editar libgcrypt para Kleopatra y KMail. La leyenda de Cleopart y la criptograf铆a rusa estaba inacabada.

PD: El art铆culo ya estaba listo cuando mi colega dijo que la discrepancia entre las implementaciones aparece cuando se encuentra una secuencia suficientemente larga de 0xFF. Ella, esta secuencia, por cierto, est谩 presente al comienzo de los archivos doc de MS Office. Lo comprob茅 tal como est谩. El archivo conten铆a 189 bytes.

Source: https://habr.com/ru/post/450024/


All Articles