aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/test
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/test')
-rw-r--r--activesupport/test/message_encryptor_test.rb118
-rw-r--r--activesupport/test/message_verifier_test.rb90
-rw-r--r--activesupport/test/messages/rotation_configuration_test.rb43
3 files changed, 251 insertions, 0 deletions
diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb
index 1fbe655642..17baf3550b 100644
--- a/activesupport/test/message_encryptor_test.rb
+++ b/activesupport/test/message_encryptor_test.rb
@@ -115,6 +115,124 @@ class MessageEncryptorTest < ActiveSupport::TestCase
assert_equal "Ruby on Rails", encryptor.decrypt_and_verify(encrypted_message)
end
+ def test_with_rotated_raw_key
+ old_raw_key = SecureRandom.random_bytes(32)
+ old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm")
+ old_message = old_encryptor.encrypt_and_sign("message encrypted with old raw key")
+
+ encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm")
+ encryptor.rotate raw_key: old_raw_key, cipher: "aes-256-gcm"
+
+ assert_equal "message encrypted with old raw key", encryptor.decrypt_and_verify(old_message)
+ end
+
+ def test_with_rotated_secret_and_salt
+ old_secret, old_salt = SecureRandom.random_bytes(32), "old salt"
+ old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt, 32)
+
+ old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm")
+ old_message = old_encryptor.encrypt_and_sign("message encrypted with old secret and salt")
+
+ encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm")
+ encryptor.rotate secret: old_secret, salt: old_salt, cipher: "aes-256-gcm"
+
+ assert_equal "message encrypted with old secret and salt", encryptor.decrypt_and_verify(old_message)
+ end
+
+ def test_with_rotated_key_generator
+ old_key_gen, old_salt = ActiveSupport::KeyGenerator.new(SecureRandom.random_bytes(32), iterations: 256), "old salt"
+
+ old_raw_key = old_key_gen.generate_key(old_salt, 32)
+ old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm")
+ old_message = old_encryptor.encrypt_and_sign("message encrypted with old key generator and salt")
+
+ encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm")
+ encryptor.rotate key_generator: old_key_gen, salt: old_salt, cipher: "aes-256-gcm"
+
+ assert_equal "message encrypted with old key generator and salt", encryptor.decrypt_and_verify(old_message)
+ end
+
+ def test_with_rotated_aes_cbc_encryptor_with_raw_keys
+ old_raw_key, old_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16)
+
+ old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1")
+ old_message = old_encryptor.encrypt_and_sign("message encrypted with old raw keys")
+
+ encryptor = ActiveSupport::MessageEncryptor.new(@secret)
+ encryptor.rotate raw_key: old_raw_key, raw_signed_key: old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1"
+
+ assert_equal "message encrypted with old raw keys", encryptor.decrypt_and_verify(old_message)
+ end
+
+ def test_with_rotated_aes_cbc_encryptor_with_secret_and_salts
+ old_secret, old_salt, old_signed_salt = SecureRandom.random_bytes(32), "old salt", "old signed salt"
+
+ old_key_gen = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000)
+ old_raw_key = old_key_gen.generate_key(old_salt, 32)
+ old_raw_signed_key = old_key_gen.generate_key(old_signed_salt)
+
+ old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1")
+ old_message = old_encryptor.encrypt_and_sign("message encrypted with old secret and salts")
+
+ encryptor = ActiveSupport::MessageEncryptor.new(@secret)
+ encryptor.rotate secret: old_secret, salt: old_salt, signed_salt: old_signed_salt, cipher: "aes-256-cbc", digest: "SHA1"
+
+ assert_equal "message encrypted with old secret and salts", encryptor.decrypt_and_verify(old_message)
+ end
+
+ def test_with_rotating_multiple_encryptors
+ older_raw_key, older_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16)
+ old_raw_key, old_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16)
+
+ older_encryptor = ActiveSupport::MessageEncryptor.new(older_raw_key, older_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1")
+ older_message = older_encryptor.encrypt_and_sign("message encrypted with older raw key")
+
+ old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1")
+ old_message = old_encryptor.encrypt_and_sign("message encrypted with old raw key")
+
+ encryptor = ActiveSupport::MessageEncryptor.new(@secret)
+ encryptor.rotate raw_key: old_raw_key, raw_signed_key: old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1"
+ encryptor.rotate raw_key: older_raw_key, raw_signed_key: older_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1"
+
+ assert_equal "encrypted message", encryptor.decrypt_and_verify(encryptor.encrypt_and_sign("encrypted message"))
+ assert_equal "message encrypted with old raw key", encryptor.decrypt_and_verify(old_message)
+ assert_equal "message encrypted with older raw key", encryptor.decrypt_and_verify(older_message)
+ end
+
+ def test_on_rotation_instance_callback_is_called_and_returns_modified_messages
+ callback_ran, message = nil, nil
+
+ older_raw_key, older_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16)
+ old_raw_key, old_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16)
+
+ older_encryptor = ActiveSupport::MessageEncryptor.new(older_raw_key, older_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1")
+ older_message = older_encryptor.encrypt_and_sign(encoded: "message")
+
+ encryptor = ActiveSupport::MessageEncryptor.new(@secret)
+ encryptor.rotate raw_key: old_raw_key, raw_signed_key: old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1"
+ encryptor.rotate raw_key: older_raw_key, raw_signed_key: older_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1"
+
+ message = encryptor.decrypt_and_verify(older_message, on_rotation: proc { callback_ran = true })
+
+ assert callback_ran, "callback was ran"
+ assert_equal({ encoded: "message" }, message)
+ end
+
+ def test_with_rotated_metadata
+ old_secret, old_salt = SecureRandom.random_bytes(32), "old salt"
+ old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt, 32)
+
+ old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm")
+ old_message = old_encryptor.encrypt_and_sign(
+ "message encrypted with old secret, salt, and metadata", purpose: "rotation")
+
+ encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm")
+ encryptor.rotate secret: old_secret, salt: old_salt, cipher: "aes-256-gcm"
+
+ assert_equal "message encrypted with old secret, salt, and metadata",
+ encryptor.decrypt_and_verify(old_message, purpose: "rotation")
+ end
+
private
def assert_aead_not_decrypted(encryptor, value)
assert_raise(ActiveSupport::MessageEncryptor::InvalidMessage) do
diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb
index fbeafca203..3079c48c02 100644
--- a/activesupport/test/message_verifier_test.rb
+++ b/activesupport/test/message_verifier_test.rb
@@ -20,6 +20,7 @@ class MessageVerifierTest < ActiveSupport::TestCase
def setup
@verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!")
@data = { some: "data", now: Time.utc(2010) }
+ @secret = SecureRandom.random_bytes(32)
end
def test_valid_message
@@ -90,6 +91,95 @@ class MessageVerifierTest < ActiveSupport::TestCase
signed_message = "BAh7BzoJc29tZUkiCWRhdGEGOgZFVDoIbm93SXU6CVRpbWUNIIAbgAAAAAAHOgtvZmZzZXRpADoJem9uZUkiCFVUQwY7BkY=--d03c52c91dfe4ccc5159417c660461bcce005e96"
assert_equal @data, @verifier.verify(signed_message)
end
+
+ def test_with_rotated_raw_key
+ old_raw_key = SecureRandom.random_bytes(32)
+
+ old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA1")
+ old_message = old_verifier.generate("message verified with old raw key")
+
+ verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1")
+ verifier.rotate raw_key: old_raw_key, digest: "SHA1"
+
+ assert_equal "message verified with old raw key", verifier.verified(old_message)
+ end
+
+ def test_with_rotated_secret_and_salt
+ old_secret, old_salt = SecureRandom.random_bytes(32), "old salt"
+
+ old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt)
+ old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA1")
+ old_message = old_verifier.generate("message verified with old secret and salt")
+
+ verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1")
+ verifier.rotate secret: old_secret, salt: old_salt, digest: "SHA1"
+
+ assert_equal "message verified with old secret and salt", verifier.verified(old_message)
+ end
+
+ def test_with_rotated_key_generator
+ old_key_gen, old_salt = ActiveSupport::KeyGenerator.new(SecureRandom.random_bytes(32), iterations: 256), "old salt"
+
+ old_raw_key = old_key_gen.generate_key(old_salt)
+ old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA1")
+ old_message = old_verifier.generate("message verified with old key generator and salt")
+
+ verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1")
+ verifier.rotate key_generator: old_key_gen, salt: old_salt, digest: "SHA1"
+
+ assert_equal "message verified with old key generator and salt", verifier.verified(old_message)
+ end
+
+ def test_with_rotating_multiple_verifiers
+ old_raw_key, older_raw_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(32)
+
+ old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA256")
+ old_message = old_verifier.generate("message verified with old raw key")
+
+ older_verifier = ActiveSupport::MessageVerifier.new(older_raw_key, digest: "SHA1")
+ older_message = older_verifier.generate("message verified with older raw key")
+
+ verifier = ActiveSupport::MessageVerifier.new("new secret", digest: "SHA512")
+ verifier.rotate raw_key: old_raw_key, digest: "SHA256"
+ verifier.rotate raw_key: older_raw_key, digest: "SHA1"
+
+ assert_equal "verified message", verifier.verified(verifier.generate("verified message"))
+ assert_equal "message verified with old raw key", verifier.verified(old_message)
+ assert_equal "message verified with older raw key", verifier.verified(older_message)
+ end
+
+ def test_on_rotation_keyword_block_is_called_and_verified_returns_message
+ callback_ran, message = nil, nil
+
+ old_raw_key, older_raw_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(32)
+
+ older_verifier = ActiveSupport::MessageVerifier.new(older_raw_key, digest: "SHA1")
+ older_message = older_verifier.generate(encoded: "message")
+
+ verifier = ActiveSupport::MessageVerifier.new("new secret", digest: "SHA512")
+ verifier.rotate raw_key: old_raw_key, digest: "SHA256"
+ verifier.rotate raw_key: older_raw_key, digest: "SHA1"
+
+ message = verifier.verified(older_message, on_rotation: proc { callback_ran = true })
+
+ assert callback_ran, "callback was ran"
+ assert_equal({ encoded: "message" }, message)
+ end
+
+ def test_with_rotated_metadata
+ old_secret, old_salt = SecureRandom.random_bytes(32), "old salt"
+
+ old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt)
+ old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA1")
+ old_message = old_verifier.generate(
+ "message verified with old secret, salt, and metadata", purpose: "rotation")
+
+ verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1")
+ verifier.rotate secret: old_secret, salt: old_salt, digest: "SHA1"
+
+ assert_equal "message verified with old secret, salt, and metadata",
+ verifier.verified(old_message, purpose: "rotation")
+ end
end
class MessageVerifierMetadataTest < ActiveSupport::TestCase
diff --git a/activesupport/test/messages/rotation_configuration_test.rb b/activesupport/test/messages/rotation_configuration_test.rb
new file mode 100644
index 0000000000..41d938e119
--- /dev/null
+++ b/activesupport/test/messages/rotation_configuration_test.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+require "active_support/messages/rotation_configuration"
+
+class MessagesRotationConfiguration < ActiveSupport::TestCase
+ def setup
+ @config = ActiveSupport::Messages::RotationConfiguration.new
+ end
+
+ def test_signed_configurations
+ @config.rotate :signed, secret: "older secret", salt: "salt", digest: "SHA1"
+ @config.rotate :signed, secret: "old secret", salt: "salt", digest: "SHA256"
+
+ assert_equal [{
+ secret: "older secret", salt: "salt", digest: "SHA1"
+ }, {
+ secret: "old secret", salt: "salt", digest: "SHA256"
+ }], @config.signed
+ end
+
+ def test_encrypted_configurations
+ @config.rotate :encrypted, raw_key: "old raw key", cipher: "aes-256-gcm"
+
+ assert_equal [{
+ raw_key: "old raw key", cipher: "aes-256-gcm"
+ }], @config.encrypted
+ end
+
+ def test_rotate_without_kind
+ @config.rotate secret: "older secret", salt: "salt", digest: "SHA1"
+ @config.rotate raw_key: "old raw key", cipher: "aes-256-gcm"
+
+ expected = [{
+ secret: "older secret", salt: "salt", digest: "SHA1"
+ }, {
+ raw_key: "old raw key", cipher: "aes-256-gcm"
+ }]
+
+ assert_equal expected, @config.encrypted
+ assert_equal expected, @config.signed
+ end
+end