openssl(2)

resource

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系にも登録されてはいるけど、一般的じゃないから、嫌われるんだろうね。


This year's Index

Home