$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

暗号技術の要点を学ぶ - 「暗号技術入門」を読んだ

最近SSLやユーザアカウント管理の安全性とかに興味があって、その要素技術である暗号技術に興味が湧いてきたので、最近新板が出た「暗号技術入門」を読んだ。

この本は、暗号学者がよく使う仕組群(この本では暗号学者の道具箱と呼称)である、対称暗号、公開鍵暗号、一方向ハッシュ関数、メッセージ認証コード、デジタル署名、擬似乱数生成器について、仕組みを順に解説してくれる。また、ただ解説してくれるだけではなくて、その仕組みによって解決できる問題と解決できない問題を明示し、では解決できない問題を解決するにはどうするかというふうに章が進んでいくので、暗号技術を分かりやすく学んでいくことが出来る。

暗号技術というと数学的で非常に難しい印象があるけど、著者である結城さんが単純化して説明してくれているおかげで数学的知識がなくとも分かりやすく、非常に面白かったのでおすすめ。もし数学が全くわからなかったとしても、その部分を飛ばしてかいつまんでいくだけでも要素技術の要点は学ぶことが出来る。これまでSSHは公開鍵で認証を行ってることは知ってるけど詳しい仕組みはよくわからんとか、httpsによる通信は盗聴出来ないことは知っているけど仕組みはよくわからん、みたいな場合はこの本を読めば理解を深めることが出来ると思う。

この本の中で面白かった部分についてまとめてみる。

  • それぞれの仕組みと解決できる問題、解決できない問題
  • 対称暗号の仕組みはXOR
  • Diffie-Hellman鍵交換

それぞれの仕組みと解決できる問題、解決できない問題

この本の中では、それぞれの仕組みが解決できる問題とそうでない問題について、章ごとにまとめてくれている。自分の中ではさらに全体でまとめておきたかったので、表にしてまとめた。

方式 解決できる問題 解決できない問題
対称暗号 平文の機密性 鍵配送問題
公開鍵暗号 鍵配送問題 速度(対称暗号と併用)、正当な受信者の公開鍵であることの保証
一方向ハッシュ関数 改ざん なりすまし
メッセージ認証コード 改ざん、なりすまし 三者に対する証明、否認
デジタル署名 改ざん、なりすまし、第三者に対する証明、否認 公開鍵が本物の送信者のものであること
証明書 公開鍵を証明すること 認証局自体の信頼
  • 対称暗号によって平文を暗号化できるようになった。しかしその鍵をどうやって配送するかという問題が出た
  • 公開鍵暗号によって、鍵の配送問題は解消された。しかし速度と正当な公開鍵である保証が問題となった
    • 速度は対称暗号と組み合わせれば良い
    • 正当な公開鍵である保証は、認証局にやってもらう必要がある
  • 一方向ハッシュ関数によって、改ざんされていないことを保証できるようになった。しかし送信者がなりすましていないことを保証はできない
  • メッセージ認証コードを利用すれば、改ざんを防ぎ、なりすましも検出できる。しかし、第三者にこのメッセージはあの人が送ったものだという証明をしてもらうことや、相手がそのメッセージは送っていないと否認することを防ぐことは出来ない
  • デジタル署名を使えば、改ざん、なりすまし、第三者に対する証明、否認などの問題を解決できる。しかしデジタル署名は公開鍵暗号を利用した仕組みなため、公開鍵暗号での解決できない問題である正当な受信者の公開鍵であることの保証、が依然問題として残る
  • 認証局をおいて、公開鍵の認証をそこで行えば、公開鍵が正当な受信者のものであることを保証できる。しかし、認証局自体を信頼できるかについて問題が残る。ただしここまで来ると社会的に何が信頼できるかという問題でもある。

まとめるとこんな感じか。実際の仕組みについては本を参照すれば良いとして、どうしてこのような要素技術を組み合わせることになったのかは、その流れを知っておけばわかるので、この辺を覚えておきたい。

対称暗号の仕組みはXOR

対称暗号の仕組みと聞くと、同じ鍵を使って暗号化復号化するんだろうとしか知識がなかった。この本を読むと基本的な仕組みはXORって書いてあって確かにと思ったのが面白かった。

対称暗号は同じbit列と2回XORすると同じものに戻るということを利用している。あるランダムなbit列を、同じ長さのメッセージとXORを取ると、ランダムなbit列に見える。これが暗号化。逆に暗号化されたメッセージと、先ほど利用したランダムbit列とをXORするとメッセージが平文に戻る。これが復号化。

実際にはいろいろ複雑な仕組みがあるけど、基本的にはこの性質を利用している、というのがおもったより単純だなあと思うことができてよかった。

Diffie-Hellman鍵交換

鍵交換の問題を解消するのは公開鍵暗号と書いた。公開鍵暗号による解消方法は、暗号化と復号化の鍵を分けて、渡す方を公開しても問題ないようにすることによって、盗聴されても問題なくなるというものだった。

これ以外の鍵交換の問題の解決方法を僕は知らなかったのだけど、この本ではDiffie-Hellman鍵交換という仕組みが解説されていて面白かった。

この方式は公開できる情報と、二人のユーザのそれぞれがプライベートに作り出した情報を利用することで、それぞれのユーザの内部で同じ共通鍵を作り出すことができるというもの。そもそも交換せず、それぞれのユーザで同じ鍵を作り出すという発想が面白いなあと思った。

まとめ

今回は「暗号技術入門」を読んで面白かったことについてまとめてみた。最近は暗号技術の要点くらいは知っておきたいと思っていたので、その目的にぴったりでよかった。

また今回の要点を知っておくと

あたりも理解できそうなので、さらに知識を深めるために読んでみたい。

乱数の性質とセッショントークンの作成

ユーザアカウントのログイン機能とか作ってると、何らかの形でセッション用のトークンを作成する機会がある。今まではこれは適当にランダムな値を利用していればいいんでしょと思っていたのだけど、ちょっと違ったのでメモ。

乱数の性質

http://akademeia.info/index.php?%CD%F0%BF%F4によると、乱数には三つの性質がある。

  • 無作為性:統計的な偏りがなく、でたらめな数列になっているという性質。
  • 予測不可能性:過去の数列から次の数を予測できないという性質。
  • 再現不可能性:同じ数列を再現できないという性質。再現するためには、数列そのものを保存しておくしかない。

この時、少なくとも無作為性のみ満たされていると弱い擬似乱数、無作為性と予測不可能性が満たされていると強い擬似乱数、全てが満たされていれば真の乱数と呼ばれる。ソフトウェアだけでは、真の乱数を作ることができず、真の乱数に近いものを作るにはハードウェアの力も借りないといけない。

暗号や推測されないトークンを生成するためには、暗号論的擬似乱数を使う必要がある。暗号論的擬似乱数は無作為性と予測不可能性を満たしたものなので、強い擬似乱数もしくは真の乱数であれば暗号や推測されないトークンのために利用できる。

/dev/random - Wikipediaによると、真の乱数に近いものを作るために/dev/randomが存在する。ただしこちらはエントロピプールが空の場合にはブロックするという性質があるため、ブロックしないものとして/dev/urandomが提供されている。この辺は#22 カジュアルに乱数を使う方法とその注意点 - KAYAC engineers' blogも参考にしたい。/dev/urandomは真の乱数ではないが、暗号用途として使える。つまり、強い擬似乱数として利用できる。


これらから、ユーザ向けにセッショントークンを発行するためには、乱数であれば何でも良いわけではなく、無作為性と予測不可能性を満たしたものを利用しなければならない。予測不可能性を満たしていなければ、過去のセッショントークンの文字列を集めればその後のトークンを予測できるため、セッションハイジャックなどが出来るように見える。

Perlでユーザアカウントのセッショントークンを作成する

実際の実装はどうするかというと、基本的にこういうのは自分で実装するとおかしなことになるので、ライブラリを利用したほうが良い。

よくランダムな文字列を作るモジュールであるString::Randomを使うのはセッショントークンとして適切ではない。https://metacpan.org/pod/String::Randomを見るとわかるが、perlのrandを利用しているのでinsecureですって書いてある。rand - Perldoc Browserを見ても、perlのrandはrand() is not cryptographically secure.と書いてあるので、セッショントークンを作成する目的としては使えない。暗号論的擬似乱数を作成するにはData::Entropy, Crypt::Random, Math::Random::Secure, Math::TrulyRandomあたりを使うのがいいらしい。

さらに使いたいalphabetや長さとかを指定できる便利モジュールとしてPerlではSession::Tokenというものがあるのでこれを使うのが良さそうに見える。内部で/dev/urandomを使いseed値を作成し、それを使ってランダムな文字列を生成してくれる。

こんな感じで、256bitのsession_tokenを作成してくれる。

my $session_token_generator = Session::Token->new(entropy => 256);
my $token = $session_token_generator->get;

ただし、Session::Tokenはforkした後にseedが共有される問題があるので、forkした後も共有される変数にSession::Tokenのインスタンスを格納しているなら、forkした時には再度作りなおす必要があるので注意。

まとめ

今回は乱数の性質について軽くまとめて、ユーザのセッション用のトークンにはどういう性質が必要かについて書いた。またPerlでそのようなものを作るためにはどのようにすれば良いかについても書いた。

セキュリティ周りに関しては、どれだけ調べても自分が正しいことを言っているのかわからないので、何かしら間違ってそうなところがあったらしてきてください。

HTML5 スタンダード・デザインガイド読んだ

HTML5を利用するにあたって注意すべき点を学ぼうと思って、HTML5 スタンダード・デザインガイドを読んだ。

この本はHTML5のタグ一つ一つについて、どのようなセマンティックな意味を持っているか説明してくれていた。ただ、タグ一つ一つについて書いてある分、読むのは結構大変かつ全て現在知っていないといけないみたいな感じではないので、少し冗長なイメージがあった。

使い方としては、よく使うタグについて一通り眺めてみてだいたいこんな感じのものがあるのかと把握しておくのが良さそう。読み終わったあとはリファレンス的に使い、新しく何らかのタグを使いたい時に、HTML5の実際の仕様を読む前の前提知識としてそのタグの説明について読むと良さそう。