GOST R 34.11-2012ハッシュ関数のオープンソース実装と、GOST R 34.10-2012の電子署名への影響について

かつて、libgcryptライブラリに国内の暗号化アルゴリズムを実装したことは、私に大きな影響を与えました。 libgcryptライブラリをGOSTエンジンでのopensslの代替として考慮するために、 これらのアルゴリズムをKleopatraおよびKmailおよびGnuPgで一般的に使用することが可能になりました。 そして、すべてが先週の金曜日まで素晴らしかった。



MS Windows上のMicrosoft Officeで作成されたドキュメントのGOST R 34.10-2012-256の電子署名を検証するように求められました。 そして、Kleopatraで確認することにしました(Linuxがあります)。 そして、あなたはどう思いますか、署名は間違っていることが判明しました。 疑いが忍び込んだ。 GOSTエンジンでopensslをチェックすることにしました。 署名が正常に検証されました。 Kleopatraのファイルに緊急に再署名しましたが、MS Windowsのチェックに合格しませんでした。 私たちは他のファイルに署名して検証しようとしましたが、すべてがうまくいきました。 問題は何が問題なのか? 文書のハッシュは署名に関係しているため、さまざまなプログラムでハッシュの計算を確認することにしました。 まず、 stribogのオープンソース実装が関係していました





そして、ショックがありました! 「有名なDegtyarev実装」によって計算されたハッシュは、endsslのGOSTでopensslで計算されたハッシュと一致しましたが、libgcryptおよびlibresslを使用して計算されたハッシュ値と一致しませんでした。



記事の冒頭で書いたru_cryptの正しさ:

私はすぐに、実装の正確性を確認しなかったことを警告します。


ちなみに、GOST R 34.10-2012の標準では、制御の例は参照のみを目的としています。 テストケースは、異なる実装がすべての場合に同じ結果を与えることを保証しないことを明確に理解する必要があります。



ハッシュ値を計算するために、次のユーティリティが使用されました。



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)Degtyarevの有名な実装



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





ここにも興味深いものがあります。ラテン語の転写では、ストライボグはストライボグまたはストリーボグのいずれかを書きます。 均一になるといいですね。 そのため、これらは異なる機能であるようです。 個人的には、最初のオプションであるstribogを好みます。



仲裁人が必要でした。



調停者として、ロシアの暗号化標準GOST R 34.10-2012、GOST R 34.11-2012、VKO GOST R 34.10-2012(RFC 7836)をキー長256および512ビットでサポートするPKCS#11トークンRUTOKEN EDS-2.0を使用することが決定されました、暗号化情報保護(CPSI)および電子署名の手段としてロシアのFSBによって認定されています。



また、RUTOKEN EDS-2.0トークンは広く配布されており、多くの場合、 ステートサービスや他のポータルにアクセスするための証明書格納されています。

トークンのハッシュ値を計算するには、 Tclの test_digest.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
      
      







実装にこの矛盾が現れるのはいつですか? これまでのところ、MS Officeで作成されたdocファイルのハッシュを計算するときに、この不一致が発生することを判別できました。 さらに、最初の143バイトからのハッシュは同じと見なされ、144バイトからハッシュを計算する場合でも、値は異なります。



16進数の最初の143バイトは次のようになります。



 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000002400000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
      
      





ファイルDoc1_143_hex.txtに保存します。



16進数の最初の144バイトは次のようになります。



 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000002400000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
      
      





それらをファイルDoc1_144_hex.txtに保存します。



hex2bin.tclスクリプトを使用して、16進数からバイナリ形式に変換すると便利です。



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





16進コードをバイナリに変換します。

 $./hex2bin Doc1_143_hex.txt Doc1_143.bin $./hex2bin Doc1_144_hex.txt Doc1_144.bin $
      
      





これで、さまざまな実装によるハッシュの計算方法を確認できます。

まず、ファイル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 $
      
      





最も重要な瞬間、認証された暗号情報保護システムでの検証の瞬間が来ました:



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





ご覧のとおり、すべてが永久に終了しました。



ファイル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 $
      
      





それだけです、ハッシュの値が一致しません。 実験の純度については、残りの実装を確認します。



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





「有名なDegtyarev実装」によって計算されたハッシュは、opensslでGOSTエンジンで計算されたハッシュと一致しますが、libgcryptおよびlibresslを使用して計算されたハッシュ値とは一致しません。



ハッシュstribog512を考慮すると、同様の結果が得られます。



1つの結論があります。 libresslおよびlibgcrypt(またはその他)によって生成されたGOST R 34.10-2012電子署名をopensslと互換性があり、最も重要なこととして、ロシアのFSB認証システムで認証された暗号情報保護と互換性がある場合は、verifiedを使用しますハッシュを計算するための実装。 この出版物が多くの誤解を回避し、libressl、libgrypt、およびおそらく他の人にstribogを実装する著者がこれらの矛盾を排除するのを助けることを願っています。 今日、上記の製品では、実際に実装されているのはGOST R 34.10-2012ではなく、他の何かであることを認めなければなりません。 これは別のアルゴリズムです。 与えられたテスト例は、おそらくGOST R 34.10-2012のテスト例として含めるとよいでしょう。 そして、KleopatraとKMailのlibgcryptを編集します。 クレオパートとロシアの暗号化の伝説は未完成でした。



PS同僚が、0xFFの十分に長いシーケンスに遭遇すると実装の不一致が現れると言ったとき、この記事はすでに準備ができていました。 ちなみに、このシーケンスは、MS Officeのdocファイルの先頭にあります。 ありのままにチェックしました。 ファイルには189バイトが含まれていました。



All Articles