From: Matthew N. Heler Date: Wed, 18 Mar 2026 23:51:49 +0000 (-0500) Subject: rgw: use bucket_id instead of bucket name in GCM key derivation X-Git-Tag: v21.0.1~125^2~6 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=d651201a90b147c58448ac4b7ffe216d07b1fed6;p=ceph.git rgw: use bucket_id instead of bucket name in GCM key derivation The bucket name isn't globally unique ie different tenants can have the same bucket name. Using bucket_id (which is globally unique and includes tenant context) prevent cross-tenant key collisions in the HMAC-SHA256 derivation. Signed-off-by: Matthew N. Heler --- diff --git a/src/rgw/rgw_crypt.cc b/src/rgw/rgw_crypt.cc index ada2681ae831..6197d52423f7 100644 --- a/src/rgw/rgw_crypt.cc +++ b/src/rgw/rgw_crypt.cc @@ -779,7 +779,7 @@ public: bool derive_object_key( const uint8_t* user_key, size_t key_len, - const std::string& bucket, + const std::string& bucket_id, const std::string& object, uint32_t part_number, const std::string& domain = "SSE-C-GCM") @@ -794,7 +794,7 @@ public: return false; } - // HMAC-SHA256(user_key, nonce || domain || bucket || object) + // HMAC-SHA256(user_key, nonce || domain || bucket_id || object) try { ceph::crypto::HMACSHA256 hmac(user_key, key_len); @@ -820,9 +820,9 @@ public: // Domain separator (length-prefixed for consistency) hmac_update_with_length(domain); - // Include bucket/object with length prefixes - // Format: nonce || len(domain) || domain || len(bucket) || bucket || len(object) || object - hmac_update_with_length(bucket); + // Include bucket_id/object with length prefixes + // Format: nonce || len(domain) || domain || len(bucket_id) || bucket_id || len(object) || object + hmac_update_with_length(bucket_id); hmac_update_with_length(object); hmac.Final(this->key); @@ -840,7 +840,7 @@ public: memcpy(this->base_key, this->key, AES_256_KEYSIZE); this->has_base_key = true; - ldpp_dout(dpp, 20) << "derive_object_key: derived key for bucket=" << bucket + ldpp_dout(dpp, 20) << "derive_object_key: derived key for bucket_id=" << bucket_id << " object=" << object << " part_number=" << part_number << dendl; @@ -1395,7 +1395,7 @@ std::string AES_256_GCM_get_nonce(BlockCrypt* block_crypt) bool AES_256_GCM_derive_object_key(BlockCrypt* block_crypt, const uint8_t* user_key, size_t key_len, - const std::string& bucket, + const std::string& bucket_id, const std::string& object, uint32_t part_number, const std::string& domain) @@ -1404,7 +1404,7 @@ bool AES_256_GCM_derive_object_key(BlockCrypt* block_crypt, if (!gcm) { return false; } - return gcm->derive_object_key(user_key, key_len, bucket, object, + return gcm->derive_object_key(user_key, key_len, bucket_id, object, part_number, domain); } @@ -2180,11 +2180,11 @@ int rgw_s3_prepare_encrypt(req_state* s, optional_yield y, if (!gcm->derive_object_key( reinterpret_cast(key_bin.c_str()), AES_256_KEYSIZE, - s->bucket->get_name(), + s->bucket->get_info().bucket.bucket_id, s->object->get_name(), part_number)) { ldpp_dout(s, 5) << "ERROR: SSE-C-AES256-GCM key derivation failed for " - << s->bucket->get_name() << "/" << s->object->get_name() << dendl; + << s->bucket->get_info().bucket.bucket_id << "/" << s->object->get_name() << dendl; s->err.message = "Failed to derive encryption key."; ::ceph::crypto::zeroize_for_security(key_bin.data(), key_bin.length()); return -EIO; @@ -2419,16 +2419,16 @@ int rgw_s3_prepare_encrypt(req_state* s, optional_yield y, } // Derive encryption key using HMAC-SHA256 with context binding - // Key = HMAC-SHA256(master_key, nonce || "RGW-AUTO-GCM" || bucket || object) + // Key = HMAC-SHA256(master_key, nonce || "RGW-AUTO-GCM" || bucket_id || object) if (!gcm->derive_object_key( reinterpret_cast(master_encryption_key.c_str()), AES_256_KEYSIZE, - s->bucket->get_name(), + s->bucket->get_info().bucket.bucket_id, s->object->get_name(), part_number, "RGW-AUTO-GCM")) { ldpp_dout(s, 5) << "ERROR: RGW-AUTO-GCM key derivation failed for " - << s->bucket->get_name() << "/" << s->object->get_name() << dendl; + << s->bucket->get_info().bucket.bucket_id << "/" << s->object->get_name() << dendl; return -EIO; } *block_crypt = std::move(gcm); @@ -2465,22 +2465,22 @@ int rgw_s3_prepare_encrypt(req_state* s, optional_yield y, static void pick_gcm_identity(req_state* s, bool copy_source, const rgw_crypt_src_identity* src_identity, - std::string& bucket_name, + std::string& bucket_id, std::string& object_name) { if (copy_source) { if (src_identity && src_identity->valid()) { - bucket_name = std::string(src_identity->bucket); + bucket_id = std::string(src_identity->bucket_id); object_name = std::string(src_identity->object); return; } - if (s->src_object) { - bucket_name = s->src_bucket_name; + if (s->src_object && s->src_object->get_bucket()) { + bucket_id = s->src_object->get_bucket()->get_info().bucket.bucket_id; object_name = s->src_object->get_name(); return; } } - bucket_name = s->bucket->get_name(); + bucket_id = s->bucket->get_info().bucket.bucket_id; object_name = s->object->get_name(); } @@ -2678,17 +2678,17 @@ int rgw_s3_prepare_decrypt(req_state* s, optional_yield y, stored_nonce.size()); // Re-derive encryption key from user key + object identity // For CopyObject, use the SOURCE object's identity (not destination) - std::string bucket_name; + std::string bucket_id; std::string object_name; - pick_gcm_identity(s, copy_source, src_identity, bucket_name, object_name); + pick_gcm_identity(s, copy_source, src_identity, bucket_id, object_name); if (!gcm->derive_object_key( reinterpret_cast(key_bin.c_str()), AES_256_KEYSIZE, - bucket_name, + bucket_id, object_name, part_number)) { ldpp_dout(s, 5) << "ERROR: SSE-C-AES256-GCM key derivation failed for " - << bucket_name << "/" << object_name << dendl; + << bucket_id << "/" << object_name << dendl; s->err.message = "Failed to derive decryption key."; return -EIO; } @@ -2849,19 +2849,19 @@ int rgw_s3_prepare_decrypt(req_state* s, optional_yield y, // Re-derive encryption key using HMAC-SHA256 with context binding // For CopyObject, use the SOURCE object's identity (not destination) - std::string bucket_name; + std::string bucket_id; std::string object_name; - pick_gcm_identity(s, copy_source, src_identity, bucket_name, object_name); + pick_gcm_identity(s, copy_source, src_identity, bucket_id, object_name); if (!gcm->derive_object_key( reinterpret_cast(master_encryption_key.c_str()), AES_256_KEYSIZE, - bucket_name, + bucket_id, object_name, part_number, "RGW-AUTO-GCM")) { ldpp_dout(s, 5) << "ERROR: RGW-AUTO-GCM key derivation failed for " - << bucket_name << "/" << object_name << dendl; + << bucket_id << "/" << object_name << dendl; s->err.message = "Failed to derive decryption key."; return -EIO; } diff --git a/src/rgw/rgw_crypt.h b/src/rgw/rgw_crypt.h index a931785846c7..6b113c99991b 100644 --- a/src/rgw/rgw_crypt.h +++ b/src/rgw/rgw_crypt.h @@ -16,11 +16,12 @@ #include "common/async/yield_context.h" struct rgw_crypt_src_identity { - std::string_view bucket; - std::string_view object; + std::string bucket_id; + std::string bucket; + std::string object; bool valid() const { - return !bucket.empty() && !object.empty(); + return !bucket_id.empty() && !bucket.empty() && !object.empty(); } }; diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 57d93b13664c..75d552c4f3fe 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -3089,7 +3089,7 @@ int RGWPutObj_ObjStore_S3::get_decrypt_filter( bufferlist* manifest_bl) { static constexpr bool copy_source = true; - rgw_crypt_src_identity src_identity{copy_source_bucket_name, copy_source_object_name}; + rgw_crypt_src_identity src_identity{copy_source_bucket_info.bucket.bucket_id, copy_source_bucket_name, copy_source_object_name}; // part_num=0 for copy source (full object read) return ::get_decrypt_filter(filter, cb, s, attrs, manifest_bl, nullptr, copy_source, 0, 0, &src_identity); diff --git a/src/test/rgw/test_rgw_crypto.cc b/src/test/rgw/test_rgw_crypto.cc index d539580ba7ad..d2478b2de152 100644 --- a/src/test/rgw/test_rgw_crypto.cc +++ b/src/test/rgw/test_rgw_crypto.cc @@ -29,7 +29,7 @@ std::unique_ptr AES_256_CBC_create(const DoutPrefixProvider *dpp, Ce bool AES_256_GCM_derive_object_key(BlockCrypt* block_crypt, const uint8_t* user_key, size_t key_len, - const std::string& bucket, + const std::string& bucket_id, const std::string& object, uint32_t part_number, const std::string& domain = "SSE-C-GCM");