Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ add_library(binsrv::lib_util ALIAS lib_util)

# OpenSSL++ files
set(opensslpp_source_files
src/opensslpp/cipher_mode_type_fwd.hpp
src/opensslpp/cipher_mode_type.hpp

src/opensslpp/cipher_context_fwd.hpp
src/opensslpp/cipher_context.hpp
src/opensslpp/cipher_context.cpp
Expand Down Expand Up @@ -531,6 +534,7 @@ set(binsrv_source_files

src/binsrv/keyring_record_fwd.hpp
src/binsrv/keyring_record.hpp
src/binsrv/keyring_record.cpp

src/binsrv/main_config_fwd.hpp
src/binsrv/main_config.hpp
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,9 @@ If this section is present, then all the binlog data files will be encrypted bef
- `<storage.encryption.format>` - specifies the encryption format (currently only `generic` is supported).
- `<storage.encryption.keyring_uri>` - specifies location of the keyring JSON data file (currently only 'file://' scheme is supported meaning that the file should be taken from the local file sytem from the path specified in this URI, e.g. `file:///var/lib/pbs/keyring/keyring_data.json`).
- `<storage.encryption.kek_id>` - specifies the ID of the key that must be used as a key-encryption-key (KEK). This ID must be present in the keyring.
- `<storage.encryption.cipher>` - specifies the data-encryption cipher name used binlog data file encryption (e.g. `AES-256-CTR`).
- `<storage.encryption.cipher>` - specifies the data-encryption cipher name used binlog data file encryption (e.g. `AES-256-CTR`). The cipher name specified here must be in `CTR` mode.

Please also notice that not all combinations of the `cipher` and KEK identified by `kek_id` are supported. For instance, if the cipher from the keyring record identified by `kek_id` is either `AES-NNN-ECB` or `AES-NNN-CBC`, then they can encrypt only file keys with lengths that are a multiple of `16` bytes. In other words, in this case it is OK for `<storage.encryption.cipher>` to be `XXX-128-CTR` or `XXX-256-CTR`, but not OK to be `XXX-192-CTR`.

##### Keyring file format
```json
Expand All @@ -669,11 +671,14 @@ If this section is present, then all the binlog data files will be encrypted bef
```
Keyring JSON file should represent a top-level JSON object with the following keys.
- `version` - currently should always be equal to `1`.
- `keys` - should be an array of objects tith the following keys
- `keys` - should be an array of objects with the following keys
- `id` - a unique string identifier of the key in the keyring.
- `cipher` - the name of the symmetric cypher which should be used with this key (e.g `AES-256-GCM`).
- `data_hex` - key bytes in hex format (typically `16`, `24`, or `32` bytes, meaning `32`, `48`, or `64` characters)

Make sure that the mode of the `cipher` is one of the `ECB`, `CBC`, `CTR`, or `GCM`. Also, make sure that the key size identified from the cipher name matches the actual `data_hex` length (for instance, for `AES-256-GCM`, the key length should be `256` bits, meaning `32` bytes, meaning `64` hexadecimal characters).
As for the `algoritm` part of the cipher name, PBS has been tested with `AES`, `AREA`, and `CAMELLIA`. However, other algorithms be supported as well.

### Resuming previous operation

Running the utility for the second time (in any mode) results in resuming streaming from the position at which the previous run finished.
Expand Down
12 changes: 10 additions & 2 deletions src/binsrv/encryption_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,23 @@

#include "util/exception_location_helpers.hpp"

#include "opensslpp/cipher_context.hpp"
#include "opensslpp/cipher_mode_type.hpp"

namespace binsrv {

void encryption_config::validate() const {
if (get<"format">() != encryption_format_type::generic) {
util::exception_location().raise<std::invalid_argument>(
"error validating storage encryption config: unsupported format");
}
// TODO: make sure that data encryption cipher is supported by OpenSSL
// and has CTR mode

if (opensslpp::cipher_context::get_mode(get<"cipher">()) !=
opensslpp::cipher_mode_type::ctr) {
util::exception_location().raise<std::invalid_argument>(
"error validating storage encryption config: only CTR mode is "
"supported for data encryption cipher");
}
}

} // namespace binsrv
42 changes: 42 additions & 0 deletions src/binsrv/keyring_record.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2023-2024 Percona and/or its affiliates.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 2.0,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License, version 2.0, for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

#include "binsrv/keyring_record.hpp"

#include <stdexcept>
#include <string>

#include "opensslpp/cipher_context.hpp"
#include "util/exception_location_helpers.hpp"

namespace binsrv {

void keyring_record::validate() const {
const auto &cipher_name = get<"cipher">();

const auto &key_id = get<"id">();
if (!opensslpp::cipher_context::is_cipher_name_supported(cipher_name)) {
util::exception_location().raise<std::invalid_argument>(
"unsupported cipher in keyring record: '" + key_id + "'");
}

if (get<"data_hex">().get_size() !=
opensslpp::cipher_context::get_key_size_in_bytes(cipher_name)) {
util::exception_location().raise<std::invalid_argument>(
"key data length mismatch in keyring record '" + key_id + "'");
}
}

} // namespace binsrv
1 change: 1 addition & 0 deletions src/binsrv/keyring_record.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct [[nodiscard]] keyring_record
result += ')';
return result;
}
void validate() const;
};

} // namespace binsrv
Expand Down
19 changes: 13 additions & 6 deletions src/binsrv/keyring_record_collection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <stdexcept>
#include <string>
#include <string_view>
#include <unordered_set>

#include <boost/json/parse.hpp>

Expand Down Expand Up @@ -62,14 +63,20 @@ void keyring_record_collection::validate() const {
util::exception_location().raise<std::invalid_argument>(
"unsupported keyring record collection version");
}
// TODO: make sure that all the keys have unique IDs
const auto &keys{root().get<"keys">()};

using key_id_container = std::unordered_set<std::string>;
key_id_container unique_ids{};

// TODO: make sure that all keys have ciphers known in OpenSSL
// (currently only ECB, CBC, CTR, GCM modes are supported by
// opensslpp::cipher_context)
for (const auto &key : keys) {
const auto &key_id = key.get<"id">();
if (!unique_ids.insert(key_id).second) {
util::exception_location().raise<std::invalid_argument>(
"duplicate key id in keyring record collection: '" + key_id + "'");
}

// TODO: make sure that all keys have data_hex values of the correct length
// that matches with the cipher
key.validate();
}
}

[[nodiscard]] std::string keyring_record_collection::get_description() const {
Expand Down
28 changes: 19 additions & 9 deletions src/binsrv/storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,24 @@ storage::storage(const storage_config &config,
util::exception_location().raise<std::runtime_error>(
"keyring does not contain the specified KEK ID");
}
// TODO: make sure that random file keys (of length that corresponds to the
// active data cipher) can be encrypted with the active KEK -
// for instance, if active data cipher is AES-192-CRT (key length 24
// bytes), then the active KEK cannot be of ECB or CBC mode as these
// ciphers can only encrypt data of length that is a multiple of the
// block size (16 bytes)
active_kek_id_ = kek_id;
active_data_cipher_ = encryption_config->get<"cipher">();

// make sure that random file keys (of length that corresponds to the
// active data cipher) can be encrypted with the active KEK -
// for instance, if active data cipher is AES-192-CRT (key length 24
// bytes), then the active KEK cannot be of ECB or CBC mode as these
// ciphers can only encrypt data of length that is a multiple of the
// block size (16 bytes)
const auto &keyring_record{keyring_->get_key(active_kek_id_)};
if (opensslpp::cipher_context::get_key_size_in_bytes(active_data_cipher_) %
opensslpp::cipher_context::get_block_size_in_bytes(
keyring_record.get<"cipher">()) !=
0U) {
util::exception_location().raise<std::runtime_error>(
"active data cipher key length is not compatible with the active "
"KEK cipher block size");
}
}

backend_ = storage_backend_factory::create(config);
Expand Down Expand Up @@ -896,7 +906,7 @@ storage::generate_binlog_encryption_record() const {
// creating an encryption context with the KEK cipher, the KEK, and
// the IV for file key encryption
opensslpp::cipher_context file_key_encryption_context{
opensslpp::cipher_context_mode_type::encryption, kek_cipher, kek,
opensslpp::cipher_context_operation_type::encryption, kek_cipher, kek,
iv_for_file_key_encryption_v};

// identify the size of the file key encryption tag from the encryption
Expand Down Expand Up @@ -970,7 +980,7 @@ void storage::write_data_to_stream(

// creating a context for the file key decryption
opensslpp::cipher_context file_key_decryption_context{
opensslpp::cipher_context_mode_type::decryption, kek_cipher, kek,
opensslpp::cipher_context_operation_type::decryption, kek_cipher, kek,
iv_for_file_key_encryption_v, tag_of_file_key_encryption_v};
util::hex_value_storage file_key_decrypted{
std::size(encryption_record->file_key_encrypted_with_kek)};
Expand All @@ -982,7 +992,7 @@ void storage::write_data_to_stream(
// key (decrypted previously), and the IV for data encryption

auto data_encryption_context{opensslpp::cipher_context::create_with_offset(
offset, opensslpp::cipher_context_mode_type::encryption,
offset, opensslpp::cipher_context_operation_type::encryption,
encryption_record->data_cipher, file_key_decrypted,
encryption_record->iv_for_data_encryption)};

Expand Down
Loading
Loading