github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/c-deps/libroach/ccl/ctr_stream.cc (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Licensed as a CockroachDB Enterprise file under the Cockroach Community 4 // License (the "License"); you may not use this file except in compliance with 5 // the License. You may obtain a copy of the License at 6 // 7 // https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt 8 // (found in the licenses/CCL.txt file in the root directory). 9 10 #include "ctr_stream.h" 11 #include <google/protobuf/stubs/port.h> 12 #include "../fmt.h" 13 #include "../plaintext_stream.h" 14 #include "crypto_utils.h" 15 16 using namespace cockroach; 17 18 namespace cockroach { 19 20 CTRCipherStreamCreator::~CTRCipherStreamCreator() {} 21 22 rocksdb::Status CTRCipherStreamCreator::InitSettingsAndCreateCipherStream( 23 std::string* settings, std::unique_ptr<rocksdb_utils::BlockAccessCipherStream>* result) { 24 auto key = key_manager_->CurrentKey(); 25 26 // Create the settings. 27 enginepbccl::EncryptionSettings enc_settings; 28 29 if (key == nullptr || key->info().encryption_type() == enginepbccl::Plaintext) { 30 // Plaintext algorithm: only encryption_type is specified. 31 enc_settings.set_encryption_type(enginepbccl::Plaintext); 32 33 result->reset(new PlaintextStream()); 34 } else { 35 // AES encryption: generate parameters and store in settings. 36 enc_settings.set_encryption_type(key->info().encryption_type()); 37 enc_settings.set_key_id(key->info().key_id()); 38 39 // Let's get 16 random bytes. 12 for the nonce, 4 for the counter. 40 std::string random_bytes = RandomBytes(16); 41 assert(random_bytes.size() == 16); 42 43 // First 12 bytes for the nonce. 44 enc_settings.set_nonce(random_bytes.substr(0, 12)); 45 // Last 4 as an unsigned int32 for the counter. 46 uint32_t counter; 47 memcpy(&counter, random_bytes.data() + 12, 4); 48 enc_settings.set_counter(counter); 49 50 result->reset(new CTRCipherStream(key, enc_settings.nonce(), enc_settings.counter())); 51 } 52 53 // Serialize enc_settings directly into the passed settings pointer. This will be ignored 54 // on error. 55 if (!enc_settings.SerializeToString(settings)) { 56 return rocksdb::Status::InvalidArgument("failed to serialize encryption settings"); 57 } 58 59 return rocksdb::Status::OK(); 60 } 61 62 // Create a cipher stream given encryption settings. 63 rocksdb::Status CTRCipherStreamCreator::CreateCipherStreamFromSettings( 64 const std::string& settings, std::unique_ptr<rocksdb_utils::BlockAccessCipherStream>* result) { 65 enginepbccl::EncryptionSettings enc_settings; 66 if (settings.size() > 0 && !enc_settings.ParseFromString(settings)) { 67 return rocksdb::Status::InvalidArgument("failed to parse encryption settings"); 68 } 69 70 if (settings.size() == 0 || enc_settings.encryption_type() == enginepbccl::Plaintext) { 71 // No entry (pre-registry file therefore plaintext) or plaintext algorithm. 72 result->reset(new PlaintextStream()); 73 return rocksdb::Status::OK(); 74 } 75 76 // Get the key from the manager. 77 auto key = key_manager_->GetKey(enc_settings.key_id()); 78 if (key == nullptr) { 79 return rocksdb::Status::InvalidArgument(fmt::StringPrintf( 80 "store key ID %s was not found", enc_settings.key_id().c_str())); 81 } 82 83 result->reset(new CTRCipherStream(key, enc_settings.nonce(), enc_settings.counter())); 84 return rocksdb::Status::OK(); 85 } 86 87 enginepb::EnvType CTRCipherStreamCreator::GetEnvType() { return env_type_; } 88 89 CTRCipherStream::CTRCipherStream(std::shared_ptr<enginepbccl::SecretKey> key, 90 const std::string& nonce, uint32_t counter) 91 : key_(key), nonce_(nonce), counter_(counter) {} 92 93 CTRCipherStream::~CTRCipherStream() {} 94 95 rocksdb::Status 96 CTRCipherStream::InitCipher(std::unique_ptr<rocksdb_utils::BlockCipher>* cipher) const { 97 // We should not be getting called for plaintext, and we only have AES. 98 if (key_->info().encryption_type() != enginepbccl::AES128_CTR && 99 key_->info().encryption_type() != enginepbccl::AES192_CTR && 100 key_->info().encryption_type() != enginepbccl::AES256_CTR) { 101 return rocksdb::Status::InvalidArgument( 102 fmt::StringPrintf("unknown encryption type %d", key_->info().encryption_type())); 103 } 104 105 cipher->reset(NewAESEncryptCipher(key_.get())); 106 return rocksdb::Status::OK(); 107 } 108 109 rocksdb::Status CTRCipherStream::EncryptBlock(rocksdb_utils::BlockCipher* cipher, 110 uint64_t blockIndex, char* data, 111 char* scratch) const { 112 // Create IV = nonce + counter 113 auto blockSize = cipher->BlockSize(); 114 auto nonce_size = blockSize - 4; 115 // Write the nonce at the beginning of the scratch space. 116 memcpy(scratch, nonce_.data(), nonce_size); 117 118 // Counter value for this block, converted to network byte order. 119 uint32_t block_counter = google::protobuf::ghtonl(counter_ + blockIndex); 120 // Write after the nonce. 121 memcpy(scratch + nonce_size, &block_counter, 4); 122 123 // Encrypt nonce+counter 124 auto status = cipher->Encrypt(scratch); 125 if (!status.ok()) { 126 return status; 127 } 128 129 // XOR data with ciphertext. 130 // TODO(mberhault): this is not an efficient XOR. Instead, we could move 131 // this into the cipher and use something like CryptoPP::ProcessAndXorBlock. 132 for (size_t i = 0; i < blockSize; i++) { 133 data[i] = data[i] ^ scratch[i]; 134 } 135 return rocksdb::Status::OK(); 136 } 137 138 // Decrypt a block of data at the given block index. 139 // Length of data is equal to BlockSize(); 140 rocksdb::Status CTRCipherStream::DecryptBlock(rocksdb_utils::BlockCipher* cipher, 141 uint64_t blockIndex, char* data, 142 char* scratch) const { 143 // For CTR decryption & encryption are the same 144 return EncryptBlock(cipher, blockIndex, data, scratch); 145 } 146 147 } // namespace cockroach