À propos des implémentations open source de la fonction de hachage GOST R 34.11-2012 et leur impact sur la signature électronique de GOST R 34.10-2012

À un moment donné, l'implémentation d'algorithmes cryptographiques nationaux dans la bibliothèque libgcrypt m'a beaucoup inspiré. Il est devenu possible d'utiliser ces algorithmes dans Kleopatra et dans Kmail et GnuPg en général, pour considérer la bibliothèque libgcrypt comme une alternative à openssl avec le moteur GOST. Et tout était super jusqu'à vendredi dernier.

On m'a demandé de vérifier la signature électronique de GOST R 34.10-2012-256 pour un document créé dans Microsoft Office sur MS Windows. Et j'ai décidé de le vérifier dans Kleopatra (j'ai Linux). Et qu'en pensez-vous, la signature s'est avérée fausse. Des doutes se sont glissés. J'ai décidé de vérifier sur openssl avec le moteur GOST . La signature a été vérifiée avec succès. Re-signé d'urgence le fichier dans Kleopatra et il n'a pas réussi le contrôle sur MS Windows. Nous avons essayé de signer et de vérifier d'autres fichiers, tout allait bien. La question était quel est le problème? Le hachage du document étant impliqué dans la signature, il a été décidé de vérifier le calcul du hachage par différents programmes. Tout d'abord, des implémentations open-source pour stribog ont été impliquées :


Et puis il y a eu un choc! Le hachage calculé par la "célèbre implémentation de Degtyarev" coïncidait avec le hachage calculé en openssl avec le GOST endine, mais ne correspondait pas à la valeur de hachage calculée à l'aide de libgcrypt et libressl.

Comment ru_crypt avait raison quand il a écrit au début de son article :
Je vous préviens immédiatement que je n'ai pas vérifié l'exactitude des implémentations.

Soit dit en passant, la norme GOST R 34.10-2012 indique également que les exemples de contrôle sont fournis à titre indicatif uniquement. Il faut bien comprendre que les cas de test ne garantissent pas que différentes implémentations donnent le même résultat pour toutes les occasions.

Pour calculer les valeurs de hachage, les utilitaires suivants ont été utilisés:

1) openssl

$ openssl dgst [–md_gost12_256|-md_gost12_512] <file> 

2) libressl

 $libressl dgst [–streebog256|streebog512] <file> 

3) libgcrypt

 $gchash [stribog256|stribog512] <file> 

4) La fameuse implémentation de Degtyarev

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

Voici aussi une chose intéressante: dans la transcription latine, les stribogs écrivent soit stribog soit streebog. Ce serait bien de parvenir à l'uniformité. Et il semble donc que ce sont des fonctions différentes. Personnellement, je préfère la première option - stribog.

J'avais besoin d'un arbitre.

En tant qu'arbitre, il a été décidé d'utiliser le jeton PKCS # 11 RUTOKEN EDS-2.0, qui prend en charge les normes cryptographiques russes GOST R 34.10-2012, GOST R 34.11-2012, VKO GOST R 34.10-2012 (RFC 7836) avec une longueur de clé de 256 et 512 bits. et est certifié par le FSB de Russie comme moyen de protection des informations cryptographiques (CPSI) et moyen de signature électronique.

De plus, le jeton RUTOKEN EDS-2.0 est largement distribué et de nombreux certificats de magasins y sont stockés pour l'accès aux services de l' État et à d'autres portails.
Pour calculer la valeur de hachage sur le jeton, nous utiliserons le script test_digest.tcl dans 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 


Quand cette différence de mise en œuvre apparaît-elle? Jusqu'à présent, il a été possible de déterminer que cette différence se produit lors du calcul du hachage des fichiers doc créés dans MS Office. De plus, le hachage des 143 premiers octets est considéré comme le même, et lors du calcul du hachage à partir de 144 octets, les valeurs sont différentes.

Les 143 premiers octets en hexadécimal ressemblent à ceci:

 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000002400000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 

Enregistrez-les dans le fichier Doc1_143_hex.txt.

Les 144 premiers octets en hexadécimal ressemblent à ceci:

 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000002400000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 

Enregistrez-les dans le fichier Doc1_144_hex.txt.

Il est pratique d'utiliser le script hex2bin.tcl pour traduire de la forme hexadécimale en forme binaire:

 #!/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 

Convertissez le code hexadécimal en binaire:
 $./hex2bin Doc1_143_hex.txt Doc1_143.bin $./hex2bin Doc1_144_hex.txt Doc1_144.bin $ 

Vous pouvez maintenant vérifier comment le hachage est calculé par différentes implémentations:
Tout d'abord, considérez le hachage du fichier 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 $ 

Le moment le plus important est venu, le moment de la vérification sur un système de protection des informations cryptographiques certifié:

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

Comme vous pouvez le voir, tout s'est terminé pour de bon.

Voyons ce qui se passe pour le fichier 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 $ 

Voilà, les valeurs des hachages ne correspondent pas. Pour la pureté de l'expérience, nous vérifions les implémentations 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 $ 

Le hachage calculé par la "célèbre implémentation de Degtyarev" correspond au hachage calculé dans openssl avec le moteur GOST, mais ne correspond pas à la valeur de hachage calculée à l'aide de libgcrypt et libressl.

Nous obtenons un résultat similaire si nous considérons le hachage stribog512.

Il y a une conclusion. Si vous souhaitez que la signature électronique de GOST R 34.10-2012, générée par libressl et libgcrypt (ou peut-être d'autres), soit compatible avec la mise en œuvre openssl et, surtout, avec la mise en œuvre dans le CIPF certifié dans le système de certification FSB de Russie, utilisez vérifié implémentations pour le calcul des hachages. J'espère que cette publication évite de nombreux malentendus, et les auteurs de la mise en œuvre de stribog dans libressl, libgrypt et peut-être d'autres aideront à éliminer ces divergences. Aujourd'hui, je dois admettre que dans les produits ci-dessus, ce n'est en fait pas GOST R 34.10-2012 qui est implémenté, mais autre chose. Il s'agit d'un algorithme différent. L'exemple de test donné serait probablement intéressant à inclure comme exemple de test pour GOST R 34.10-2012. Et je vais éditer libgcrypt pour Kleopatra et KMail. La légende de Cléopart et de la cryptographie russe était inachevée.

PS L'article était déjà prêt lorsque mon collègue a dit que la divergence entre les implémentations apparaît lorsqu'une séquence suffisamment longue de 0xFF est rencontrée. Elle, d'ailleurs, cette séquence est présente au début des fichiers doc de MS Office. J'ai vérifié la façon dont c'est. Le fichier contient 189 octets.

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


All Articles