IV(初期化ベクトル)をどこに保存してあるのだろう、どうやって復号化しているのだろうか?と調べたのでメモ。
#encrypt_and_sign
の返り値は、--
区切りで暗号化データ・初期化ベクトル・認証タグとなっている(それぞれBase64符号化を行っている)。
# Rely on OpenSSL for the initialization vector iv = cipher.random_iv cipher.auth_data = "" if aead_mode? encrypted_data = cipher.update(Messages::Metadata.wrap(@serializer.dump(value), metadata_options)) encrypted_data << cipher.final blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}" blob = "#{blob}--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode? blob
ref: active_support/message_encryptor.rb
実行結果
crypt = ActiveSupport::MessageEncryptor.new('a'*32) => #<ActiveSupport::MessageEncryptor:0x00007f9fc8fefaa0 @secret="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", @sign_secret=nil, @cipher="aes-256-gcm", @aead_mode=true, @verifier=ActiveSupport::MessageEncryptor::NullVerifier, @serializer=Marshal, @options={}, @rotations=[]> crypt.encrypt_and_sign('secret data that needs to be concealed') => "PnsLv1E/iLi0ZZxE4NKAavBO3UJc4Xe3KUZnvsJfqmA61Rt/iGPjtYbxy55/OUng--HcmSJhaaOrSoBGVL--oGfjcvq66Kw4i2Yk3A6neQ==" irb(main):054:0>
上に示したように返り値は PnsLv1E/iLi0ZZxE4NKAavBO3UJc4Xe3KUZnvsJfqmA61Rt/iGPjtYbxy55/OUng--HcmSJhaaOrSoBGVL--oGfjcvq66Kw4i2Yk3A6neQ==
となっており、--
で分解するとそれぞれ
PnsLv1E/iLi0ZZxE4NKAavBO3UJc4Xe3KUZnvsJfqmA61Rt/iGPjtYbxy55/OUng
(暗号化データ)HcmSJhaaOrSoBGVL
(IV)oGfjcvq66Kw4i2Yk3A6neQ==
(認証タグ)
となっている。
したがって初期化ベクトルをDBに保存する必要がない。以下に引用するように、実際の処理ではは encrypted_data, iv, auth_tag = encrypted_message.split("--").map { |v| ::Base64.strict_decode64(v) }
としている。ここが attr-encrypted gemとの違いで気軽に使いやすい。
def _decrypt(encrypted_message, purpose) cipher = new_cipher encrypted_data, iv, auth_tag = encrypted_message.split("--").map { |v| ::Base64.strict_decode64(v) } # Currently the OpenSSL bindings do not raise an error if auth_tag is # truncated, which would allow an attacker to easily forge it. See # https://github.com/ruby/openssl/issues/63 raise InvalidMessage if aead_mode? && (auth_tag.nil? || auth_tag.bytes.length != 16) cipher.decrypt cipher.key = @secret cipher.iv = iv if aead_mode? cipher.auth_tag = auth_tag cipher.auth_data = "" end decrypted_data = cipher.update(encrypted_data) decrypted_data << cipher.final message = Messages::Metadata.verify(decrypted_data, purpose) @serializer.load(message) if message rescue OpenSSLCipherError, TypeError, ArgumentError raise InvalidMessage end
ref: active_support/message_encryptor.rb