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