CubicLouve

Spring_MTの技術ブログ

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

github.com

class OpenSSL::Cipher (Ruby 2.4.0)

切り捨ててしまうのは、そもそもOpenSSLの仕様です。

なぜ切り捨てるようにできるのか?というのを暗号化の復習も兼ねて調べてみました。

暗号化

暗号化とは、意味の分かる情報(平文)を、意味の分からない情報(暗号文)に変換することを言います。

また、意味の分からない情報を意味の分かる情報に戻すことを復号(化)と言います。

暗号 - Wikipedia

暗号化する際に使う変換のアルゴリズムとかの歴史を追っていくと終わらないので、こちらを是非読んで貰えれば。

暗号化周りをわかりやすく読みやすく書いている本なので一読することをおすすめします。

AESとは

今回は暗号化方式の一つのAESを取り上げます。 AESはブロック暗号という暗号アルゴリズムに当たります。

ブロック暗号は、特定の固定長のビット数のまとまり(ブロック)に分けて、ブロックごとに暗号化していく暗号アルゴリズムのことです。

ブロックのビット数はブロック長と呼ばれます。

AESは128ビット(16バイト)のブロック長のみです。

暗号化する対象は、この固定長のまとまりであるブロック一つより大きいことがほとんどなので、 ブロック長ごとにブロック暗号アルゴリズムを繰り返し使って全体を暗号化する事になります。

この繰り返しの方法をブロック暗号のモードと呼びます。(複数の方法があります)

また、AESは暗号化と復号に同一の(共通の)鍵を使う対称鍵暗号化方式(共通鍵暗号方式)です。

AESの暗号化について

AESでは暗号化の鍵のビット長(鍵長)は128ビット、192ビット、256ビットが選択可能です。

この鍵を使って、ブロック長の128ビットと同じ長さのサブ鍵を作成して、それを利用して暗号化を行います。

なので、ブロック長と同じ長さである必要はありません。

鍵長がながければ長いほど、暗号強度は上がります。

AESの暗号化・復号ロジックの詳細は

の3章や

www.atmarkit.co.jp

などを参照ください。

ブロック暗号のモード CBC(Cipher Block Chaining)について

一つ前の暗号ブロックと平文ブロックのXOR(排他的論理和)を取ってから暗号化を行うモードです。

初期化ベクトル(IV)

最初の平文を暗号化するときには一つ前のの暗号化ブロックがないので、1ブロック分のビット列を用意する必要があります。 このビット列を初期化ベクトル(IV)といいます。

CBCモードの暗号化、復号化のフロー

CBCモードのフローは下記の通りです。

CFB encryption 作者 Gwenda (PNG version), WhiteTimberwolf (SVG version) (PNG version) [Public domain], ウィキメディア・コモンズ経由で

CFB decryption

暗号利用モード - Wikipedia

長い鍵と初期化ベクトル(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 でも、もっと理解したら面白かった話 · けんごのお屋敷

thinkit.co.jp

The AES-CBC Cipher Algorithm and Its Use with IPsec

http://www.risk.tsukuba.ac.jp/pdf/group-work2005/2005group-4-resume.pdf

鍵 (暗号) - Wikipedia

openssl/evp_enc.c at 64846096b18340b9a39ddd29a7a0e23c56f22959 · openssl/openssl · GitHub

この記事は、クリエイティブ・コモンズ・表示・継承ライセンス3.0のもとで公表されたウィキペディアの項目「暗号利用モード」を素材として二次利用しています。