Ruby2.4に上げたら"ArgumentError: key must be %d bytes" のエラーが出るようになったことの調査と暗号化の復習
Rubyの2.4以上から、共通鍵暗号を扱うOpenSSL::Cipherにおいて、暗号化鍵と初期化ベクトル(IV)を設定する際に、指定した暗号化方式の鍵長のビット数を超えて指定した場合、ArgumentError
を返すようになりました。
irb(main):001:0> require 'openssl' => true irb(main):002:0> c = OpenSSL::Cipher.new('aes-256-cbc') => #<OpenSSL::Cipher:0x007fc3c389da30> irb(main):003:0> c.encrypt => #<OpenSSL::Cipher:0x007fc3c389da30> irb(main):004:0> c.key = "1" * 33 ArgumentError: key must be 32 bytes from (irb):4:in `key=' from (irb):4 from /Users/hoge/.rbenv/versions/2.4.1/bin/irb:11:in `<main>' irb(main):018:0> c.iv = "1" * 17 ArgumentError: iv must be 16 bytes from (irb):18:in `iv=' from (irb):18 from /Users/hoge/.rbenv/versions/2.4.1/bin/irb:11:in `<main>'
2.3までは短い場合のみエラーになっており、長い場合は切り捨てて使っていました。
https://github.com/ruby/ruby/blob/v2_4_0/doc/ChangeLog-2.4.0#L4332
class OpenSSL::Cipher (Ruby 2.4.0)
切り捨ててしまうのは、そもそもOpenSSLの仕様です。
なぜ切り捨てるようにできるのか?というのを暗号化の復習も兼ねて調べてみました。
暗号化
暗号化とは、意味の分かる情報(平文)を、意味の分からない情報(暗号文)に変換することを言います。
また、意味の分からない情報を意味の分かる情報に戻すことを復号(化)と言います。
暗号化する際に使う変換のアルゴリズムとかの歴史を追っていくと終わらないので、こちらを是非読んで貰えれば。
- 作者: 結城浩
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2015/09/17
- メディア: Kindle版
- この商品を含むブログ (1件) を見る
暗号化周りをわかりやすく読みやすく書いている本なので一読することをおすすめします。
AESとは
今回は暗号化方式の一つのAESを取り上げます。 AESはブロック暗号という暗号アルゴリズムに当たります。
ブロック暗号は、特定の固定長のビット数のまとまり(ブロック)に分けて、ブロックごとに暗号化していく暗号アルゴリズムのことです。
ブロックのビット数はブロック長と呼ばれます。
AESは128ビット(16バイト)のブロック長のみです。
暗号化する対象は、この固定長のまとまりであるブロック一つより大きいことがほとんどなので、 ブロック長ごとにブロック暗号アルゴリズムを繰り返し使って全体を暗号化する事になります。
この繰り返しの方法をブロック暗号のモードと呼びます。(複数の方法があります)
また、AESは暗号化と復号に同一の(共通の)鍵を使う対称鍵暗号化方式(共通鍵暗号方式)です。
AESの暗号化について
AESでは暗号化の鍵のビット長(鍵長)は128ビット、192ビット、256ビットが選択可能です。
この鍵を使って、ブロック長の128ビットと同じ長さのサブ鍵を作成して、それを利用して暗号化を行います。
なので、ブロック長と同じ長さである必要はありません。
鍵長がながければ長いほど、暗号強度は上がります。
AESの暗号化・復号ロジックの詳細は
- 作者: 結城浩
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2015/09/17
- メディア: Kindle版
- この商品を含むブログ (1件) を見る
の3章や
などを参照ください。
ブロック暗号のモード CBC(Cipher Block Chaining)について
一つ前の暗号ブロックと平文ブロックのXOR(排他的論理和)を取ってから暗号化を行うモードです。
初期化ベクトル(IV)
最初の平文を暗号化するときには一つ前のの暗号化ブロックがないので、1ブロック分のビット列を用意する必要があります。 このビット列を初期化ベクトル(IV)といいます。
CBCモードの暗号化、復号化のフロー
CBCモードのフローは下記の通りです。
作者 Gwenda (PNG version), WhiteTimberwolf (SVG version) (PNG version) [Public domain], ウィキメディア・コモンズ経由で
長い鍵と初期化ベクトル(IV)は切り捨てても問題ない?
そもそもAESの場合だと、鍵長を長くしても扱えませんし、IVを長くしてもブロック長を超えて与えても使えないから切り捨てるしかないですね。
でも、意図せずそういう設定にしてしまった可能性があるので、Rubyではあえてエラーにしているということかなと思います。
鍵のとIVを生成
ビット列として十分のランダムであることが求められます。
例えば鍵長が128ビットだと、2の128乗で340282366920938463463374607431768211456
通り組み合わせがあります。
ただし、a-z A-Z 0-9 のasciiだけ鍵を作ろうとすると、62 の 16乗(1byte = 8bit)で、47672401706823533450263330816
通りの組み合わせに減ってしまいます。
そこで、Rubyであれば、 module SecureRandom (Ruby 2.4.0) を使って、安全な鍵を生成するのがよいかと思います。
irb(main):001:0> require 'securerandom' => true irb(main):002:0> SecureRandom.base64(16) => "NkIG8GcEL9fYTH+BXKYjQg=="
参考
理解してるつもりの SSL/TLS でも、もっと理解したら面白かった話 · けんごのお屋敷
The AES-CBC Cipher Algorithm and Its Use with IPsec
http://www.risk.tsukuba.ac.jp/pdf/group-work2005/2005group-4-resume.pdf
openssl/evp_enc.c at 64846096b18340b9a39ddd29a7a0e23c56f22959 · openssl/openssl · GitHub
この記事は、クリエイティブ・コモンズ・表示・継承ライセンス3.0のもとで公表されたウィキペディアの項目「暗号利用モード」を素材として二次利用しています。