aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib')
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb41
1 files changed, 37 insertions, 4 deletions
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 721efea789..87efe117c5 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -1,6 +1,7 @@
require 'openssl'
require 'base64'
require 'active_support/core_ext/array/extract_options'
+require 'active_support/message_verifier'
module ActiveSupport
# MessageEncryptor is a simple way to encrypt values which get stored
@@ -28,6 +29,16 @@ module ActiveSupport
end
end
+ module NullVerifier #:nodoc:
+ def self.verify(value)
+ value
+ end
+
+ def self.generate(value)
+ value
+ end
+ end
+
class InvalidMessage < StandardError; end
OpenSSLCipherError = OpenSSL::Cipher::CipherError
@@ -40,7 +51,8 @@ module ActiveSupport
# Options:
# * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
# <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'.
- # * <tt>:digest</tt> - String of digest to use for signing. Default is +SHA1+.
+ # * <tt>:digest</tt> - String of digest to use for signing. Default is
+ # +SHA1+. Ignored when using an AEAD cipher like 'aes-256-gcm'.
# * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
def initialize(secret, *signature_key_or_options)
options = signature_key_or_options.extract_options!
@@ -48,7 +60,8 @@ module ActiveSupport
@secret = secret
@sign_secret = sign_secret
@cipher = options[:cipher] || 'aes-256-cbc'
- @verifier = MessageVerifier.new(@sign_secret || @secret, digest: options[:digest] || 'SHA1', serializer: NullSerializer)
+ @digest = options[:digest] || 'SHA1' unless aead_mode?
+ @verifier = resolve_verifier
@serializer = options[:serializer] || Marshal
end
@@ -73,20 +86,28 @@ module ActiveSupport
# Rely on OpenSSL for the initialization vector
iv = cipher.random_iv
+ cipher.auth_data = "" if aead_mode?
encrypted_data = cipher.update(@serializer.dump(value))
encrypted_data << cipher.final
- "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
+ blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
+ blob << "--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
+ blob
end
def _decrypt(encrypted_message)
cipher = new_cipher
- encrypted_data, iv = encrypted_message.split("--".freeze).map {|v| ::Base64.strict_decode64(v)}
+ encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).map {|v| ::Base64.strict_decode64(v)}
+ raise InvalidMessage if aead_mode? && 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
@@ -103,5 +124,17 @@ module ActiveSupport
def verifier
@verifier
end
+
+ def aead_mode?
+ @aead_mode ||= new_cipher.authenticated?
+ end
+
+ def resolve_verifier
+ if aead_mode?
+ NullVerifier
+ else
+ MessageVerifier.new(@sign_secret || @secret, digest: @digest, serializer: NullSerializer)
+ end
+ end
end
end