/* * Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers * All rights reserved. * Licensed under the Apache License, Version 2.0 * * or the MIT license , * at your option. All files in the project carrying such * notice may not be copied, modified, or distributed except * according to those terms. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. // Copyright (c) 2020 Intel Corporation // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). */ #ifndef ROCKSDB_LITE #include "openssl_provider.h" #include #include "rocksdb/utilities/customizable_util.h" #include #include #include #include #include #include "portable_endian.h" #endif namespace ROCKSDB_NAMESPACE { #ifndef ROCKSDB_LITE static void RegisterEncryptionAES() { static std::once_flag once; std::call_once(once, [&]() { ERR_load_crypto_strings(); ObjectLibrary::Default()->AddFactory( OpensslProvider::kName(), [](const std::string& /* uri */, std::unique_ptr* f, std::string* /* errmsg */) { *f = OpensslProvider::CreateProvider(); return f->get(); }); }); } Status EncryptionProvider::CreateFromString( const ConfigOptions& config_options, const std::string& value, std::shared_ptr* result) { RegisterEncryptionAES(); return LoadSharedObject(config_options, value, result); } // extern "C" FactoryFunc ippcp_reg; // FactoryFunc ippcp_reg = // ObjectLibrary::Default()->AddFactory( // OpensslProvider::kName(), // [](const std::string& /* uri */, std::unique_ptr* f, // std::string* /* errmsg */) { // *f = OpensslProvider::CreateProvider(); // return f->get(); // }); // OpensslCipherStream implements BlockAccessCipherStream using AES block // cipher and a CTR mode of operation. // class OpensslCipherStream : public BlockAccessCipherStream { public: static constexpr size_t kBlockSize = 16; // in bytes //static constexpr size_t kCounterLen = 64; // in bits OpensslCipherStream(const EVP_CIPHER *aes_cipher, const unsigned char* key, const char* init_vector); ~OpensslCipherStream(); virtual Status Encrypt(uint64_t fileOffset, char* data, size_t dataSize) override; virtual Status Decrypt(uint64_t fileOffset, char* data, size_t dataSize) override; virtual size_t BlockSize() override { return kBlockSize; } protected: // These functions are not needed and will never be called! virtual void AllocateScratch(std::string&) override {} virtual Status EncryptBlock(uint64_t, char*, char*) override { return Status::NotSupported("Operation not supported."); } virtual Status DecryptBlock(uint64_t, char*, char*) override { return Status::NotSupported("Operation not supported."); } private: const EVP_CIPHER *aes_cipher_; const unsigned char* key_; char init_vector_[kBlockSize]; Status handleErrors(const char * str); }; OpensslCipherStream::OpensslCipherStream(const EVP_CIPHER *aes_cipher, const unsigned char* key, const char* init_vector) : aes_cipher_(aes_cipher), key_(key) { memcpy(init_vector_,init_vector,kBlockSize); //ctx_ = EVP_CIPHER_CTX_new(); } OpensslCipherStream::~OpensslCipherStream() { //EVP_CIPHER_free(aes_cipher_); //if (ctx_ != nullptr) EVP_CIPHER_CTX_free(ctx_); } Status OpensslCipherStream::handleErrors(const char * str) { # ifndef OPENSSL_NO_STDIO ERR_print_errors_fp(stderr); # endif // if (ctx_ != nullptr) { // EVP_CIPHER_CTX_free(ctx_); // ctx_ = nullptr; // } return Status::Aborted(str); } // #include // void printb(const char * name, const char * buffer, size_t n) { // int nn = static_cast(n); // printf("%s (%d)\n",name,nn); // for(int i = 0; i(buffer[i])); // printf("\n"); // } Status OpensslCipherStream::Encrypt(uint64_t fileOffset, char* data, size_t dataSize) { if (dataSize == 0) return Status::OK(); const char * err_str = nullptr; EVP_CIPHER_CTX* ctx_ = EVP_CIPHER_CTX_new(); // if ( 1 != EVP_CIPHER_CTX_reset(ctx_)) { err_str="Failed to reset context."; goto error; } size_t index = fileOffset / kBlockSize; size_t offset = fileOffset % kBlockSize; // printf("\nfileOffset %" PRIu64 " INDEX %zu OFFSET %zu\n",fileOffset,index,offset); // printb("data",data,dataSize); unsigned char ctr_block[kBlockSize]; uint64_t init_vector_lower_part; memcpy(&init_vector_lower_part, init_vector_+8, sizeof init_vector_lower_part); // printb("right part of IV",init_vector_+8,8); uint64_t init_vector_lower_part_h = be64toh(init_vector_lower_part); // printf("right part of IV as host %" PRIu64 "\n",init_vector_lower_part_h); memcpy(ctr_block, init_vector_, 8); ctr_block[7] &= 254; // we zero the right-most bit, for the eventual remainder // printb("left part of IV",(const char*)ctr_block,8); if (index > ULLONG_MAX - init_vector_lower_part_h) { // we have an overflow already now, even before dataSize is added // we set the remainder bit // use |= 1 to set the right-most last bit to 1 (the remainder) ctr_block[7] |= 1; // printf("will overflow\n"); // printb("new left part of IV",(const char*)ctr_block,8); } uint64_t be_counter = htobe64(index + init_vector_lower_part_h); // printf("counter as host %" PRIu64 "\n",index + init_vector_lower_part_h); // printf("counter as BE %" PRIu64 "\n",be_counter); char* ptr_counter = (char*)&be_counter; for (size_t i = 8; i < 16; ++i) ctr_block[i] = ptr_counter[i-8]; // printb("right part of IV",(const char*)ctr_block+8,8); int len; if( 1 != EVP_EncryptInit_ex(ctx_, aes_cipher_, NULL, key_, ctr_block)) {err_str="Failed to init cipher."; goto error;} EVP_CIPHER_CTX_set_padding(ctx_, 0); if (offset == 0) { unsigned char *out = (unsigned char*)malloc(dataSize); if( 1 != EVP_EncryptUpdate(ctx_, out, &len, reinterpret_cast(data), static_cast(dataSize))) {err_str="Failed to encrypt."; goto error;} memcpy(data, out, dataSize); //EVP_EncryptFinal_ex(ctx_, reinterpret_cast(data) + len, &len); } else { unsigned char zero_block[kBlockSize]{0}; unsigned char zero_block_out[kBlockSize]{0}; if( 1 != EVP_EncryptUpdate(ctx_, zero_block_out, &len, zero_block, static_cast(kBlockSize))) {err_str="Failed to encrypt zero block."; goto error;} //unsigned char * end = reinterpret_cast(zero_block) + len; size_t n = std::min(kBlockSize - offset, dataSize); for (size_t i = 0; i < n; ++i) data[i] ^= zero_block_out[offset + i]; //memset(zero_block, 0, kBlockSize); n = kBlockSize - offset; if (dataSize > n) { char* ptr = (char*)(data + n); unsigned char *out = (unsigned char*)malloc(dataSize - n); if( 1 != EVP_EncryptUpdate(ctx_, out, &len, reinterpret_cast(ptr), static_cast(dataSize - n))) {err_str="Failed to encrypt remaining."; goto error;} memcpy(ptr, out, dataSize - n); //end = reinterpret_cast(ptr) + len; } //EVP_EncryptFinal_ex(ctx_, end, &len); } if (ctx_ != nullptr) EVP_CIPHER_CTX_free(ctx_); return Status::OK(); error: if (ctx_ != nullptr) EVP_CIPHER_CTX_free(ctx_); ERR_print_errors_fp(stderr); if (err_str != nullptr) return Status::Aborted(err_str); else return Status::Aborted("unknown error"); } Status OpensslCipherStream::Decrypt(uint64_t fileOffset, char* data, size_t dataSize) { // Decryption is implemented as encryption in CTR mode of operation return Encrypt(fileOffset, data, dataSize); } std::unique_ptr OpensslProvider::CreateProvider() { return std::unique_ptr(new OpensslProvider); } Status OpensslProvider::handleErrors (const char * str) const { # ifndef OPENSSL_NO_STDIO ERR_print_errors_fp(stderr); # endif return Status::Aborted(str); } Status OpensslProvider::AddCipher(const std::string& /*descriptor*/, const char* cipher, size_t len, bool /*for_write*/) { // We currently don't support more than one encryption key if (aes_cipher_ != nullptr) { return Status::InvalidArgument("Multiple encryption keys not supported."); } if (len == 16) { aes_cipher_ = EVP_aes_128_ctr(); } else if (len == 24) { aes_cipher_ = EVP_aes_192_ctr(); } else if (len == 32) { aes_cipher_ = EVP_aes_256_ctr(); } else return Status::InvalidArgument("Invalid key size in provider."); memcpy(key_,cipher,len); //if( 1 != EVP_CIPHER_up_ref(aes_cipher_)) return handleErrors("Failed to create provider."); return Status::OK(); } Status OpensslProvider::CreateNewPrefix(const std::string& /*fname*/, char* prefix, size_t prefixLength) const { if (1 != RAND_bytes(reinterpret_cast(prefix), static_cast(OpensslCipherStream::kBlockSize)) ){ return handleErrors("Failed to get random numbers.");//Status::Aborted(ERR_reason_error_string(ERR_get_error())); } //if( 1 != EVP_CIPHER_up_ref(aes_cipher_)) return handleErrors("Failed to create OpensslCipherStream."); // printb("new prefix",prefix,32); OpensslCipherStream cs(aes_cipher_, key_, prefix); Status s = cs.Encrypt(0, prefix + OpensslCipherStream::kBlockSize, prefixLength - OpensslCipherStream::kBlockSize); // printb("encoded prefix",prefix + OpensslCipherStream::kBlockSize,16); return s; } Status OpensslProvider::CreateCipherStream( const std::string& /*fname*/, const EnvOptions& /*options*/, Slice& prefix, std::unique_ptr* result) { assert(result != nullptr); assert(prefix.size() >= OpensslCipherStream::kBlockSize); //if( 1 != EVP_CIPHER_up_ref(aes_cipher_)) return handleErrors("Failed to create OpensslCipherStream."); result->reset(new OpensslCipherStream(aes_cipher_, key_, prefix.data())); Status status = (*result)->Decrypt( 0, (char*)prefix.data() + OpensslCipherStream::kBlockSize, prefix.size() - OpensslCipherStream::kBlockSize); // printb("decoded prefix",(char*)prefix.data() + OpensslCipherStream::kBlockSize,16); return status; } OpensslProvider::~OpensslProvider() { ////EVP_CIPHER_free(aes_cipher_); //FIXME: zero the key //memset(key_, 0, key_len_); (void)(key_len_); } #endif // ROCKSDB_LITE } // namespace ROCKSDB_NAMESPACE