diff --git a/src/common/legacy_config_opts.h b/src/common/legacy_config_opts.h index 10c4d7d206b625b2a2af95ac2e4de52b3d5ed80c..c2e9d9a83b91f9c9ba1e43a83055a728566f1988 100644 --- a/src/common/legacy_config_opts.h +++ b/src/common/legacy_config_opts.h @@ -1241,6 +1241,7 @@ OPTION(rgw_max_attr_size, OPT_SIZE) OPTION(rgw_max_attrs_num_in_req, OPT_U64) OPTION(rgw_max_chunk_size, OPT_INT) +OPTION(rgw_crypt_chunk_size, OPT_INT) OPTION(rgw_put_obj_min_window_size, OPT_INT) OPTION(rgw_put_obj_max_window_size, OPT_INT) OPTION(rgw_max_put_size, OPT_U64) @@ -1527,6 +1528,7 @@ OPTION(rgw_crypt_kmip_s3_key_template, OPT_STR) // sse-s3; kmip key names OPTION(rgw_crypt_s3_kms_encryption_keys, OPT_STR) // extra keys that may be used for aws:kms // defined as map "key1=YmluCmJvb3N0CmJvb3N0LQ== key2=b3V0CnNyYwpUZXN0aW5nCg==" +OPTION(rgw_crypt_s3_kms_algorithm, OPT_STR) // SSE-KMS encryption algorithm, support aes and sm4 OPTION(rgw_crypt_suppress_logs, OPT_BOOL) // suppress logs that might print customer key OPTION(rgw_list_bucket_min_readahead, OPT_INT) // minimum number of entries to read from rados for bucket listing diff --git a/src/common/options.cc b/src/common/options.cc index cef26003c208cf9cef8120599e98487e198699f9..dcc22b0ff4755ad80adb11e368cb3b311825f77f 100644 --- a/src/common/options.cc +++ b/src/common/options.cc @@ -5772,6 +5772,11 @@ std::vector<Option> get_rgw_options() { "need to be atomic, and anything larger than this would require more than a single " "operation."), + Option("rgw_crypt_chunk_size", Option::TYPE_SIZE, Option::LEVEL_ADVANCED) + .set_default(4096) + .set_min(4096) + .set_description("Set RGW crypt chunk size"), + Option("rgw_put_obj_min_window_size", Option::TYPE_SIZE, Option::LEVEL_ADVANCED) .set_default(16_M) .set_description("The minimum RADOS write window size (in bytes).") @@ -7139,6 +7144,11 @@ std::vector<Option> get_rgw_options() { .set_default("") .set_description(""), + Option("rgw_crypt_s3_kms_algorithm", Option::TYPE_STR, Option::LEVEL_ADVANCED) + .set_default("aes") + .set_enum_allowed({"aes", "sm4"}) + .set_description("rgw s3 SSE-KMS algorithm, aes or sm4, Default is aes alg."), + Option("rgw_crypt_vault_auth", Option::TYPE_STR, Option::LEVEL_ADVANCED) .set_default("token") .set_enum_allowed({"token", "agent"}) diff --git a/src/rgw/rgw_crypt.cc b/src/rgw/rgw_crypt.cc index 9f7e692564218e62d524dece6f1e6521683ca8cf..db6098f7a4667c3a6536f5625f362320b1b3dcf5 100644 --- a/src/rgw/rgw_crypt.cc +++ b/src/rgw/rgw_crypt.cc @@ -309,7 +309,6 @@ CryptoAccelRef get_crypto_accel(CephContext *cct) } -template <std::size_t KeySizeV, std::size_t IvSizeV> static inline bool evp_sym_transform(CephContext* const cct, const EVP_CIPHER* const type, @@ -318,6 +317,8 @@ bool evp_sym_transform(CephContext* const cct, const size_t size, const unsigned char* const iv, const unsigned char* const key, + size_t IvSizeV, + size_t KeySizeV, const bool encrypt) { using pctx_t = \ @@ -335,11 +336,11 @@ bool evp_sym_transform(CephContext* const cct, } // we want to support ciphers that don't use IV at all like AES-256-ECB - if constexpr (static_cast<bool>(IvSizeV)) { - ceph_assert(EVP_CIPHER_CTX_iv_length(pctx.get()) == IvSizeV); - ceph_assert(EVP_CIPHER_CTX_block_size(pctx.get()) == IvSizeV); + if (static_cast<bool>(IvSizeV)) { + ceph_assert(EVP_CIPHER_CTX_iv_length(pctx.get()) == (int)IvSizeV); + ceph_assert(EVP_CIPHER_CTX_block_size(pctx.get()) == (int)IvSizeV); } - ceph_assert(EVP_CIPHER_CTX_key_length(pctx.get()) == KeySizeV); + ceph_assert(EVP_CIPHER_CTX_key_length(pctx.get()) == (int)KeySizeV); if (1 != EVP_CipherInit_ex(pctx.get(), nullptr, nullptr, key, iv, encrypt)) { ldout(cct, 5) << "EVP: failed to 2nd initialization stage" << dendl; @@ -398,81 +399,99 @@ bool evp_sym_transform(CephContext* const cct, * 6. (Special case) If m == 0 then last n bytes are xor-ed with pattern * obtained by CBC ENCRYPTION of {0} with IV derived from offset */ -class AES_256_CBC : public BlockCrypt { -public: - static const size_t AES_256_KEYSIZE = 256 / 8; - static const size_t AES_256_IVSIZE = 128 / 8; - static const size_t CHUNK_SIZE = 4096; +const size_t IVSIZE = 128 / 8; +const size_t KEYSIZE = 256 / 8; +const uint8_t IV[IVSIZE] = + { 'a', 'e', 's', '2', '5', '6', 'i', 'v', '_', 'c', 't', 'r', '1', '3', '3', '7' }; // no change, version compatibility +class CBC_MODE : public BlockCrypt { private: - static const uint8_t IV[AES_256_IVSIZE]; CephContext* cct; - uint8_t key[AES_256_KEYSIZE]; + uint8_t* iv; + uint8_t* key; public: - explicit AES_256_CBC(CephContext* cct): cct(cct) { + const EVP_CIPHER* type; + const size_t keysize; + const size_t ivsize; + const size_t chunksize; + + explicit CBC_MODE(CephContext* cct, const EVP_CIPHER* _type, const size_t _keysize, + const size_t _ivsize, const size_t _chunksize): + cct(cct), + type(_type), + keysize(_keysize), + ivsize(_ivsize), + chunksize(_chunksize) + { + iv = new uint8_t[ivsize](); + key = new uint8_t[keysize]; + if (ivsize == IVSIZE) { + memcpy(iv , IV, IVSIZE); + } } - ~AES_256_CBC() { - ::ceph::crypto::zeroize_for_security(key, AES_256_KEYSIZE); + ~CBC_MODE() { + delete []iv; + delete []key; } - bool set_key(const uint8_t* _key, size_t key_size) { - if (key_size != AES_256_KEYSIZE) { + bool set_key(const uint8_t* _key, size_t _keysize) { + if (_keysize != keysize) { return false; } - memcpy(key, _key, AES_256_KEYSIZE); + memcpy(key, _key, keysize); return true; } size_t get_block_size() { - return CHUNK_SIZE; + return chunksize; } bool cbc_transform(unsigned char* out, const unsigned char* in, const size_t size, - const unsigned char (&iv)[AES_256_IVSIZE], - const unsigned char (&key)[AES_256_KEYSIZE], + const unsigned char* const _iv, + const unsigned char* const _key, bool encrypt) { - return evp_sym_transform<AES_256_KEYSIZE, AES_256_IVSIZE>( - cct, EVP_aes_256_cbc(), out, in, size, iv, key, encrypt); + return evp_sym_transform(cct, type, out, in, + size, _iv, _key, ivsize, keysize, encrypt); } bool cbc_transform(unsigned char* out, const unsigned char* in, size_t size, off_t stream_offset, - const unsigned char (&key)[AES_256_KEYSIZE], + const unsigned char* _key, bool encrypt) { static std::atomic<bool> failed_to_get_crypto(false); CryptoAccelRef crypto_accel; - if (! failed_to_get_crypto.load()) - { + if (! failed_to_get_crypto.load()) { crypto_accel = get_crypto_accel(cct); if (!crypto_accel) failed_to_get_crypto = true; } bool result = true; - unsigned char iv[AES_256_IVSIZE]; - for (size_t offset = 0; result && (offset < size); offset += CHUNK_SIZE) { - size_t process_size = offset + CHUNK_SIZE <= size ? CHUNK_SIZE : size - offset; + unsigned char iv[IVSIZE]; + unsigned char keybuff[KEYSIZE]; + memcpy(keybuff, _key, keysize); + + for (size_t offset = 0; result && (offset < size); offset += chunksize) { + size_t process_size = offset + chunksize <= size ? chunksize : size - offset; prepare_iv(iv, stream_offset + offset); - if (crypto_accel != nullptr) { + if (EVP_CIPHER_nid(type) != NID_sm4_cbc && crypto_accel != nullptr) { if (encrypt) { result = crypto_accel->cbc_encrypt(out + offset, in + offset, - process_size, iv, key); + process_size, iv, keybuff); } else { result = crypto_accel->cbc_decrypt(out + offset, in + offset, - process_size, iv, key); + process_size, iv, keybuff); } } else { - result = cbc_transform( - out + offset, in + offset, process_size, - iv, key, encrypt); + result = cbc_transform(out + offset, in + offset, + process_size, iv, keybuff, encrypt); } } return result; } - bool encrypt(bufferlist& input, off_t in_ofs, size_t size, @@ -480,10 +499,10 @@ public: off_t stream_offset) { bool result = false; - size_t aligned_size = size / AES_256_IVSIZE * AES_256_IVSIZE; + size_t aligned_size = size / ivsize * ivsize; size_t unaligned_rest_size = size - aligned_size; output.clear(); - buffer::ptr buf(aligned_size + AES_256_IVSIZE); + buffer::ptr buf(aligned_size + ivsize); unsigned char* buf_raw = reinterpret_cast<unsigned char*>(buf.c_str()); const unsigned char* input_raw = reinterpret_cast<const unsigned char*>(input.c_str()); @@ -494,31 +513,30 @@ public: stream_offset, key, true); if (result && (unaligned_rest_size > 0)) { /* remainder to encrypt */ - if (aligned_size % CHUNK_SIZE > 0) { + if (aligned_size % chunksize > 0) { /* use last chunk for unaligned part */ - unsigned char iv[AES_256_IVSIZE] = {0}; + unsigned char iv[IVSIZE] = {0}; result = cbc_transform(buf_raw + aligned_size, - buf_raw + aligned_size - AES_256_IVSIZE, - AES_256_IVSIZE, + buf_raw + aligned_size - ivsize, + ivsize, iv, key, true); } else { - /* 0 full blocks in current chunk, use IV as base for unaligned part */ - unsigned char iv[AES_256_IVSIZE] = {0}; - unsigned char data[AES_256_IVSIZE]; - prepare_iv(data, stream_offset + aligned_size); + unsigned char iv[IVSIZE] = {0}; + std::unique_ptr<unsigned char[]> data(new unsigned char[ivsize]); + prepare_iv(&data[0], stream_offset + aligned_size); result = cbc_transform(buf_raw + aligned_size, - data, - AES_256_IVSIZE, + &data[0], + ivsize, iv, key, true); } if (result) { - for(size_t i = aligned_size; i < size; i++) { + for (size_t i = aligned_size; i < size; i++) { *(buf_raw + i) ^= *(input_raw + in_ofs + i); } } } if (result) { - ldout(cct, 25) << "Encrypted " << size << " bytes"<< dendl; + ldout(cct, 25) << "Encrypted " << size << " bytes" << dendl; buf.set_length(size); output.append(buf); } else { @@ -527,7 +545,6 @@ public: return result; } - bool decrypt(bufferlist& input, off_t in_ofs, size_t size, @@ -535,45 +552,44 @@ public: off_t stream_offset) { bool result = false; - size_t aligned_size = size / AES_256_IVSIZE * AES_256_IVSIZE; + size_t aligned_size = size / ivsize * ivsize; size_t unaligned_rest_size = size - aligned_size; output.clear(); - buffer::ptr buf(aligned_size + AES_256_IVSIZE); + buffer::ptr buf(aligned_size + ivsize); unsigned char* buf_raw = reinterpret_cast<unsigned char*>(buf.c_str()); - unsigned char* input_raw = reinterpret_cast<unsigned char*>(input.c_str()); + const unsigned char* input_raw = reinterpret_cast<const unsigned char*>(input.c_str()); - /* decrypt main bulk of data */ + /* decrypt mian bulk of data */ result = cbc_transform(buf_raw, input_raw + in_ofs, aligned_size, stream_offset, key, false); if (result && unaligned_rest_size > 0) { /* remainder to decrypt */ - if (aligned_size % CHUNK_SIZE > 0) { - /*use last chunk for unaligned part*/ - unsigned char iv[AES_256_IVSIZE] = {0}; + if (aligned_size % chunksize > 0) { + unsigned char iv[IVSIZE] = {0}; result = cbc_transform(buf_raw + aligned_size, - input_raw + in_ofs + aligned_size - AES_256_IVSIZE, - AES_256_IVSIZE, + input_raw + in_ofs + aligned_size - ivsize, + ivsize, iv, key, true); } else { /* 0 full blocks in current chunk, use IV as base for unaligned part */ - unsigned char iv[AES_256_IVSIZE] = {0}; - unsigned char data[AES_256_IVSIZE]; - prepare_iv(data, stream_offset + aligned_size); + unsigned char iv[IVSIZE] = {0}; + std::unique_ptr<unsigned char[]> data(new unsigned char[ivsize]); + prepare_iv(&data[0], stream_offset + aligned_size); result = cbc_transform(buf_raw + aligned_size, - data, - AES_256_IVSIZE, + &data[0], + ivsize, iv, key, true); } if (result) { - for(size_t i = aligned_size; i < size; i++) { + for (size_t i = aligned_size; i < size; i++) { *(buf_raw + i) ^= *(input_raw + in_ofs + i); } } } if (result) { - ldout(cct, 25) << "Decrypted " << size << " bytes"<< dendl; + ldout(cct, 25) << "Decrypted " << size << " bytes" << dendl; buf.set_length(size); output.append(buf); } else { @@ -582,15 +598,14 @@ public: return result; } - - void prepare_iv(unsigned char (&iv)[AES_256_IVSIZE], off_t offset) { - off_t index = offset / AES_256_IVSIZE; - off_t i = AES_256_IVSIZE - 1; + void prepare_iv(unsigned char* _iv, off_t offset) { + off_t index = offset / ivsize; + off_t i = ivsize - 1; unsigned int val; unsigned int carry = 0; - while (i>=0) { - val = (index & 0xff) + IV[i] + carry; - iv[i] = val; + while (i >= 0) { + val = (index & 0xff) + iv[i] + carry; + _iv[i] = val; carry = val >> 8; index = index >> 8; i--; @@ -598,18 +613,21 @@ public: } }; - std::unique_ptr<BlockCrypt> AES_256_CBC_create(CephContext* cct, const uint8_t* key, size_t len) { - auto cbc = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(cct)); + auto cbc = std::unique_ptr<CBC_MODE>(new CBC_MODE(cct, EVP_aes_256_cbc(), + AES_256_KEYSIZE, AES_256_IVSIZE, cct->_conf->rgw_crypt_chunk_size)); cbc->set_key(key, AES_256_KEYSIZE); return cbc; } - -const uint8_t AES_256_CBC::IV[AES_256_CBC::AES_256_IVSIZE] = - { 'a', 'e', 's', '2', '5', '6', 'i', 'v', '_', 'c', 't', 'r', '1', '3', '3', '7' }; - +std::unique_ptr<BlockCrypt> SM4_CBC_create(CephContext* cct, const uint8_t* key, size_t len) +{ + auto cbc = std::unique_ptr<CBC_MODE>(new CBC_MODE(cct, EVP_sm4_cbc(), + SM4_KEYSIZE, SM4_IVSIZE, cct->_conf->rgw_crypt_chunk_size)); + cbc->set_key(key, SM4_KEYSIZE); + return cbc; +} bool AES_256_ECB_encrypt(CephContext* cct, const uint8_t* key, @@ -619,9 +637,8 @@ bool AES_256_ECB_encrypt(CephContext* cct, size_t data_size) { if (key_size == AES_256_KEYSIZE) { - return evp_sym_transform<AES_256_KEYSIZE, 0 /* no IV in ECB */>( - cct, EVP_aes_256_ecb(), data_out, data_in, data_size, - nullptr /* no IV in ECB */, key, true /* encrypt */); + return evp_sym_transform(cct, EVP_aes_256_ecb(), data_out, data_in, data_size, + nullptr /* no IV in ECB */, key, 0, AES_256_KEYSIZE, true /* encrypt */); } else { ldout(cct, 5) << "Key size must be 256 bits long" << dendl; return false; @@ -835,10 +852,10 @@ int RGWPutObj_BlockEncrypt::process(bufferlist&& data, uint64_t logical_offset) } -std::string create_random_key_selector(CephContext * const cct) { - char random[AES_256_KEYSIZE]; - cct->random()->get_bytes(&random[0], sizeof(random)); - return std::string(random, sizeof(random)); +std::string create_random_key_selector(CephContext * const cct, const size_t keysize) { + std::unique_ptr<char[]> random(new char[keysize]); + cct->random()->get_bytes(&random[0], keysize); + return std::string(&random[0], keysize); } typedef enum { @@ -908,11 +925,11 @@ int rgw_s3_prepare_encrypt(struct req_state* s, std::string_view req_sse_ca = get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM); if (! req_sse_ca.empty()) { - if (req_sse_ca != "AES256") { + if (req_sse_ca != "AES256" && req_sse_ca != "SM4") { ldpp_dout(s, 5) << "ERROR: Invalid value for header " << "x-amz-server-side-encryption-customer-algorithm" << dendl; - s->err.message = "The requested encryption algorithm is not valid, must be AES256."; + s->err.message = "The requested encryption algorithm is not valid, must be AES256 or SM4."; return -ERR_INVALID_ENCRYPTION_ALGORITHM; } if (s->cct->_conf->rgw_crypt_require_ssl && @@ -934,11 +951,22 @@ int rgw_s3_prepare_encrypt(struct req_state* s, return -EINVAL; } - if (key_bin.size() != AES_256_CBC::AES_256_KEYSIZE) { - ldpp_dout(s, 5) << "ERROR: invalid encryption key size" << dendl; - s->err.message = "Requests specifying Server Side Encryption with Customer " - "provided keys must provide an appropriate secret key."; - return -EINVAL; + if (req_sse_ca == "AES256") { + if (key_bin.size() != AES_256_KEYSIZE) { + ldpp_dout(s, 5) << "ERROR: invalid encryption key size" << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key."; + return -EINVAL; + } + } + + if (req_sse_ca == "SM4") { + if (key_bin.size() != SM4_KEYSIZE) { + ldpp_dout(s, 5) << "ERROR: invalid encryption key size" << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key."; + return -EINVAL; + } } std::string_view keymd5 = @@ -974,16 +1002,28 @@ int rgw_s3_prepare_encrypt(struct req_state* s, return -EINVAL; } - set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-AES256"); + if (req_sse_ca == "AES256") { + set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-AES256"); + } else if (req_sse_ca == "SM4") { + set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-SM4"); + } set_attr(attrs, RGW_ATTR_CRYPT_KEYMD5, keymd5_bin); if (block_crypt) { - auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct)); - aes->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), AES_256_KEYSIZE); - *block_crypt = std::move(aes); + if (req_sse_ca == "AES256") { + auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_aes_256_cbc(), + AES_256_KEYSIZE, AES_256_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size)); + aes->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), AES_256_KEYSIZE); + *block_crypt = std::move(aes); + } else if (req_sse_ca == "SM4") { + auto sm4 = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_sm4_cbc(), + SM4_KEYSIZE, SM4_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size)); + sm4->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), SM4_KEYSIZE); + *block_crypt = std::move(sm4); + } } - crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "AES256"; + crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = req_sse_ca; crypt_http_responses["x-amz-server-side-encryption-customer-key-MD5"] = std::string(keymd5); return 0; } else { @@ -1036,8 +1076,15 @@ int rgw_s3_prepare_encrypt(struct req_state* s, return -ERR_INVALID_ACCESS_KEY; } /* try to retrieve actual key */ - std::string key_selector = create_random_key_selector(s->cct); - set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-KMS"); + std::string s3_kms_algorithm = s->cct->_conf->rgw_crypt_s3_kms_algorithm; + std::string key_selector; + if (s3_kms_algorithm == "aes") { + key_selector = create_random_key_selector(s->cct, AES_256_KEYSIZE); + set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-KMS-AES"); + } else if (s3_kms_algorithm == "sm4") { + key_selector = create_random_key_selector(s->cct, SM4_KEYSIZE); + set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-KMS-SM4"); + } set_attr(attrs, RGW_ATTR_CRYPT_KEYID, key_id); set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector); set_attr(attrs, RGW_ATTR_CRYPT_CONTEXT, cooked_context); @@ -1048,18 +1095,38 @@ int rgw_s3_prepare_encrypt(struct req_state* s, s->err.message = "Failed to retrieve the actual key, kms-keyid: " + std::string(key_id); return res; } - if (actual_key.size() != AES_256_KEYSIZE) { - ldpp_dout(s, 5) << "ERROR: key obtained from key_id:" << - key_id << " is not 256 bit size" << dendl; - s->err.message = "KMS provided an invalid key for the given kms-keyid."; - return -ERR_INVALID_ACCESS_KEY; - } + if (s3_kms_algorithm == "aes") { + if (actual_key.size() != AES_256_KEYSIZE) { + ldpp_dout(s, 5) << "ERROR: key obtained from key_id:" << + key_id << " is not 256 bit size" << dendl; + s->err.message = "KMS provided an invalid key for the given kms-keyid."; + return -ERR_INVALID_ACCESS_KEY; + } + } - if (block_crypt) { - auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct)); - aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), AES_256_KEYSIZE); - *block_crypt = std::move(aes); - } + if (s3_kms_algorithm == "sm4") { + if (actual_key.size() != SM4_KEYSIZE) { + ldpp_dout(s, 5) << "ERROR: key obtained from key_id:" << + key_id << " is not 128 bit size" << dendl; + s->err.message = "KMS provided an invalid key for the given kms-keyid."; + return -ERR_INVALID_ACCESS_KEY; + } + } + + if (block_crypt) { + if (s3_kms_algorithm == "aes") { + auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_aes_256_cbc(), + AES_256_KEYSIZE, AES_256_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size)); + aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), AES_256_KEYSIZE); + *block_crypt = std::move(aes); + } + if (s3_kms_algorithm == "sm4") { + auto sm4 = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_sm4_cbc(), + SM4_KEYSIZE, SM4_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size)); + sm4->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), SM4_KEYSIZE); + *block_crypt = std::move(sm4); + } + } ::ceph::crypto::zeroize_for_security(actual_key.data(), actual_key.length()); crypt_http_responses["x-amz-server-side-encryption"] = "aws:kms"; @@ -1111,7 +1178,7 @@ int rgw_s3_prepare_encrypt(struct req_state* s, } set_attr(attrs, RGW_ATTR_CRYPT_MODE, "RGW-AUTO"); - std::string key_selector = create_random_key_selector(s->cct); + std::string key_selector = create_random_key_selector(s->cct, AES_256_KEYSIZE); set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector); uint8_t actual_key[AES_256_KEYSIZE]; @@ -1123,7 +1190,8 @@ int rgw_s3_prepare_encrypt(struct req_state* s, return -EIO; } if (block_crypt) { - auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct)); + auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_aes_256_cbc(), + AES_256_KEYSIZE, AES_256_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size)); aes->set_key(reinterpret_cast<const uint8_t*>(actual_key), AES_256_KEYSIZE); *block_crypt = std::move(aes); } @@ -1184,7 +1252,7 @@ int rgw_s3_prepare_decrypt(struct req_state* s, return -EINVAL; } - if (key_bin.size() != AES_256_CBC::AES_256_KEYSIZE) { + if (key_bin.size() != AES_256_KEYSIZE) { ldpp_dout(s, 5) << "ERROR: Invalid encryption key size" << dendl; s->err.message = "Requests specifying Server Side Encryption with Customer " "provided keys must provide an appropriate secret key."; @@ -1223,8 +1291,9 @@ int rgw_s3_prepare_decrypt(struct req_state* s, s->err.message = "The calculated MD5 hash of the key did not match the hash that was provided."; return -EINVAL; } - auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct)); - aes->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), AES_256_CBC::AES_256_KEYSIZE); + auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_aes_256_cbc(), + AES_256_KEYSIZE, AES_256_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size)); + aes->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), AES_256_KEYSIZE); if (block_crypt) *block_crypt = std::move(aes); crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "AES256"; @@ -1232,7 +1301,90 @@ int rgw_s3_prepare_decrypt(struct req_state* s, return 0; } - if (stored_mode == "SSE-KMS") { + if (stored_mode == "SSE-C-SM4") { + if (s->cct->_conf->rgw_crypt_require_ssl && + !rgw_transport_is_secure(s->cct, *s->info.env)) { + ldpp_dout(s, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl; + return -ERR_INVALID_REQUEST; + } + const char *req_cust_alg = + s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL); + + if (nullptr == req_cust_alg) { + ldpp_dout(s, 5) << "ERROR: Request for SSE-C encrypted object missing " + << "x-amz-server-side-encryption-customer-algorithm" + << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide a valid encryption algorithm."; + return -EINVAL; + } else if (strcmp(req_cust_alg, "SM4") != 0) { + ldpp_dout(s, 5) << "ERROR: The requested encryption algorithm is not valid, must be SM4." << dendl; + s->err.message = "The requested encryption algorithm is not valid, must be SM4."; + return -ERR_INVALID_ENCRYPTION_ALGORITHM; + } + + std::string key_bin; + try { + key_bin = from_base64(s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", "")); + } catch (...) { + ldpp_dout(s, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key " + << "which contains character that is not base64 encoded." + << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key."; + return -EINVAL; + } + + if (key_bin.size() != SM4_KEYSIZE) { + ldpp_dout(s, 5) << "ERROR: Invalid encryption key size" << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key."; + return -EINVAL; + } + + std::string keymd5 = + s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", ""); + std::string keymd5_bin; + try { + keymd5_bin = from_base64(keymd5); + } catch (...) { + ldpp_dout(s, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key md5 " + << "which contains character that is not base64 encoded." + << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key md5."; + return -EINVAL; + } + + + if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) { + ldpp_dout(s, 5) << "ERROR: Invalid key md5 size " << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key md5."; + return -EINVAL; + } + + MD5 key_hash; + uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE]; + key_hash.Update(reinterpret_cast<const unsigned char*>(key_bin.c_str()), key_bin.size()); + key_hash.Final(key_hash_res); + + if ((memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) || + (get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYMD5) != keymd5_bin)) { + s->err.message = "The calculated MD5 hash of the key did not match the hash that was provided."; + return -EINVAL; + } + auto sm4 = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_sm4_cbc(), + SM4_KEYSIZE, SM4_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size)); + sm4->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), SM4_KEYSIZE); + if (block_crypt) *block_crypt = std::move(sm4); + + crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "SM4"; + crypt_http_responses["x-amz-server-side-encryption-customer-key-MD5"] = keymd5; + return 0; + } + + if (stored_mode == "SSE-KMS-AES") { if (s->cct->_conf->rgw_crypt_require_ssl && !rgw_transport_is_secure(s->cct, *s->info.env)) { ldpp_dout(s, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl; @@ -1254,7 +1406,8 @@ int rgw_s3_prepare_decrypt(struct req_state* s, return -ERR_INVALID_ACCESS_KEY; } - auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct)); + auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_aes_256_cbc(), + AES_256_KEYSIZE, AES_256_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size)); aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), AES_256_KEYSIZE); actual_key.replace(0, actual_key.length(), actual_key.length(), '\000'); if (block_crypt) *block_crypt = std::move(aes); @@ -1264,6 +1417,39 @@ int rgw_s3_prepare_decrypt(struct req_state* s, return 0; } + if (stored_mode == "SSE-KMS-SM4") { + if (s->cct->_conf->rgw_crypt_require_ssl && + !rgw_transport_is_secure(s->cct, *s->info.env)) { + ldpp_dout(s, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl; + return -ERR_INVALID_REQUEST; + } + /* try to retrieve actual key */ + std::string key_id = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYID); + std::string actual_key; + res = reconstitute_actual_key_from_kms(s->cct, attrs, actual_key); + if (res != 0) { + ldpp_dout(s, 10) << "ERROR: failed to retrieve actual key from key_id: " << key_id << dendl; + s->err.message = "Failed to retrieve the actual key, kms-keyid: " + key_id; + return res; + } + if (actual_key.size() != SM4_KEYSIZE) { + ldpp_dout(s, 0) << "ERROR: key obtained from key_id:" << + key_id << " is not 128 bit size" << dendl; + s->err.message = "KMS provided an invalid key for the given kms-keyid."; + return -ERR_INVALID_ACCESS_KEY; + } + + auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_sm4_cbc(), + SM4_KEYSIZE, SM4_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size)); + aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), SM4_KEYSIZE); + actual_key.replace(0, actual_key.length(), actual_key.length(), '\000'); + if (block_crypt) *block_crypt = std::move(aes); + + crypt_http_responses["x-amz-server-side-encryption"] = "aws:kms"; + crypt_http_responses["x-amz-server-side-encryption-aws-kms-key-id"] = key_id; + return 0; + } + if (stored_mode == "RGW-AUTO") { std::string master_encryption_key; try { @@ -1281,7 +1467,7 @@ int rgw_s3_prepare_decrypt(struct req_state* s, return -EIO; } std::string attr_key_selector = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYSEL); - if (attr_key_selector.size() != AES_256_CBC::AES_256_KEYSIZE) { + if (attr_key_selector.size() != AES_256_KEYSIZE) { ldpp_dout(s, 0) << "ERROR: missing or invalid " RGW_ATTR_CRYPT_KEYSEL << dendl; return -EIO; } @@ -1294,7 +1480,8 @@ int rgw_s3_prepare_decrypt(struct req_state* s, ::ceph::crypto::zeroize_for_security(actual_key, sizeof(actual_key)); return -EIO; } - auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct)); + auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_aes_256_cbc(), + AES_256_KEYSIZE, AES_256_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size)); aes->set_key(actual_key, AES_256_KEYSIZE); ::ceph::crypto::zeroize_for_security(actual_key, sizeof(actual_key)); if (block_crypt) *block_crypt = std::move(aes); diff --git a/src/rgw/rgw_crypt.h b/src/rgw/rgw_crypt.h index ff221549d6fde83a0f05aca08d4ffcf53020d29c..94e12ff4a1433dd520231c6393607e13258a9820 100644 --- a/src/rgw/rgw_crypt.h +++ b/src/rgw/rgw_crypt.h @@ -80,6 +80,9 @@ public: }; static const size_t AES_256_KEYSIZE = 256 / 8; +static const size_t AES_256_IVSIZE = 128 / 8; +static const size_t SM4_KEYSIZE = 128 / 8; +static const size_t SM4_IVSIZE = 128 / 8; bool AES_256_ECB_encrypt(CephContext* cct, const uint8_t* key, size_t key_size, diff --git a/src/rgw/rgw_kms.cc b/src/rgw/rgw_kms.cc index dcdcf875115dbe5f042084cd8b631df2de045954..42aa3d492dbced55bf233311c3276fa8d1ef1e63 100644 --- a/src/rgw/rgw_kms.cc +++ b/src/rgw/rgw_kms.cc @@ -765,16 +765,30 @@ static int get_actual_key_from_conf(CephContext *cct, } if (master_key.length() == AES_256_KEYSIZE) { - uint8_t _actual_key[AES_256_KEYSIZE]; - if (AES_256_ECB_encrypt(cct, - reinterpret_cast<const uint8_t*>(master_key.c_str()), AES_256_KEYSIZE, - reinterpret_cast<const uint8_t*>(key_selector.data()), - _actual_key, AES_256_KEYSIZE)) { - actual_key = std::string((char*)&_actual_key[0], AES_256_KEYSIZE); - } else { - res = -EIO; + std::string s3_kms_algorithm = cct->_conf->rgw_crypt_s3_kms_algorithm; + if (s3_kms_algorithm == "aes") { + uint8_t _actual_key[AES_256_KEYSIZE]; + if (AES_256_ECB_encrypt(cct, + reinterpret_cast<const uint8_t*>(master_key.c_str()), AES_256_KEYSIZE, + reinterpret_cast<const uint8_t*>(key_selector.data()), + _actual_key, AES_256_KEYSIZE)) { + actual_key = std::string((char*)&_actual_key[0], AES_256_KEYSIZE); + } else { + res = -EIO; + } + ::ceph::crypto::zeroize_for_security(_actual_key, sizeof(_actual_key)); + } else if (s3_kms_algorithm == "sm4") { + uint8_t _actual_key[SM4_KEYSIZE]; + if (AES_256_ECB_encrypt(cct, + reinterpret_cast<const uint8_t*>(master_key.c_str()), AES_256_KEYSIZE, + reinterpret_cast<const uint8_t*>(key_selector.data()), + _actual_key, SM4_KEYSIZE)) { + actual_key = std::string((char*)&_actual_key[0], SM4_KEYSIZE); + } else { + res = -EIO; + } + ::ceph::crypto::zeroize_for_security(_actual_key, sizeof(_actual_key)); } - ::ceph::crypto::zeroize_for_security(_actual_key, sizeof(_actual_key)); } else { ldout(cct, 20) << "Wrong size for key=" << key_id << dendl; res = -EIO; @@ -811,9 +825,11 @@ static int request_key_from_barbican(CephContext *cct, return -EACCES; } + std::string s3_kms_algorithm = cct->_conf->rgw_crypt_s3_kms_algorithm; if (secret_req.get_http_status() >=200 && secret_req.get_http_status() < 300 && - secret_bl.length() == AES_256_KEYSIZE) { + ((s3_kms_algorithm == "aes" && secret_bl.length() == AES_256_KEYSIZE) || + (s3_kms_algorithm == "sm4" && secret_bl.length() == SM4_KEYSIZE))) { actual_key.assign(secret_bl.c_str(), secret_bl.length()); secret_bl.zero(); } else { diff --git a/src/test/rgw/test_rgw_crypto.cc b/src/test/rgw/test_rgw_crypto.cc index b0dbabfd4e22ad8e87f47dc6b627efea936e099d..4d42f568cd04ded5c414c2243f5723cfba2b2fdf 100644 --- a/src/test/rgw/test_rgw_crypto.cc +++ b/src/test/rgw/test_rgw_crypto.cc @@ -25,6 +25,7 @@ using namespace std; std::unique_ptr<BlockCrypt> AES_256_CBC_create(CephContext* cct, const uint8_t* key, size_t len); +std::unique_ptr<BlockCrypt> SM4_CBC_create(CephContext* cct, const uint8_t* key, size_t len); class ut_get_sink : public RGWGetObj_Filter { @@ -144,6 +145,56 @@ TEST(TestRGWCrypto, verify_AES_256_CBC_identity) } } +TEST(TestRGWCrypto, verify_SM4_CBC_identity) +{ + //create some input for encryption + const off_t test_range = 1024*1024; + buffer::ptr buf(test_range); + char* p = buf.c_str(); + for(size_t i = 0; i < buf.length(); i++) + p[i] = i + i*i + (i >> 2); + + bufferlist input; + input.append(buf); + + for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17}) + { + //make some random key + uint8_t key[16]; + for(size_t i=0;i<sizeof(key);i++) + key[i]=i*step; + + auto sm4(SM4_CBC_create(g_ceph_context, &key[0], 16)); + ASSERT_NE(sm4.get(), nullptr); + + size_t block_size = sm4->get_block_size(); + ASSERT_NE(block_size, 0u); + + for (size_t r = 97; r < 123 ; r++) + { + off_t begin = (r*r*r*r*r % test_range); + begin = begin - begin % block_size; + off_t end = begin + r*r*r*r*r*r*r % (test_range - begin); + if (r % 3) + end = end - end % block_size; + off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000); + offset = offset - offset % block_size; + + ASSERT_EQ(begin % block_size, 0u); + ASSERT_LE(end, test_range); + ASSERT_EQ(offset % block_size, 0u); + + bufferlist encrypted; + ASSERT_TRUE(sm4->encrypt(input, begin, end - begin, encrypted, offset)); + bufferlist decrypted; + ASSERT_TRUE(sm4->decrypt(encrypted, 0, end - begin, decrypted, offset)); + + ASSERT_EQ(decrypted.length(), end - begin); + ASSERT_EQ(std::string_view(input.c_str() + begin, end - begin), + std::string_view(decrypted.c_str(), end - begin) ); + } + } +} TEST(TestRGWCrypto, verify_AES_256_CBC_identity_2) { @@ -192,6 +243,52 @@ TEST(TestRGWCrypto, verify_AES_256_CBC_identity_2) } } +TEST(TestRGWCrypto, verify_SM4_CBC_identity_2) +{ + //create some input for encryption + const off_t test_range = 1024*1024; + buffer::ptr buf(test_range); + char* p = buf.c_str(); + for(size_t i = 0; i < buf.length(); i++) + p[i] = i + i*i + (i >> 2); + + bufferlist input; + input.append(buf); + + for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17}) + { + //make some random key + uint8_t key[16]; + for(size_t i=0;i<sizeof(key);i++) + key[i]=i*step; + + auto sm4(SM4_CBC_create(g_ceph_context, &key[0], 16)); + ASSERT_NE(sm4.get(), nullptr); + + size_t block_size = sm4->get_block_size(); + ASSERT_NE(block_size, 0u); + + for (off_t end = 1; end < 6096 ; end+=3) + { + off_t begin = 0; + off_t offset = end*end*end*end*end % (1000*1000*1000); + offset = offset - offset % block_size; + + ASSERT_EQ(begin % block_size, 0u); + ASSERT_LE(end, test_range); + ASSERT_EQ(offset % block_size, 0u); + + bufferlist encrypted; + ASSERT_TRUE(sm4->encrypt(input, begin, end, encrypted, offset)); + bufferlist decrypted; + ASSERT_TRUE(sm4->decrypt(encrypted, 0, end, decrypted, offset)); + + ASSERT_EQ(decrypted.length(), end); + ASSERT_EQ(std::string_view(input.c_str(), end), + std::string_view(decrypted.c_str(), end) ); + } + } +} TEST(TestRGWCrypto, verify_AES_256_CBC_identity_3) { @@ -269,6 +366,81 @@ TEST(TestRGWCrypto, verify_AES_256_CBC_identity_3) } } +TEST(TestRGWCrypto, verify_SM4_CBC_identity_3) +{ + //create some input for encryption + const off_t test_range = 1024*1024; + buffer::ptr buf(test_range); + char* p = buf.c_str(); + for(size_t i = 0; i < buf.length(); i++) + p[i] = i + i*i + (i >> 2); + + bufferlist input; + input.append(buf); + + for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17}) + { + //make some random key + uint8_t key[16]; + for(size_t i=0;i<sizeof(key);i++) + key[i]=i*step; + + auto sm4(SM4_CBC_create(g_ceph_context, &key[0], 16)); + ASSERT_NE(sm4.get(), nullptr); + + size_t block_size = sm4->get_block_size(); + ASSERT_NE(block_size, 0u); + size_t rr = 111; + for (size_t r = 97; r < 123 ; r++) + { + off_t begin = 0; + off_t end = begin + r*r*r*r*r*r*r % (test_range - begin); + //sometimes make aligned + if (r % 3) + end = end - end % block_size; + off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000); + offset = offset - offset % block_size; + + ASSERT_EQ(begin % block_size, 0u); + ASSERT_LE(end, test_range); + ASSERT_EQ(offset % block_size, 0u); + + bufferlist encrypted1; + bufferlist encrypted2; + + off_t pos = begin; + off_t chunk; + while (pos < end) { + chunk = block_size + (rr/3)*(rr+17)*(rr+71)*(rr+123)*(rr+131) % 50000; + chunk = chunk - chunk % block_size; + if (pos + chunk > end) + chunk = end - pos; + bufferlist tmp; + ASSERT_TRUE(sm4->encrypt(input, pos, chunk, tmp, offset + pos)); + encrypted1.append(tmp); + pos += chunk; + rr++; + } + + pos = begin; + while (pos < end) { + chunk = block_size + (rr/3)*(rr+97)*(rr+151)*(rr+213)*(rr+251) % 50000; + chunk = chunk - chunk % block_size; + if (pos + chunk > end) + chunk = end - pos; + bufferlist tmp; + ASSERT_TRUE(sm4->encrypt(input, pos, chunk, tmp, offset + pos)); + encrypted2.append(tmp); + pos += chunk; + rr++; + } + ASSERT_EQ(encrypted1.length(), end); + ASSERT_EQ(encrypted2.length(), end); + ASSERT_EQ(std::string_view(encrypted1.c_str(), end), + std::string_view(encrypted2.c_str(), end) ); + } + } +} TEST(TestRGWCrypto, verify_AES_256_CBC_size_0_15) { @@ -318,6 +490,53 @@ TEST(TestRGWCrypto, verify_AES_256_CBC_size_0_15) } } +TEST(TestRGWCrypto, verify_SM4_CBC_size_0_15) +{ + //create some input for encryption + const off_t test_range = 1024*1024; + buffer::ptr buf(test_range); + char* p = buf.c_str(); + for(size_t i = 0; i < buf.length(); i++) + p[i] = i + i*i + (i >> 2); + + bufferlist input; + input.append(buf); + + for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17}) + { + //make some random key + uint8_t key[16]; + for(size_t i=0;i<sizeof(key);i++) + key[i]=i*step; + + auto sm4(SM4_CBC_create(g_ceph_context, &key[0], 16)); + ASSERT_NE(sm4.get(), nullptr); + + size_t block_size = sm4->get_block_size(); + ASSERT_NE(block_size, 0u); + for (size_t r = 97; r < 123 ; r++) + { + off_t begin = 0; + off_t end = begin + r*r*r*r*r*r*r % (16); + + off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000); + offset = offset - offset % block_size; + + ASSERT_EQ(begin % block_size, 0u); + ASSERT_LE(end, test_range); + ASSERT_EQ(offset % block_size, 0u); + + bufferlist encrypted; + bufferlist decrypted; + ASSERT_TRUE(sm4->encrypt(input, 0, end, encrypted, offset)); + ASSERT_TRUE(sm4->encrypt(encrypted, 0, end, decrypted, offset)); + ASSERT_EQ(encrypted.length(), end); + ASSERT_EQ(decrypted.length(), end); + ASSERT_EQ(std::string_view(input.c_str(), end), + std::string_view(decrypted.c_str(), end) ); + } + } +} TEST(TestRGWCrypto, verify_AES_256_CBC_identity_last_block) { @@ -394,8 +613,82 @@ TEST(TestRGWCrypto, verify_AES_256_CBC_identity_last_block) } } +TEST(TestRGWCrypto, verify_SM4_CBC_identity_last_block) +{ + //create some input for encryption + const off_t test_range = 1024*1024; + buffer::ptr buf(test_range); + char* p = buf.c_str(); + for(size_t i = 0; i < buf.length(); i++) + p[i] = i + i*i + (i >> 2); + + bufferlist input; + input.append(buf); + + for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17}) + { + //make some random key + uint8_t key[16]; + for(size_t i=0;i<sizeof(key);i++) + key[i]=i*step; -TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_ranges) + auto sm4(SM4_CBC_create(g_ceph_context, &key[0], 16)); + ASSERT_NE(sm4.get(), nullptr); + + size_t block_size = sm4->get_block_size(); + ASSERT_NE(block_size, 0u); + size_t rr = 111; + for (size_t r = 97; r < 123 ; r++) + { + off_t begin = 0; + off_t end = r*r*r*r*r*r*r % (test_range - 16); + end = end - end % block_size; + end = end + (r+3)*(r+5)*(r+7) % 16; + + off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000); + offset = offset - offset % block_size; + + ASSERT_EQ(begin % block_size, 0u); + ASSERT_LE(end, test_range); + ASSERT_EQ(offset % block_size, 0u); + + bufferlist encrypted1; + bufferlist encrypted2; + + off_t pos = begin; + off_t chunk; + while (pos < end) { + chunk = block_size + (rr/3)*(rr+17)*(rr+71)*(rr+123)*(rr+131) % 50000; + chunk = chunk - chunk % block_size; + if (pos + chunk > end) + chunk = end - pos; + bufferlist tmp; + ASSERT_TRUE(sm4->encrypt(input, pos, chunk, tmp, offset + pos)); + encrypted1.append(tmp); + pos += chunk; + rr++; + } + pos = begin; + while (pos < end) { + chunk = block_size + (rr/3)*(rr+97)*(rr+151)*(rr+213)*(rr+251) % 50000; + chunk = chunk - chunk % block_size; + if (pos + chunk > end) + chunk = end - pos; + bufferlist tmp; + ASSERT_TRUE(sm4->encrypt(input, pos, chunk, tmp, offset + pos)); + encrypted2.append(tmp); + pos += chunk; + rr++; + } + ASSERT_EQ(encrypted1.length(), end); + ASSERT_EQ(encrypted2.length(), end); + ASSERT_EQ(std::string_view(encrypted1.c_str(), end), + std::string_view(encrypted2.c_str(), end) ); + } + } +} + +TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_ranges_AES) { //create some input for encryption const off_t test_range = 1024*1024; @@ -440,8 +733,52 @@ TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_ranges) } } +TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_ranges_SM4) +{ + //create some input for encryption + const off_t test_range = 1024*1024; + bufferptr buf(test_range); + char* p = buf.c_str(); + for(size_t i = 0; i < buf.length(); i++) + p[i] = i + i*i + (i >> 2); + + bufferlist input; + input.append(buf); + + uint8_t key[16]; + for(size_t i=0;i<sizeof(key);i++) + key[i] = i; + + auto cbc = SM4_CBC_create(g_ceph_context, &key[0], 16); + ASSERT_NE(cbc.get(), nullptr); + bufferlist encrypted; + ASSERT_TRUE(cbc->encrypt(input, 0, test_range, encrypted, 0)); + -TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_chunks) + for (off_t r = 93; r < 150; r++ ) + { + ut_get_sink get_sink; + auto cbc = SM4_CBC_create(g_ceph_context, &key[0], 16); + ASSERT_NE(cbc.get(), nullptr); + RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink, std::move(cbc) ); + + //random ranges + off_t begin = (r/3)*r*(r+13)*(r+23)*(r+53)*(r+71) % test_range; + off_t end = begin + (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - begin) - 1; + + off_t f_begin = begin; + off_t f_end = end; + decrypt.fixup_range(f_begin, f_end); + decrypt.handle_data(encrypted, f_begin, f_end - f_begin + 1); + decrypt.flush(); + const std::string& decrypted = get_sink.get_sink(); + size_t expected_len = end - begin + 1; + ASSERT_EQ(decrypted.length(), expected_len); + ASSERT_EQ(decrypted, std::string_view(input.c_str()+begin, expected_len)); + } +} + +TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_chunks_AES) { //create some input for encryption const off_t test_range = 1024*1024; @@ -496,6 +833,60 @@ TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_chunks) } } +TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_chunks_SM4) +{ + //create some input for encryption + const off_t test_range = 1024*1024; + bufferptr buf(test_range); + char* p = buf.c_str(); + for(size_t i = 0; i < buf.length(); i++) + p[i] = i + i*i + (i >> 2); + + bufferlist input; + input.append(buf); + + uint8_t key[16]; + for(size_t i=0;i<sizeof(key);i++) + key[i] = i; + + auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 16); + ASSERT_NE(cbc.get(), nullptr); + bufferlist encrypted; + ASSERT_TRUE(cbc->encrypt(input, 0, test_range, encrypted, 0)); + + for (off_t r = 93; r < 150; r++ ) + { + ut_get_sink get_sink; + auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 16); + ASSERT_NE(cbc.get(), nullptr); + RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink, std::move(cbc) ); + + //random + off_t begin = (r/3)*r*(r+13)*(r+23)*(r+53)*(r+71) % test_range; + off_t end = begin + (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - begin) - 1; + + off_t f_begin = begin; + off_t f_end = end; + decrypt.fixup_range(f_begin, f_end); + off_t pos = f_begin; + do + { + off_t size = 2 << ((pos * 17 + pos / 113 + r) % 16); + size = (pos + 1117) * (pos + 2229) % size + 1; + if (pos + size > f_end + 1) + size = f_end + 1 - pos; + + decrypt.handle_data(encrypted, pos, size); + pos = pos + size; + } while (pos < f_end + 1); + decrypt.flush(); + + const std::string& decrypted = get_sink.get_sink(); + size_t expected_len = end - begin + 1; + ASSERT_EQ(decrypted.length(), expected_len); + ASSERT_EQ(decrypted, std::string_view(input.c_str()+begin, expected_len)); + } +} using range_t = std::pair<off_t, off_t>; @@ -688,7 +1079,7 @@ TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup_invalid_ranges) } -TEST(TestRGWCrypto, verify_RGWPutObj_BlockEncrypt_chunks) +TEST(TestRGWCrypto, verify_RGWPutObj_BlockEncrypt_chunks_AES) { //create some input for encryption const off_t test_range = 1024*1024; @@ -745,8 +1136,64 @@ TEST(TestRGWCrypto, verify_RGWPutObj_BlockEncrypt_chunks) } } +TEST(TestRGWCrypto, verify_RGWPutObj_BlockEncrypt_chunks_SM4) +{ + //create some input for encryption + const off_t test_range = 1024*1024; + bufferptr buf(test_range); + char* p = buf.c_str(); + for(size_t i = 0; i < buf.length(); i++) + p[i] = i + i*i + (i >> 2); + + bufferlist input; + input.append(buf); + + uint8_t key[16]; + for(size_t i=0;i<sizeof(key);i++) + key[i] = i; + + for (off_t r = 93; r < 150; r++ ) + { + ut_put_sink put_sink; + auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 16); + ASSERT_NE(cbc.get(), nullptr); + RGWPutObj_BlockEncrypt encrypt(g_ceph_context, &put_sink, + std::move(cbc) ); + + off_t test_size = (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - 1) + 1; + off_t pos = 0; + do + { + off_t size = 2 << ((pos * 17 + pos / 113 + r) % 16); + size = (pos + 1117) * (pos + 2229) % size + 1; + if (pos + size > test_size) + size = test_size - pos; + + bufferlist bl; + bl.append(input.c_str()+pos, size); + encrypt.process(std::move(bl), pos); + + pos = pos + size; + } while (pos < test_size); + encrypt.process({}, pos); + + ASSERT_EQ(put_sink.get_sink().length(), static_cast<size_t>(test_size)); + + cbc = AES_256_CBC_create(g_ceph_context, &key[0], 16); + ASSERT_NE(cbc.get(), nullptr); + + bufferlist encrypted; + bufferlist decrypted; + encrypted.append(put_sink.get_sink()); + ASSERT_TRUE(cbc->decrypt(encrypted, 0, test_size, decrypted, 0)); + + ASSERT_EQ(decrypted.length(), test_size); + ASSERT_EQ(std::string_view(decrypted.c_str(), test_size), + std::string_view(input.c_str(), test_size)); + } +} -TEST(TestRGWCrypto, verify_Encrypt_Decrypt) +TEST(TestRGWCrypto, verify_Encrypt_Decrypt_AES) { uint8_t key[32]; for(size_t i=0;i<sizeof(key);i++) @@ -795,6 +1242,54 @@ TEST(TestRGWCrypto, verify_Encrypt_Decrypt) while (test_size < 20000); } +TEST(TestRGWCrypto, verify_Encrypt_Decrypt_SM4) +{ + uint8_t key[16]; + for(size_t i=0;i<sizeof(key);i++) + key[i]=i; + + size_t fi_a = 0; + size_t fi_b = 1; + size_t test_size; + do + { + //fibonacci + size_t tmp = fi_b; + fi_b = fi_a + fi_b; + fi_a = tmp; + + test_size = fi_b; + + uint8_t* test_in = new uint8_t[test_size]; + //fill with something + memset(test_in, test_size & 0xff, test_size); + + ut_put_sink put_sink; + RGWPutObj_BlockEncrypt encrypt(g_ceph_context, &put_sink, + AES_256_CBC_create(g_ceph_context, &key[0], 16) ); + bufferlist bl; + bl.append((char*)test_in, test_size); + encrypt.process(std::move(bl), 0); + encrypt.process({}, test_size); + ASSERT_EQ(put_sink.get_sink().length(), test_size); + + bl.append(put_sink.get_sink().data(), put_sink.get_sink().length()); + ASSERT_EQ(bl.length(), test_size); + + ut_get_sink get_sink; + RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink, + AES_256_CBC_create(g_ceph_context, &key[0], 16) ); + + off_t bl_ofs = 0; + off_t bl_end = test_size - 1; + decrypt.fixup_range(bl_ofs, bl_end); + decrypt.handle_data(bl, 0, bl.length()); + decrypt.flush(); + ASSERT_EQ(get_sink.get_sink().length(), test_size); + ASSERT_EQ(get_sink.get_sink(), std::string_view((char*)test_in,test_size)); + } + while (test_size < 20000); +} int main(int argc, char **argv) { vector<const char*> args;