diff --git a/gems/aws-sdk-s3/CHANGELOG.md b/gems/aws-sdk-s3/CHANGELOG.md index 2385d906d8e..27be70afbca 100644 --- a/gems/aws-sdk-s3/CHANGELOG.md +++ b/gems/aws-sdk-s3/CHANGELOG.md @@ -1,6 +1,8 @@ Unreleased Changes ------------------ +* Issue - S3 Encryption Client, encryptionV2 and encryptionV3, returns a decryption error for a malformed material description. + 1.228.1 (2026-07-23) ------------------ diff --git a/gems/aws-sdk-s3/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb b/gems/aws-sdk-s3/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb index 65970395b8c..edeaca5aba8 100644 --- a/gems/aws-sdk-s3/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +++ b/gems/aws-sdk-s3/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb @@ -50,7 +50,7 @@ def encryption_cipher(options = {}) # @return [Cipher] Given an encryption envelope, returns a # decryption cipher. def decryption_cipher(envelope, options = {}) - encryption_context = Json.load(envelope['x-amz-matdesc']) + encryption_context = extract_encryption_context(envelope['x-amz-matdesc']) cek_alg = envelope['x-amz-cek-alg'] case envelope['x-amz-wrap-alg'] @@ -115,6 +115,19 @@ def decryption_cipher(envelope, options = {}) private + # Raise a decryption error for a malformed material description. A + # material description must be a JSON object. + def extract_encryption_context(matdesc) + context = Json.load(matdesc) if matdesc.is_a?(String) + unless context.is_a?(Hash) + raise Errors::DecryptionError, 'Malformed material description' + end + + context + rescue Aws::Json::ParseError, EncodingError + raise Errors::DecryptionError, 'Malformed material description' + end + def validate_key_wrap(key_wrap_schema) case key_wrap_schema when :kms_context then 'kms+context' diff --git a/gems/aws-sdk-s3/lib/aws-sdk-s3/encryptionV3/kms_cipher_provider.rb b/gems/aws-sdk-s3/lib/aws-sdk-s3/encryptionV3/kms_cipher_provider.rb index 38c61240c62..f5458a26ebe 100644 --- a/gems/aws-sdk-s3/lib/aws-sdk-s3/encryptionV3/kms_cipher_provider.rb +++ b/gems/aws-sdk-s3/lib/aws-sdk-s3/encryptionV3/kms_cipher_provider.rb @@ -54,7 +54,7 @@ def decryption_cipher(envelope, options = {}) cek_alg = envelope['x-amz-c'] encryption_context = if !envelope['x-amz-t'].nil? - Json.load(envelope['x-amz-t']) + extract_encryption_context(envelope['x-amz-t']) else ##= ../specification/s3-encryption/data-format/content-metadata.md#v3-only ##% If the mapkey x-amz-t is not present, the default Material Description value MUST be set to an empty map (`{}`). @@ -102,6 +102,18 @@ def decryption_cipher(envelope, options = {}) private + # Raise a decryption error for a malformed material description. + def extract_encryption_context(matdesc) + context = Json.load(matdesc) if matdesc.is_a?(String) + unless context.is_a?(Hash) + raise Errors::DecryptionError, 'Malformed material description' + end + + context + rescue Aws::Json::ParseError, EncodingError + raise Errors::DecryptionError, 'Malformed material description' + end + def validate_key_wrap(key_wrap_schema) case key_wrap_schema when :kms_context then '12' diff --git a/gems/aws-sdk-s3/spec/encryptionV2/kms_cipher_provider_spec.rb b/gems/aws-sdk-s3/spec/encryptionV2/kms_cipher_provider_spec.rb new file mode 100644 index 00000000000..2f896ae0929 --- /dev/null +++ b/gems/aws-sdk-s3/spec/encryptionV2/kms_cipher_provider_spec.rb @@ -0,0 +1,43 @@ +require_relative '../spec_helper' + +module Aws + module S3 + module EncryptionV2 + describe KmsCipherProvider do + let(:provider) do + KmsCipherProvider.new( + kms_key_id: 'kms-key-id', + kms_client: KMS::Client.new(stub_responses: true), + key_wrap_schema: :kms_context, + content_encryption_schema: :aes_gcm_no_padding + ) + end + + describe '#decryption_cipher' do + # A malformed material description raises DecryptionError. + ['=?utf-8?B?gA==?=', '=?utf-8?B?/w==?=', '=?utf-8?B?YWJj/w==?=', "abc\xFF"].each do |matdesc| + it "raises DecryptionError (#{matdesc.inspect})" do + expect do + provider.decryption_cipher('x-amz-matdesc' => matdesc) + end.to raise_error(Errors::DecryptionError, /Malformed material description/) + end + end + + # Valid JSON that isn't an object, or an absent matdesc, is malformed. + ['123', nil].each do |matdesc| + it "raises DecryptionError (#{matdesc.inspect})" do + envelope = { + 'x-amz-matdesc' => matdesc, + 'x-amz-wrap-alg' => 'kms+context', + 'x-amz-cek-alg' => 'AES/GCM/NoPadding' + } + expect do + provider.decryption_cipher(envelope) + end.to raise_error(Errors::DecryptionError, /Malformed material description/) + end + end + end + end + end + end +end diff --git a/gems/aws-sdk-s3/spec/encryptionv3/kms_cipher_provider_spec.rb b/gems/aws-sdk-s3/spec/encryptionv3/kms_cipher_provider_spec.rb new file mode 100644 index 00000000000..38bbab26735 --- /dev/null +++ b/gems/aws-sdk-s3/spec/encryptionv3/kms_cipher_provider_spec.rb @@ -0,0 +1,29 @@ +require_relative '../spec_helper' + +module Aws + module S3 + module EncryptionV3 + describe KmsCipherProvider do + let(:provider) do + KmsCipherProvider.new( + kms_key_id: 'kms-key-id', + kms_client: KMS::Client.new(stub_responses: true), + key_wrap_schema: :kms_context, + content_encryption_schema: :alg_aes_256_gcm_hkdf_sha512_commit_key + ) + end + + describe '#decryption_cipher' do + # A malformed material description raises DecryptionError. + ['=?utf-8?B?gA==?=', '=?utf-8?B?/w==?=', '=?utf-8?B?YWJj/w==?=', "abc\xFF", '123'].each do |matdesc| + it "raises DecryptionError (#{matdesc.inspect})" do + expect do + provider.decryption_cipher('x-amz-w' => '12', 'x-amz-t' => matdesc) + end.to raise_error(Errors::DecryptionError, /Malformed material description/) + end + end + end + end + end + end +end