Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0512399
adding tmp file encryption
Jun 16, 2025
496dff7
Adding full encryption pragma
Jun 16, 2025
1b4e4cf
merge with main
Jun 18, 2025
09ab5e2
refactor temp file encryption
Jun 18, 2025
35fff4e
remove redundant pragmas
Jun 18, 2025
97eb2b6
wip
Jun 18, 2025
8d97b47
wip
Jun 18, 2025
3ad11ed
Merge branch 'wal_final' into encryption/temp_files
Jun 23, 2025
54920d9
adding config
Jun 23, 2025
558a031
small fixes and changing tests
Jun 23, 2025
f5228e9
delete user key test
Jun 23, 2025
e28694d
Fixing last test
Jun 23, 2025
7452ffc
remove redundant test
Jun 23, 2025
c78c89d
merge with wal
Jun 24, 2025
b0f75ef
Merge branch 'wal_final' into encryption/temp_files
Jun 24, 2025
9232eeb
remove redundant functions
Jun 24, 2025
b3465af
Refactoring w/ feedback
Jun 24, 2025
ed5dc38
Merge branch 'wal_final' into encryption/temp_files
Jun 25, 2025
046563b
Changing Filebuffer, removing restructure method
Jun 25, 2025
3ee2bd3
Add EncryptionNonce and EncryptionTag for WAL encryption
Jun 25, 2025
13cf483
Adding missing includes
Jun 25, 2025
f74c593
adding Headersize and reusable buffer check
Jun 25, 2025
e0fa033
change name of getblockheadersize for file buffers
Jun 26, 2025
c324b0f
Merge branch 'main' into encryption/temp_files
Jun 26, 2025
d287d45
Merge branch 'main' into tempfileencryption
Mytherin Jul 10, 2025
a93b8bc
Use correct nonce size
Mytherin Jul 10, 2025
7590746
Unify encryption code
Mytherin Jul 10, 2025
0166e25
Remove temporary block manager header size, and instead track header …
Mytherin Jul 10, 2025
3fcfd00
Use correct size for files allocated in temporary file manager
Mytherin Jul 10, 2025
2013b20
Remove unnecessary methods
Mytherin Jul 10, 2025
41cdbd9
Add TPC-H test
Mytherin Jul 10, 2025
d394dab
Disable setting temp_file_encryption automatically
Mytherin Jul 10, 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
123 changes: 94 additions & 29 deletions src/common/encryption_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,18 @@ void EncryptionEngine::AddTempKeyToCache(DatabaseInstance &db) {
void EncryptionEngine::EncryptBlock(DatabaseInstance &db, const string &key_id, FileBuffer &block,
FileBuffer &temp_buffer_manager, uint64_t delta) {
data_ptr_t block_offset_internal = temp_buffer_manager.InternalBuffer();
auto encrypt_key = GetKeyFromCache(db, key_id);
auto encryption_state =
db.GetEncryptionUtil()->CreateEncryptionState(encrypt_key, MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);

auto encryption_state = db.GetEncryptionUtil()->CreateEncryptionState(GetKeyFromCache(db, key_id),
MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);

uint8_t tag[MainHeader::AES_TAG_LEN];
memset(tag, 0, MainHeader::AES_TAG_LEN);

//! a nonce is randomly generated for every block
uint8_t nonce[MainHeader::AES_IV_LEN];
memset(nonce, 0, MainHeader::AES_IV_LEN);
encryption_state->GenerateRandomData(static_cast<data_ptr_t>(nonce), MainHeader::AES_NONCE_LEN);
EncryptionTag tag;
EncryptionNonce nonce;
encryption_state->GenerateRandomData(nonce.data(), nonce.size());

//! store the nonce at the start of the block
memcpy(block_offset_internal, nonce, MainHeader::AES_NONCE_LEN);
encryption_state->InitializeEncryption(static_cast<data_ptr_t>(nonce), MainHeader::AES_NONCE_LEN,
GetKeyFromCache(db, key_id), MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);
memcpy(block_offset_internal, nonce.data(), nonce.size());
encryption_state->InitializeEncryption(nonce.data(), nonce.size(), encrypt_key,
MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);

auto checksum_offset = block.InternalBuffer() + delta;
auto encryption_checksum_offset = block_offset_internal + delta;
Expand All @@ -87,30 +83,27 @@ void EncryptionEngine::EncryptBlock(DatabaseInstance &db, const string &key_id,
}

//! Finalize and extract the tag
aes_res = encryption_state->Finalize(block.InternalBuffer() + delta, 0, static_cast<data_ptr_t>(tag),
MainHeader::AES_TAG_LEN);
aes_res = encryption_state->Finalize(block.InternalBuffer() + delta, 0, tag.data(), tag.size());

//! store the generated tag after consequetively the nonce
memcpy(block_offset_internal + MainHeader::AES_NONCE_LEN, tag, MainHeader::AES_TAG_LEN);
memcpy(block_offset_internal + nonce.size(), tag.data(), tag.size());
}

void EncryptionEngine::DecryptBlock(DatabaseInstance &db, const string &key_id, data_ptr_t internal_buffer,
uint64_t block_size, uint64_t delta) {
//! initialize encryption state
auto encryption_state = db.GetEncryptionUtil()->CreateEncryptionState(GetKeyFromCache(db, key_id),
MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);
auto decrypt_key = GetKeyFromCache(db, key_id);
auto encryption_state =
db.GetEncryptionUtil()->CreateEncryptionState(decrypt_key, MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);

//! load the stored nonce
uint8_t nonce[MainHeader::AES_IV_LEN];
memset(nonce, 0, MainHeader::AES_IV_LEN);
memcpy(nonce, internal_buffer, MainHeader::AES_NONCE_LEN);

//! load the tag for verification
uint8_t tag[MainHeader::AES_TAG_LEN];
memcpy(tag, internal_buffer + MainHeader::AES_NONCE_LEN, MainHeader::AES_TAG_LEN);
//! load the stored nonce and tag
EncryptionTag tag;
EncryptionNonce nonce;
memcpy(nonce.data(), internal_buffer, nonce.size());
memcpy(tag.data(), internal_buffer + nonce.size(), tag.size());

//! Initialize the decryption
encryption_state->InitializeDecryption(nonce, MainHeader::AES_NONCE_LEN, GetKeyFromCache(db, key_id),
encryption_state->InitializeDecryption(nonce.data(), nonce.size(), decrypt_key,
MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);

auto checksum_offset = internal_buffer + delta;
Expand All @@ -124,8 +117,80 @@ void EncryptionEngine::DecryptBlock(DatabaseInstance &db, const string &key_id,
}

//! check the tag
aes_res =
encryption_state->Finalize(internal_buffer + delta, 0, static_cast<data_ptr_t>(tag), MainHeader::AES_TAG_LEN);
aes_res = encryption_state->Finalize(internal_buffer + delta, 0, tag.data(), tag.size());
}

void EncryptionEngine::EncryptTemporaryBuffer(DatabaseInstance &db, data_ptr_t buffer, idx_t buffer_size,
data_ptr_t metadata) {
if (!ContainsKey(db, "temp_key")) {
AddTempKeyToCache(db);
}

auto temp_key = GetKeyFromCache(db, "temp_key");

auto encryption_util = db.GetEncryptionUtil();
auto encryption_state = encryption_util->CreateEncryptionState(temp_key, MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);

// zero-out the metadata buffer
memset(metadata, 0, DEFAULT_ENCRYPTED_BUFFER_HEADER_SIZE);

EncryptionTag tag;
EncryptionNonce nonce;

encryption_state->GenerateRandomData(nonce.data(), nonce.size());

//! store the nonce at the start of metadata buffer
memcpy(metadata, nonce.data(), nonce.size());

encryption_state->InitializeEncryption(nonce.data(), nonce.size(), temp_key,
MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);

auto aes_res = encryption_state->Process(buffer, buffer_size, buffer, buffer_size);

if (aes_res != buffer_size) {
throw IOException("Encryption failure: in- and output size differ");
}

//! Finalize and extract the tag
encryption_state->Finalize(buffer, 0, tag.data(), tag.size());

//! store the generated tag after consequetively the nonce
memcpy(metadata + nonce.size(), tag.data(), tag.size());

// check if tag is correctly stored
D_ASSERT(memcmp(tag.data(), metadata + nonce.size(), tag.size()) == 0);
}

void EncryptionEngine::DecryptBuffer(EncryptionState &encryption_state, const_data_ptr_t temp_key, data_ptr_t buffer,
idx_t buffer_size, data_ptr_t metadata) {
//! load the stored nonce and tag
EncryptionTag tag;
EncryptionNonce nonce;
memcpy(nonce.data(), metadata, nonce.size());
memcpy(tag.data(), metadata + nonce.size(), tag.size());

//! Initialize the decryption
encryption_state.InitializeDecryption(nonce.data(), nonce.size(), temp_key,
MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);

auto aes_res = encryption_state.Process(buffer, buffer_size, buffer, buffer_size);

if (aes_res != buffer_size) {
throw IOException("Encryption failure: in- and output size differ");
}

//! check the tag
encryption_state.Finalize(buffer, 0, tag.data(), tag.size());
}

void EncryptionEngine::DecryptTemporaryBuffer(DatabaseInstance &db, data_ptr_t buffer, idx_t buffer_size,
data_ptr_t metadata) {
//! initialize encryption state
auto encryption_util = db.GetEncryptionUtil();
auto temp_key = GetKeyFromCache(db, "temp_key");
auto encryption_state = encryption_util->CreateEncryptionState(temp_key, MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);

DecryptBuffer(*encryption_state, temp_key, buffer, buffer_size, metadata);
}

} // namespace duckdb
8 changes: 5 additions & 3 deletions src/common/file_buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ void FileBuffer::Init() {
internal_size = 0;
}

FileBuffer::FileBuffer(FileBuffer &source, FileBufferType type_p) : allocator(source.allocator), type(type_p) {
FileBuffer::FileBuffer(FileBuffer &source, FileBufferType type_p, idx_t block_header_size)
: allocator(source.allocator), type(type_p) {
// take over the structures of the source buffer
buffer = source.buffer;
size = source.size;
buffer = source.internal_buffer + block_header_size;
size = source.internal_size - block_header_size;
internal_buffer = source.internal_buffer;
internal_size = source.internal_size;

Expand All @@ -62,6 +63,7 @@ void FileBuffer::ReallocBuffer(idx_t new_size) {
if (!new_buffer) {
throw std::bad_alloc();
}

internal_buffer = new_buffer;
internal_size = new_size;

Expand Down
7 changes: 7 additions & 0 deletions src/common/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,13 @@
"scope": "global",
"custom_implementation": true
},
{
"name": "temp_file_encryption",
"description": "Encrypt all temporary files if database is encrypted",
"type": "BOOLEAN",
"scope": "global",
"custom_implementation": true
},
{
"name": "threads",
"description": "The number of total threads used by the system.",
Expand Down
1 change: 1 addition & 0 deletions src/function/pragma/pragma_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "duckdb/planner/expression_binder.hpp"
#include "duckdb/storage/buffer_manager.hpp"
#include "duckdb/storage/storage_manager.hpp"
#include "duckdb/common/encryption_functions.hpp"

#include <cctype>

Expand Down
35 changes: 35 additions & 0 deletions src/include/duckdb/common/encryption_functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,36 @@

namespace duckdb {

struct EncryptionTag {
EncryptionTag() = default;

data_ptr_t data() {
return tag;
}

idx_t size() const {
return MainHeader::AES_TAG_LEN;
}

private:
data_t tag[MainHeader::AES_TAG_LEN];
};

struct EncryptionNonce {
EncryptionNonce() = default;

data_ptr_t data() {
return nonce;
}

idx_t size() const {
return MainHeader::AES_NONCE_LEN;
}

private:
data_t nonce[MainHeader::AES_NONCE_LEN];
};

class EncryptionEngine {

public:
Expand All @@ -30,6 +60,11 @@ class EncryptionEngine {
FileBuffer &temp_buffer_manager, uint64_t delta);
static void DecryptBlock(DatabaseInstance &db, const string &key_id, data_ptr_t internal_buffer,
uint64_t block_size, uint64_t delta);

static void EncryptTemporaryBuffer(DatabaseInstance &db, data_ptr_t buffer, idx_t buffer_size, data_ptr_t metadata);
static void DecryptBuffer(EncryptionState &encryption_state, const_data_ptr_t temp_key, data_ptr_t buffer,
idx_t buffer_size, data_ptr_t metadata);
static void DecryptTemporaryBuffer(DatabaseInstance &db, data_ptr_t buffer, idx_t buffer_size, data_ptr_t metadata);
};

class EncryptionTypes {
Expand Down
6 changes: 5 additions & 1 deletion src/include/duckdb/common/file_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class FileBuffer {
//! DIRECT_IO
FileBuffer(Allocator &allocator, FileBufferType type, uint64_t user_size, idx_t block_header_size);
FileBuffer(Allocator &allocator, FileBufferType type, BlockManager &block_manager);
FileBuffer(FileBuffer &source, FileBufferType type);
FileBuffer(FileBuffer &source, FileBufferType type, idx_t block_header_size);

virtual ~FileBuffer();

Expand Down Expand Up @@ -60,6 +60,10 @@ class FileBuffer {
void Resize(uint64_t user_size, BlockManager &block_manager);
void Resize(BlockManager &block_manager);

idx_t GetHeaderSize() const {
return internal_size - size;
}

uint64_t AllocSize() const {
return internal_size;
}
Expand Down
3 changes: 1 addition & 2 deletions src/include/duckdb/common/types/row/row_data_collection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,7 @@ class RowDataCollection {
#ifdef DEBUG
for (auto &block : blocks) {
D_ASSERT(block->block->GetMemoryUsage() ==
BufferManager::GetAllocSize(block->capacity * entry_size +
buffer_manager.GetTemporaryBlockHeaderSize()));
BufferManager::GetAllocSize(block->capacity * entry_size + Storage::DEFAULT_BLOCK_HEADER_SIZE));
}
#endif
}
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 @@ -272,6 +272,8 @@ struct DBConfigOptions {
bool old_implicit_casting = false;
//! By default, WAL is encrypted for encrypted databases
bool wal_encryption = true;
//! Encrypt the temp files
bool temp_file_encryption = false;
//! 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
10 changes: 10 additions & 0 deletions src/include/duckdb/main/settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,16 @@ struct TempDirectorySetting {
static Value GetSetting(const ClientContext &context);
};

struct TempFileEncryptionSetting {
using RETURN_TYPE = bool;
static constexpr const char *Name = "temp_file_encryption";
static constexpr const char *Description = "Encrypt all temporary files if 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 ThreadsSetting {
using RETURN_TYPE = int64_t;
static constexpr const char *Name = "threads";
Expand Down
2 changes: 1 addition & 1 deletion src/include/duckdb/storage/block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Block : public FileBuffer {
Block(Allocator &allocator, const block_id_t id, const idx_t block_size, const idx_t block_header_size);
Block(Allocator &allocator, block_id_t id, uint32_t internal_size, idx_t block_header_size);
Block(Allocator &allocator, const block_id_t id, BlockManager &block_manager);
Block(FileBuffer &source, block_id_t id);
Block(FileBuffer &source, block_id_t id, idx_t block_header_size);

block_id_t id;
};
Expand Down
4 changes: 2 additions & 2 deletions src/include/duckdb/storage/buffer_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ class BufferManager {
virtual idx_t GetBlockAllocSize() const = 0;
//! Returns the block size for buffer-managed blocks.
virtual idx_t GetBlockSize() const = 0;
//! Returns the block header size for buffer-managed blocks.
virtual idx_t GetTemporaryBlockHeaderSize() const = 0;
//! Returns the maximum available memory for a given query.
virtual idx_t GetQueryMaxMemory() const = 0;

Expand Down Expand Up @@ -110,6 +108,8 @@ class BufferManager {
virtual void SetTemporaryDirectory(const string &new_dir);
//! Returns true, if the path to the temporary file directory is not empty.
virtual bool HasTemporaryDirectory() const;
//! Returns true if there are files found in the temporary directory
virtual bool HasFilesInTemporaryDirectory() const;

//! Construct a managed buffer.
virtual unique_ptr<FileBuffer> ConstructManagedBuffer(idx_t size, idx_t block_header_size,
Expand Down
5 changes: 1 addition & 4 deletions src/include/duckdb/storage/standard_buffer_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ class StandardBufferManager : public BufferManager {

public:
static unique_ptr<StandardBufferManager> CreateBufferManager(DatabaseInstance &db, string temp_directory);
static unique_ptr<FileBuffer> ReadTemporaryBufferInternal(BufferManager &buffer_manager, FileHandle &handle,
idx_t position, idx_t size,
unique_ptr<FileBuffer> reusable_buffer);

//! Registers a transient memory buffer.
shared_ptr<BlockHandle> RegisterTransientMemory(const idx_t size, BlockManager &block_manager) final;
Expand All @@ -59,7 +56,6 @@ class StandardBufferManager : public BufferManager {
idx_t GetBlockAllocSize() const final;
//! Returns the block size for buffer-managed blocks.
idx_t GetBlockSize() const final;
idx_t GetTemporaryBlockHeaderSize() const final;
idx_t GetQueryMaxMemory() const final;

//! Allocate an in-memory buffer with a single pin.
Expand Down Expand Up @@ -108,6 +104,7 @@ class StandardBufferManager : public BufferManager {
DUCKDB_API void ReserveMemory(idx_t size) final;
DUCKDB_API void FreeReservedMemory(idx_t size) final;
bool HasTemporaryDirectory() const final;
bool HasFilesInTemporaryDirectory() const final;

protected:
//! Helper
Expand Down
3 changes: 2 additions & 1 deletion src/include/duckdb/storage/storage_info.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ struct FileHandle;
#define DEFAULT_ENCRYPTION_BLOCK_HEADER_SIZE 40ULL
//! The configurable block allocation size.
#ifndef DUCKDB_BLOCK_HEADER_STORAGE_SIZE
#define DUCKDB_BLOCK_HEADER_STORAGE_SIZE DEFAULT_BLOCK_HEADER_STORAGE_SIZE
#define DUCKDB_BLOCK_HEADER_STORAGE_SIZE DEFAULT_BLOCK_HEADER_STORAGE_SIZE
#define DEFAULT_ENCRYPTED_BUFFER_HEADER_SIZE 28ULL
#endif

using block_id_t = int64_t;
Expand Down
Loading
Loading