Informationen zu Open-Source-Implementierungen der Hash-Funktion GOST R 34.11-2012 und deren Auswirkungen auf die elektronische Signatur von GOST R 34.10-2012

Zu einer Zeit hat mich die Implementierung von inländischen kryptografischen Algorithmen in der libgcrypt-Bibliothek sehr inspiriert. Es wurde möglich, diese Algorithmen in Kleopatra und in Kmail und GnuPg im Allgemeinen zu verwenden, um die libgcrypt-Bibliothek als Alternative zu openssl mit der GOST-Engine zu betrachten. Und bis letzten Freitag war alles super.

Ich wurde gebeten, die elektronische Signatur von GOST R 34.10-2012-256 für ein in Microsoft Office unter MS Windows erstelltes Dokument zu überprüfen. Und ich habe beschlossen, es in Kleopatra zu überprüfen (ich habe Linux). Und was denkst du, die Unterschrift hat sich als falsch herausgestellt. Zweifel schlichen sich ein. Ich habe mich entschlossen, mit der GOST- Engine nach openssl zu suchen . Die Signatur wurde erfolgreich überprüft. Die Datei wurde in Kleopatra dringend neu signiert und unter MS Windows nicht bestanden. Wir haben versucht, andere Dateien zu signieren und zu verifizieren, alles war in Ordnung. Die Frage war, was ist das Problem? Da der Hash des Dokuments an der Signierung beteiligt ist, wurde beschlossen, die Berechnung des Hash durch verschiedene Programme zu überprüfen. Zunächst waren Open-Source-Implementierungen für Stribog beteiligt :


Und dann gab es einen Schock! Der von der "berühmten Degtyarev-Implementierung" berechnete Hash stimmte mit dem in openssl mit dem GOST-Endin berechneten Hash überein, stimmte jedoch nicht mit dem mit libgcrypt und libressl berechneten Hash-Wert überein.

Wie ru_crypt richtig war, als er am Anfang seines Artikels schrieb :
Ich warne Sie sofort, dass ich die Richtigkeit der Implementierungen nicht überprüft habe.

Übrigens besagt der Standard zu GOST R 34.10-2012 auch, dass Kontrollbeispiele nur als Referenz dienen. Es muss klar sein, dass die Testfälle nicht garantieren, dass unterschiedliche Implementierungen bei allen Gelegenheiten das gleiche Ergebnis liefern.

Zur Berechnung der Hashwerte wurden die folgenden Dienstprogramme verwendet:

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) Die berühmte Implementierung von Degtyarev

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

Auch hier ist eine interessante Sache: In der lateinischen Transkription schreiben Stribogs entweder Stribog oder Streebog. Es wäre schön, zur Einheitlichkeit zu kommen. Es scheint also, dass dies verschiedene Funktionen sind. Persönlich bevorzuge ich die erste Option - Stribog.

Ich brauchte einen Schiedsrichter.

Als Schiedsrichter wurde beschlossen, das PKCS # 11-Token RUTOKEN EDS-2.0 zu verwenden, das die russischen kryptografischen Standards GOST R 34.10-2012, GOST R 34.11-2012, VKO GOST R 34.10-2012 (RFC 7836) mit einer Schlüssellänge von 256 und 512 Bit unterstützt und ist vom FSB Russlands als Mittel zum Schutz kryptografischer Informationen (CPSI) und als Mittel zur elektronischen Signatur zertifiziert.

Darüber hinaus ist das RUTOKEN EDS-2.0-Token weit verbreitet und enthält viele Zertifikate für den Zugriff auf staatliche Dienste und andere Portale.
Um den Hashwert für das Token zu berechnen, verwenden wir das Skript test_digest.tcl in 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 


Wann tritt diese Diskrepanz bei der Implementierung auf? Bisher konnte festgestellt werden, dass diese Diskrepanz bei der Berechnung des Hash von in MS Office erstellten Dokumentdateien auftritt. Darüber hinaus wird der Hash aus den ersten 143 Bytes als gleich angesehen, und bei der Berechnung des Hash aus 144 Bytes sind die Werte unterschiedlich.

Die ersten 143 Bytes in hexadezimaler Darstellung sehen folgendermaßen aus:

 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000002400000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 

Speichern Sie sie in der Datei Doc1_143_hex.txt.

Die ersten 144 Bytes in hexadezimaler Darstellung sehen folgendermaßen aus:

 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000002400000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 

Speichern Sie sie in der Datei Doc1_144_hex.txt.

Es ist praktisch, das Skript hex2bin.tcl zu verwenden, um von der hexadezimalen in die binäre Form zu übersetzen:

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

Konvertieren Sie den Hexadezimalcode in Binärcode:
 $./hex2bin Doc1_143_hex.txt Doc1_143.bin $./hex2bin Doc1_144_hex.txt Doc1_144.bin $ 

Jetzt können Sie überprüfen, wie der Hash durch verschiedene Implementierungen berechnet wird:
Betrachten Sie zunächst den Hash für die Datei 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 $ 

Der wichtigste Moment ist gekommen, der Moment der Überprüfung auf einem zertifizierten kryptografischen Informationsschutzsystem:

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

Wie Sie sehen können, endete alles für immer.

Mal sehen, was mit der Datei Doc1_144.bin passiert:

 $ ./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 $ 

Das war's, die Werte der Hashes stimmen nicht überein. Auf die Reinheit des Experiments überprüfen wir die verbleibenden Implementierungen:

 $ ./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 $ 

Der von der "berühmten Degtyarev-Implementierung" berechnete Hash stimmt mit dem in openssl berechneten Hash mit der GOST-Engine überein, stimmt jedoch nicht mit dem mit libgcrypt und libressl berechneten Hashwert überein.

Wir erhalten ein ähnliches Ergebnis, wenn wir den Hash stribog512 betrachten.

Es gibt eine Schlussfolgerung. Wenn Sie möchten, dass die von libressl und libgcrypt (oder möglicherweise anderen) generierte elektronische Signatur GOST R 34.10-2012 mit openssl und vor allem mit dem im russischen FSB-Zertifizierungssystem zertifizierten kryptografischen Informationsschutz kompatibel ist, verwenden Sie verifiziert Implementierungen für die Berechnung von Hashes. Ich hoffe, dass diese Veröffentlichung viele Missverständnisse vermeidet und die Autoren der Implementierung von stribog in libressl, libgrypt und möglicherweise anderen dazu beitragen werden, diese Unterschiede zu beseitigen. Heute muss ich zugeben, dass in den oben genannten Produkten nicht GOST R 34.10-2012 implementiert ist, sondern etwas anderes. Dies ist ein anderer Algorithmus. Das angegebene Testbeispiel wäre wahrscheinlich schön, um es als Testbeispiel für GOST R 34.10-2012 aufzunehmen. Und ich werde libgcrypt für Kleopatra und KMail bearbeiten. Die Legende von Cleopart und der russischen Kryptographie war unvollendet.

PS Der Artikel war bereits fertig, als mein Kollege sagte, dass die Diskrepanz zwischen den Implementierungen auftritt, wenn eine ausreichend lange Folge von 0xFF auftritt. Sie, diese Sequenz, ist übrigens am Anfang von Dokumentdateien von MS Office vorhanden. Ich habe nachgesehen, wie es ist. Die Datei enthielt 189 Bytes.

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


All Articles