Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
cb5a2cd
Add encryption keys in the cl
Jun 13, 2025
e5e4ded
Merge branch 'main' into cl_bbe
Jun 13, 2025
c90092b
skipping test for windows
Jun 13, 2025
733ae25
skip test for windows
Jun 13, 2025
0b93c5c
ff
Jun 13, 2025
9fd9317
adding WAL encryption
Jun 13, 2025
6d4136c
fix windows error
Jun 13, 2025
5b82cc7
remove comments
Jun 14, 2025
d82e293
refactoring cl keys usage
Jun 15, 2025
159c045
add force plaintext
Jun 16, 2025
8beb541
adding tests
Jun 16, 2025
afa953c
adding encryption test infra
Jun 16, 2025
d531067
WAL encryption tests
Jun 17, 2025
641fca9
test/
Jun 17, 2025
7e95b78
remove comments
Jun 17, 2025
86f342e
merge with main
Jun 17, 2025
863ea7f
Remove master key
Jun 17, 2025
197ae2b
Remove unused masterkey functions
Jun 17, 2025
2ccf9ce
remove unused option for attach
Jun 17, 2025
db1ee6c
Adding SET statement for key (python api)
Jun 17, 2025
b8594f4
fixes
Jun 17, 2025
b2f9820
remove master key
Jun 17, 2025
45899fc
Adding test for WAL encryption
Jun 17, 2025
8ffbec6
fix in test
Jun 17, 2025
7d388e5
close file handle
Jun 17, 2025
87b3b9c
remove handle from test case
Jun 18, 2025
57cbcba
remove temp file test files
Jun 18, 2025
105740c
remove WAL version test
Jun 18, 2025
617d24d
Remove CLI key code
Jun 23, 2025
9fccf46
remove -key explanation
Jun 23, 2025
01ccda5
remove unused config
Jun 23, 2025
2cf2f8c
delete test for user key
Jun 23, 2025
534a841
Refactor w/ feedback
Jun 24, 2025
963f21c
removing semicolon + unused variables
Jun 24, 2025
30fed6e
change reset value
Jun 24, 2025
b6919d8
removing virtual catalog functions
Jun 25, 2025
4954ec2
Cast DuckCatalog differently
Jun 25, 2025
4b07c9d
add missing includes
Jun 25, 2025
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
19 changes: 19 additions & 0 deletions src/catalog/duck_catalog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,23 @@ optional_idx DuckCatalog::GetCatalogVersion(ClientContext &context) {
return transaction_manager.GetCatalogVersion(*transaction.transaction);
}

//===--------------------------------------------------------------------===//
// Encryption
//===--------------------------------------------------------------------===//
void DuckCatalog::SetEncryptionKeyId(const string &key_id) {
encryption_key_id = key_id;
}

string &DuckCatalog::GetEncryptionKeyId() {
return encryption_key_id;
}

void DuckCatalog::SetIsEncrypted() {
is_encrypted = true;
}

bool DuckCatalog::GetIsEncrypted() {
return is_encrypted;
}

} // namespace duckdb
3 changes: 2 additions & 1 deletion src/common/encryption_functions.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "duckdb/common/exception/conversion_exception.hpp"
#include "duckdb/common/encryption_key_manager.hpp"
#include "duckdb/common/encryption_functions.hpp"
#include "duckdb/main/attached_database.hpp"
Expand All @@ -13,7 +14,7 @@ const_data_ptr_t EncryptionEngine::GetKeyFromCache(DatabaseInstance &db, const s
return keys.GetKey(key_name);
}

bool EncryptionEngine::ContainsKey(DatabaseInstance &db, const string &key_name) const {
bool EncryptionEngine::ContainsKey(DatabaseInstance &db, const string &key_name) {
auto &keys = EncryptionKeyManager::Get(db);
return keys.HasKey(key_name);
}
Expand Down
16 changes: 11 additions & 5 deletions src/common/encryption_key_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ void EncryptionKey::LockEncryptionKey(data_ptr_t key, idx_t key_len) {
void EncryptionKey::UnlockEncryptionKey(data_ptr_t key, idx_t key_len) {
memset(key, 0, key_len);
#if defined(_WIN32)
VirtualUnlock(static_cast<void *>(&key[0]), key_len);
VirtualUnlock(key, key_len);
#else
munlock(static_cast<void *>(&key[0]), key_len);
munlock(key, key_len);
#endif
}

Expand Down Expand Up @@ -88,15 +88,20 @@ void EncryptionKeyManager::DeleteKey(const string &key_name) {
derived_keys.erase(key_name);
}

void EncryptionKeyManager::KeyDerivationFunctionSHA256(const string &user_key, data_ptr_t salt,
void EncryptionKeyManager::KeyDerivationFunctionSHA256(const_data_ptr_t key, idx_t key_size, data_ptr_t salt,
data_ptr_t derived_key) {
//! For now, we are only using SHA256 for key derivation
duckdb_mbedtls::MbedTlsWrapper::SHA256State state;
state.AddSalt(salt, MainHeader::SALT_LEN);
state.AddBytes(duckdb::data_ptr_t(reinterpret_cast<const uint8_t *>(user_key.data())), user_key.size());
state.AddBytes(key, key_size);
state.FinalizeDerivedKey(derived_key);
}

void EncryptionKeyManager::KeyDerivationFunctionSHA256(data_ptr_t user_key, idx_t user_key_size, data_ptr_t salt,
data_ptr_t derived_key) {
KeyDerivationFunctionSHA256(reinterpret_cast<const_data_ptr_t>(user_key), user_key_size, salt, derived_key);
}

string EncryptionKeyManager::Base64Decode(const string &key) {
auto result_size = Blob::FromBase64Size(key);
auto output = duckdb::unique_ptr<unsigned char[]>(new unsigned char[result_size]);
Expand All @@ -117,7 +122,8 @@ void EncryptionKeyManager::DeriveKey(string &user_key, data_ptr_t salt, data_ptr
decoded_key = user_key;
}

KeyDerivationFunctionSHA256(decoded_key, salt, derived_key);
KeyDerivationFunctionSHA256(reinterpret_cast<const_data_ptr_t>(decoded_key.data()), decoded_key.size(), salt,
derived_key);

// wipe the original and decoded key
std::fill(user_key.begin(), user_key.end(), 0);
Expand Down
6 changes: 6 additions & 0 deletions src/common/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,12 @@
"user"
]
},
{
"name": "wal_encryption",
"description": "Encrypt the WAL if the database is encrypted",
"type": "BOOLEAN",
"scope": "global"
},
{
"name": "zstd_min_string_length",
"description": "The (average) length at which to enable ZSTD compression, defaults to 4096",
Expand Down
1 change: 1 addition & 0 deletions src/include/duckdb/catalog/catalog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class Catalog {
virtual bool IsDuckCatalog() {
return false;
}

virtual void Initialize(bool load_builtin) = 0;
virtual void Initialize(optional_ptr<ClientContext> context, bool load_builtin);
virtual void FinalizeLoad(optional_ptr<ClientContext> context);
Expand Down
12 changes: 12 additions & 0 deletions src/include/duckdb/catalog/duck_catalog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class DuckCatalog : public Catalog {
public:
bool IsDuckCatalog() override;
void Initialize(bool load_builtin) override;

string GetCatalogType() override {
return "duckdb";
}
Expand All @@ -29,6 +30,12 @@ class DuckCatalog : public Catalog {
return write_lock;
}

// Encryption Functions
void SetEncryptionKeyId(const string &key_id);
string &GetEncryptionKeyId();
void SetIsEncrypted();
bool GetIsEncrypted();

public:
DUCKDB_API optional_ptr<CatalogEntry> CreateSchema(CatalogTransaction transaction, CreateSchemaInfo &info) override;
DUCKDB_API void ScanSchemas(ClientContext &context, std::function<void(SchemaCatalogEntry &)> callback) override;
Expand Down Expand Up @@ -79,6 +86,11 @@ class DuckCatalog : public Catalog {
mutex write_lock;
//! The catalog set holding the schemas
unique_ptr<CatalogSet> schemas;

//! Identifies whether the db is encrypted
bool is_encrypted = false;
//! If is encrypted, store the encryption key_id
string encryption_key_id;
};

} // namespace duckdb
4 changes: 3 additions & 1 deletion src/include/duckdb/common/encryption_functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ class EncryptionEngine {
~EncryptionEngine();

public:
//! General key management wrapper functions
static const_data_ptr_t GetKeyFromCache(DatabaseInstance &db, const string &key_name);
bool ContainsKey(DatabaseInstance &db, const string &key_name) const;
static bool ContainsKey(DatabaseInstance &db, const string &key_name);
static void AddKeyToCache(DatabaseInstance &db, data_ptr_t key, const string &key_name, bool wipe = true);
static string AddKeyToCache(DatabaseInstance &db, data_ptr_t key);
static void AddTempKeyToCache(DatabaseInstance &db);

//! Encryption Functions
static void EncryptBlock(DatabaseInstance &db, const string &key_id, FileBuffer &block,
FileBuffer &temp_buffer_manager, uint64_t delta);
static void DecryptBlock(DatabaseInstance &db, const string &key_id, data_ptr_t internal_buffer,
Expand Down
16 changes: 10 additions & 6 deletions src/include/duckdb/common/encryption_key_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ class EncryptionKey {
return key;
}

private:
data_t key[MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH];

private:
public:
static void LockEncryptionKey(data_ptr_t key, idx_t key_len = MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);
static void UnlockEncryptionKey(data_ptr_t key, idx_t key_len = MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);

private:
data_t key[MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH];
};

class EncryptionKeyManager : public ObjectCacheEntry {
Expand All @@ -62,9 +62,13 @@ class EncryptionKeyManager : public ObjectCacheEntry {
string GetObjectType() override;

public:
static string Base64Decode(const string &key);
public:
static void DeriveKey(string &user_key, data_ptr_t salt, data_ptr_t derived_key);
static void KeyDerivationFunctionSHA256(const string &user_key, data_ptr_t salt, data_ptr_t derived_key);
static void KeyDerivationFunctionSHA256(const_data_ptr_t user_key, idx_t user_key_size, data_ptr_t salt,
data_ptr_t derived_key);
static void KeyDerivationFunctionSHA256(data_ptr_t user_key, idx_t user_key_size, data_ptr_t salt,
data_ptr_t derived_key);
static string Base64Decode(const string &key);
static string GenerateRandomKeyID();

public:
Expand Down
2 changes: 2 additions & 0 deletions src/include/duckdb/main/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ struct DBConfigOptions {
string custom_user_agent;
//! Use old implicit casting style (i.e. allow everything to be implicitly casted to VARCHAR)
bool old_implicit_casting = false;
//! By default, WAL is encrypted for encrypted databases
bool wal_encryption = true;
//! The default block allocation size for new duckdb database files (new as-in, they do not yet exist).
idx_t default_block_alloc_size = DUCKDB_BLOCK_ALLOC_SIZE;
//! The default block header size for new duckdb database files.
Expand Down
12 changes: 11 additions & 1 deletion src/include/duckdb/main/settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ enum class SettingScope : uint8_t {
GLOBAL,
//! Setting is from the local Setting scope
LOCAL,
//! Setting was not feteched from settings, but it was fetched from a secret instead
//! Setting was not fetched from settings, but it was fetched from a secret instead
SECRET,
//! The setting was not found or invalid in some other way
INVALID
Expand Down Expand Up @@ -1282,6 +1282,16 @@ struct UsernameSetting {
static Value GetSetting(const ClientContext &context);
};

struct WalEncryptionSetting {
using RETURN_TYPE = bool;
static constexpr const char *Name = "wal_encryption";
static constexpr const char *Description = "Encrypt the WAL if the database is encrypted";
static constexpr const char *InputType = "BOOLEAN";
static void SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &parameter);
static void ResetGlobal(DatabaseInstance *db, DBConfig &config);
static Value GetSetting(const ClientContext &context);
};

struct ZstdMinStringLengthSetting {
using RETURN_TYPE = idx_t;
static constexpr const char *Name = "zstd_min_string_length";
Expand Down
5 changes: 5 additions & 0 deletions src/include/duckdb/storage/single_file_block_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ class SingleFileBlockManager : public BlockManager {
static void StoreSalt(MainHeader &main_header, data_ptr_t salt);
void StoreEncryptionMetadata(MainHeader &main_header) const;

//! Check and adding Encryption Keys
void CheckAndAddEncryptionKey(MainHeader &main_header, string &user_key);
void CheckAndAddEncryptionKey(MainHeader &main_header);
void CheckAndAddEncryptionKey(MainHeader &main_header, DBConfigOptions &config_options);

//! Return the blocks to which we will write the free list and modified blocks
vector<MetadataHandle> GetFreeListBlocks();
void TrimFreeBlocks();
Expand Down
3 changes: 3 additions & 0 deletions src/include/duckdb/storage/write_ahead_log.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ class WriteAheadLog {

void WriteVersion();

//! Determines if WAL should be encrypted
bool IsEncrypted() const;

virtual void WriteCreateTable(const TableCatalogEntry &entry);
void WriteDropTable(const TableCatalogEntry &entry);

Expand Down
1 change: 1 addition & 0 deletions src/main/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ static const ConfigurationOption internal_options[] = {
DUCKDB_GLOBAL_ALIAS("worker_threads", ThreadsSetting),
DUCKDB_GLOBAL(UsernameSetting),
DUCKDB_GLOBAL_ALIAS("user", UsernameSetting),
DUCKDB_GLOBAL(WalEncryptionSetting),
DUCKDB_GLOBAL(ZstdMinStringLengthSetting),
FINAL_SETTING};

Expand Down
16 changes: 16 additions & 0 deletions src/main/settings/autogenerated_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,22 @@ Value SchedulerProcessPartialSetting::GetSetting(const ClientContext &context) {
return Value::BOOLEAN(config.options.scheduler_process_partial);
}

//===----------------------------------------------------------------------===//
// Wal Encryption
//===----------------------------------------------------------------------===//
void WalEncryptionSetting::SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &input) {
config.options.wal_encryption = input.GetValue<bool>();
}

void WalEncryptionSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config) {
config.options.wal_encryption = DBConfig().options.wal_encryption;
}

Value WalEncryptionSetting::GetSetting(const ClientContext &context) {
auto &config = DBConfig::GetConfig(context);
return Value::BOOLEAN(config.options.wal_encryption);
}

//===----------------------------------------------------------------------===//
// Zstd Min String Length
//===----------------------------------------------------------------------===//
Expand Down
Loading
Loading