openssl(2)
resource
暗号技術 一版を網羅した素晴らしいサイト
理解してるつもりの SSL/TLS でも、もっと理解したら面白かった話
static-DHによるSSL/TLS とか、 DH鍵交換でもWiresharkでSSL/TLSを復号化したい とか、 httpsだからというだけで安全?調べたら怖くなってきたSSLの話!? なんて、興味深い話が出て来る
nc
前回やったncを使って、tlsな経路を確保する所を見る。libtlsの中にあるmanの原稿を漁ると、 tls_read
とかと同列で、 tls_handshake
関数が説明されてた。確かにサーバーからデータを読み込む操作だよな。
下記のようにしてncを起動。
(gdb) b tls_handshake_client Function "tls_handshake_client" not defined. Breakpoint 1 (tls_handshake_client) pending. (gdb) r -cv -T noverify -T noname localhost 443 Starting program: /tmp/nc/a.out -cv -T noverify -T noname localhost 443 Breakpoint 1, tls_handshake_client (ctx=0xbd84c3b4500) at /usr/src/lib/libtls/t\ ls_client.c:423 423 {
暗号のプロトコルとかは、自前での定義を止めお任せにし、必要最低限なオプションで起動。
(gdb) bt #0 tls_handshake_client (ctx=0xbd84c3b4500) at /usr/src/lib/libtls/tls_client.c:423 #1 0x00000bd8099bf528 in tls_handshake (ctx=0xbd84c3b4500) at /usr/src/lib/libtls/tls.c:694 #2 0x00000bd5c35f2ded in timeout_tls (s=3, tls_ctx=0xbd84c3b4500, func=0xbd8099bf480 <tls_handshake>) at netcat.c:800 #3 0x00000bd5c35f3a5c in tls_setup_client (tls_ctx=0xbd84c3b4500, s=3, host=0x7f7ffffd4348 "localhost") at netcat.c:831 #4 0x00000bd5c35f19be in main (argc=2, argv=0x7f7ffffd41f8) at netcat.c:740
timeout関数の中から呼ばれている。って事は応答無しを用心してるんだな。そして、このhandshake関数の中で、 SSL_connection()
とかに分解され、やがて、tls.cの中の共通処理が呼び出される。
if (rv == 0) { ctx->ssl_peer_cert = SSL_get_peer_certificate(ctx->ssl_conn); => ctx->ssl_peer_chain = SSL_get_peer_cert_chain(ctx->ssl_conn);
起動時にvフラグを付けておくと、上記で収集した認証情報をレポートされる。最後にnetcat.c:742のreadwrite()関数の所に辿り付く。ここからは、安全路が確保出来ているので、httpなデータの送受が可能となる。
tls_xxx
って関数はOpenBSD特有なものだ。
HISTORY tls_read(), tls_write(), tls_error(), tls_close(), and tls_reset() appeared in OpenBSD 5.6 and got their final names in OpenBSD 5.7. tls_handshake() appeared in OpenBSD 5.9.
sslの関数を組み合わせれば出来る事を、綺麗に整理しましたって感じだな。と言う事は、libsslの中にあるそれは、ごちゃごちゃしてて、使いにくい(==間違い易い)って事か。
tls_ocsp.c
ってのが、オイラーには全く未知なものだった。なんじゃらほいとばかり軽く調べてみると、証明書が無効になっていないかを調べる機構らしい。証明書には有効期限が設定してあるんで、それを過ぎれば無効になるってのは、当たり前。
その他、有効期間内でも、やんごとなき理由により証明書を無効扱いにしたい場合があるそうな。オイラーは経験無いけど、交通違反で捕まった時、担当の警察官は必ず、免許証が無効になっていないか調べるそうな。有効期限内でも、免停とか有るからね。
やんごと無き理由ってのは、認証局が荒らされたとか、認証方式に重大な欠陥が見つかった場合とかがあるらしい。
libtls
OpenBSDで非常に嬉しい事の一つに、gdbを使った時、libの中も当たり前のように入って行って眺められる事が挙げられる。他のOSには無い特徴。libだってBUGが潜んでいる可能性は捨てきれない。そんな場合でもlibの中へ躊躇する事無くダイブ出来る事は、信頼性向上に大いに寄与すると思う。
前々から、どう実現してるか調べようと思っていたんだけど、適当なlibに遭遇しなかったんで(規模が大きすぎるんで大変と尻込み)これを機会に探ってみる。
ob$ ls Makefile tls.c tls_config.c tls_ocsp.c tls_verify.c Symbols.list tls.h tls_conninfo.c tls_peer.c man/ tls_bio_cb.c tls_internal.h tls_server.c shlib_version tls_client.c tls_keypair.c tls_util.c
眼に付くのは、Symbols.listってやつ。どんな内容?
tls_read tls_reset tls_server tls_unload_file tls_write
一部を列挙してみた。関数リストっぽいな。tls.hに登録されてる関数を適当に抜き出してgrepしたら、含まれていたぞ。
ob$ wc Symbols.list 90 90 2048 Symbols.list ob$ fgrep '(' tls.h | fgrep ')' | wc 74 392 4302
こんないい加減なテストでも、全て網羅してるっぽい。後はMakefileのチェック。最後の所で、何やら見慣れない事をやってた。どんな挙動になるか、走らせてしまえ。
cc -O2 -pipe -g -Wall -Wimplicit -Wundef -Werror -DLIBRESSL_INTERNAL -Wall -Wpointer-arith -Wuninitialized -Wstrict-prototypes -Wmissing-prototypes -Wunused -Wsign-compare -Wshadow -MD -MP -c tls_verify.c -o tls_verify.o building standard tls library ranlib libtls.a : cc -O2 -pipe -g -Wall -Wimplicit -Wundef -Werror -DLIBRESSL_INTERNAL -Wall -Wpointer-arith -Wuninitialized -Wstrict-prototypes -Wmissing-prototypes -Wunused -Wsign-compare -Wshadow -MD -MP -c -p tls_verify.c -o tls_verify.po building profiled tls library ranlib libtls_p.a { printf '{\n\tglobal:\n'; sed '/^[._a-zA-Z]/s/$/;/; s/^/ /' /tmp/libtls/Symbols.list; printf '\n\tlocal:\n\t\t*;\n};\n'; } >Symbols.map.tmp && mv Symbols.map.tmp Symbols.map : cc -O2 -pipe -g -Wall -Wimplicit -Wundef -Werror -DLIBRESSL_INTERNAL -Wall -Wpointer-arith -Wuninitialized -Wstrict-prototypes -Wmissing-prototypes -Wunused -Wsign-compare -Wshadow -MD -MP -c -fpic -DPIC tls_verify.c -o tls_verify.so building shared tls library (version 20.1) cc -shared -Wl,-soname,libtls.so.20.1 -fpic -o libtls.so.20.1 `echo tls.so tls_bio_cb.so tls_client.so tls_config.so tls_conninfo.so tls_keypair.so tls_peer.so tls_server.so tls_util.so tls_ocsp.so tls_verify.so | tr ' ' '\n' | sort -R` -L/usr/obj/lib/libcrypto -lcrypto -L/usr/obj/lib/libssl -lssl -Wl,--version-script=Symbols.map ===> man
静的なライブラリィー、それのプロファイル版、シェアード版と言う具合に3種類のライブラリィーを作っている。それぞれgdbにかけられるように -g フラグを付けている。 この、-g オプションの御利益を受けているんだな。
Symbols.listから作られるmapは、ひょっとしたらプロファイラーあたりが使うんかな。
{ global: tls_accept_cbs; tls_accept_fds; : tls_write; local: *; };
こんな内容だった。システムエリアに登録されてるかと思ったら、皆無だった。気にするな。
DH
上でncを肴にtlsの流れをざっと見したけど、詳細は SSL/TLSプロトコル に詳しい。後で踏み込んで見れたらいいな。
思いっきり思い付きで、やってるので、今度は目についた Diffie-Hellman 鍵共有法(DH 法) を調べてみる。
パラメータの発生
ob$ openssl dhparam -text 64 Generating DH parameters, 64 bit long safe prime, generator 2 This is going to take a long time .++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++* PKCS#3 DH Parameters: (64 bit) prime: 16271891620686995243 (0xe1d15f3782b41f2b) generator: 2 (0x2) -----BEGIN DH PARAMETERS----- MA4CCQDh0V83grQfKwIBAg== -----END DH PARAMETERS-----
素数が重要な役目を果たしている。そんなに素数を見つけ出して、他の人と衝突する恐れは無いのか? ちょっと心配なので、ガウス先生に伺ってみる。2からxまでの整数の間にある、素数の個数は、素数定理で見積もれるそう。
x / (log x)
cheme@(guile-user)> (define x (expt 2 64)) scheme@(guile-user)> x $1 = 18446744073709551616 scheme@(guile-user)> (/ x (log x)) $2 = 415828534307635100.0
264 の整数xを計算。それを自然対数で除してやれば良い。この場合対数値は44になった。おおよそ44個に一つは、素数ですって事だ。
21024 とかの巨大な数値までだと、素数の間隔は709にぐらいで1個って出て来た。
dhparam.cを眺めていたら、面白いオプションを発見
ob$ openssl dhparam -C 16 Generating DH parameters, 16 bit long safe prime, generator 2 This is going to take a long time .++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++* #ifndef HEADER_DH_H #include <openssl/dh.h> #endif DH *get_dh16() { static unsigned char dh16_p[] = { 0x9F, 0xB3, }; static unsigned char dh16_g[] = { 0x02, }; DH *dh; if ((dh = DH_new()) == NULL) return(NULL); dh->p = BN_bin2bn(dh16_p, sizeof(dh16_p), NULL); dh->g = BN_bin2bn(dh16_g, sizeof(dh16_g), NULL); if ((dh->p == NULL) || (dh->g == NULL)) { DH_free(dh); return(NULL); } return(dh); } -----BEGIN DH PARAMETERS----- MAgCAwCfswIBAg== -----END DH PARAMETERS-----
pとgを盛り込んだdhを作ってくれるのね。
追ってみる
折角なのでgdbにかけてみる。知りたいのは、いかにも計算の途中ですって言う '*++*++*+' 言うやつの出している所と、その意味。
gdb openssl で起動するので、実際の起動は一皮むけた状態で与える。
r dhparam -text 16
止める所は、mainならぬ、 dhparam_main
あたりになるかな。でつらつら追うと
307 BIO_printf(bio_err, "Generating DH parameters, % d bit long safe prime, generator %d\n", num, dhparam_config.g); (gdb) Generating DH parameters, 16 bit long safe prime, generator 2 308 BIO_printf(bio_err, "This is going to take a lon g time\n"); (gdb) This is going to take a long time 309 if (!dh || !DH_generate_parameters_ex(dh, num, d hparam_config.g, &cb)) { (gdb) .++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*
ちょいと行きすぎたので、絞り込み
Breakpoint 7, DH_generate_parameters_ex (ret=0x830f0281800, prime_len=16, generator=2, cb=0x7f7ffffc6ee0) at /usr/src/lib/libcrypto/dh/dh_gen.c:75 75 { (gdb) n 76 if (ret->meth->generate_params) (gdb) 78 return dh_builtin_genparams(ret, prime_len, generator, cb); (gdb) .++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*
更に絞り込み
Breakpoint 12, BN_generate_prime_ex (ret=0xdda6a808600, bits=16, safe=1, add=0xddab3b5d000, rem=0xddab3b5d018, cb=0x7f7fffff7d50) at /usr/src/lib/libcrypto/bn/bn_prime.c:164 164 { (gdb) bt #0 BN_generate_prime_ex (ret=0xdda6a808600, bits=16, safe=1, add=0xddab3b5d000, rem=0xddab3b5d018, cb=0x7f7fffff7d50) at /usr/src/lib/libcrypto/bn/bn_prime.c:164 #1 0x00000dda20084722 in dh_builtin_genparams (ret=0xdda92c59400, prime_len=16, generator=<optimized out>, cb=0x7f7fffff7d50) at /usr/src/lib/libcrypto/dh/dh_gen.c:161 #2 DH_generate_parameters_ex (ret=0xdda92c59400, prime_len=16, generator=<optimized out>, cb=0x7f7fffff7d50) at /usr/src/lib/libcrypto/dh/dh_gen.c:78 #3 0x00000dd7bd1ce7eb in dhparam_main (argc=3, argv=0x7f7fffff83d0) at dhparam.c:309 #4 0x00000dd7bd1dbb09 in do_cmd (prog=0xddab3b5a700, argc=3, argv=0x7f7fffff83d0) at openssl.c:577 #5 0x00000dd7bd1db553 in main (argc=3, argv=0x7f7fffff83d0) at openssl.c:477 (gdb) finish Run till exit from #0 BN_generate_prime_ex (ret=0xdda6a808600, bits=16, safe=1, add=0xddab3b5d000, rem=0xddab3b5d018, cb=0x7f7fffff7d50) at /usr/src/lib/libcrypto/bn/bn_prime.c:164 .++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*++*+ +*++*++*++*++*++*++*++*0x00000dda20084722 in dh_builtin_genparams (ret=0xdda92c5 9400, prime_len=16, generator=<optimized out>, cb=0x7f7fffff7d50) at /usr/src/lib/libcrypto/dh/dh_gen.c:161 161 if (!BN_generate_prime_ex(ret->p, prime_len, 1, t1, t2, cb)) Value returned is $12 = 1
どうやら、核心部分に迫ってきたな。 ちょいと疲れてきたので、 素数生成アルゴリズムの調査・開発 なんてのを見ている。
素数判定 や 安全素数の生成方法 も面白い。 素数の世界 はじめの一歩 なんてのが、中坊の必須項目になってるらしい。おじさん、うかうかできないね。
(gdb) c Continuing. + Breakpoint 12, BN_is_prime_fasttest_ex (a=<optimized out>, checks=34, ctx_passed=0x1364bc369b40, do_trial_division=<optimized out>, cb=0x7f7ffffd3278) at /usr/src/lib/libcrypto/bn/bn_prime.c:352 352 if (!BN_GENCB_call(cb, 1, i)) (gdb) c Continuing. + Breakpoint 12, BN_is_prime_fasttest_ex (a=<optimized out>, checks=34, ctx_passed=0x1364bc369b40, do_trial_division=<optimized out>, cb=0x7f7ffffd3278) at /usr/src/lib/libcrypto/bn/bn_prime.c:352 352 if (!BN_GENCB_call(cb, 1, i)) (gdb) c Continuing. +
どうやら、. は、開始、+ は、継続、* は、成功を表しているようだ。
(gdb) s BN_GENCB_call (cb=0x7f7ffffd3278, a=1, b=3) at /usr/src/lib/libcrypto/bn/bn_prime.c:142 142 if (!cb) (gdb) 144 switch (cb->ver) { (gdb) 153 return cb->cb.cb_2(a, b, cb); (gdb) +BN_is_prime_fasttest_ex (a=<optimized out>, checks=34, ctx_passed=0x1364bc369b40, do_trial_division=<optimized out>, cb=0x7f7ffffd3278) at /usr/src/lib/libcrypto/bn/bn_prime.c:352 352 if (!BN_GENCB_call(cb, 1, i))
流れは分かったけど、.+* のそれぞれの文字は、どうやって出力してるのだろう? stderrに流れて来るのは分かっているんで、fputsとかfprintfとかにBPを置いたんだけど、引っかかってこないのよ。この他に、stderr送り出来る関数って有ったかなあ?
広い視点で、素数発生を使っているであろうssh-keygenを調べたら、 BN_GENCB_call
を使って呼び出されるのは、 BN_generate_prime_ex
だけであった。そして、.+* での報告は無い。
openssl系で、 BN_is_prime_fasttest_ex
が呼ばれた時にのみ、状況の報告が有る。
speed
共通暗号の性能テストが行える。3秒間に何回実行出来たかが表示される。
b$ openssl speed des Doing des cbc for 3s on 16 size blocks: 10587697 des cbc's in 3.01s Doing des cbc for 3s on 64 size blocks: 2815236 des cbc's in 3.01s : type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes des cbc 56280.12k 59858.84k 60759.09k 61002.16k 60988.22k des ede3 21116.78k 21595.32k 21774.20k 21759.15k 21887.06k ob$ openssl speed -multi 2 des des cbc 100295.05k 106598.85k 108103.17k 108648.45k 108446.83k des ede3 37224.65k 38289.98k 38750.49k 38605.15k 39037.51k
今となってはディスコンになった古めかしい暗号(des)と、それを違う鍵で3回実行する(3des)の評価。マルチを付けると、複数のCPUで実行される。
実行スピードが、暗号したいサイズに余り影響しないのが、面白い。
blowfish
色々な Unix 系 OS の crypt(3) について調べたら面白かった話 なんてのを楽しんで見ている。
blowfishって何よ?
The blowfish is irresistible but our life is dear.
ふぐは食いたし、命は惜しし らしい。英語のことわざにもなってるって事は、国際的になったものだな。
OpenBSDでは、passwordに採用されてる。他の使い道としては、openssl系にも登録されてはいるけど、一般的じゃないから、嫌われるんだろうね。