Coverage Report

Created: 2026-03-31 07:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/duckdb/src/common/encryption_functions.cpp
Line
Count
Source
1
#include "duckdb/common/exception/conversion_exception.hpp"
2
#include "duckdb/common/encryption_key_manager.hpp"
3
#include "duckdb/common/encryption_functions.hpp"
4
#include "duckdb/main/attached_database.hpp"
5
#include "mbedtls_wrapper.hpp"
6
#include "duckdb/storage/storage_manager.hpp"
7
#include "duckdb/storage/storage_info.hpp"
8
#include "duckdb/common/typedefs.hpp"
9
10
namespace duckdb {
11
12
constexpr uint32_t AdditionalAuthenticatedData::INITIAL_AAD_CAPACITY;
13
14
0
AdditionalAuthenticatedData::~AdditionalAuthenticatedData() = default;
15
16
0
data_ptr_t AdditionalAuthenticatedData::data() const {
17
0
  return additional_authenticated_data->GetData();
18
0
}
19
20
0
idx_t AdditionalAuthenticatedData::size() const {
21
0
  return additional_authenticated_data->GetPosition();
22
0
}
23
24
0
void AdditionalAuthenticatedData::WriteStringData(const std::string &val) const {
25
0
  additional_authenticated_data->WriteData(reinterpret_cast<const_data_ptr_t>(val.data()), val.size());
26
0
}
27
28
0
EncryptionEngine::EncryptionEngine() {
29
0
}
30
31
0
EncryptionEngine::~EncryptionEngine() {
32
0
}
33
34
0
const_data_ptr_t EncryptionEngine::GetKeyFromCache(DatabaseInstance &db, const string &key_name) {
35
0
  auto &keys = EncryptionKeyManager::Get(db);
36
0
  return keys.GetKey(key_name);
37
0
}
38
39
0
bool EncryptionEngine::ContainsKey(DatabaseInstance &db, const string &key_name) {
40
0
  auto &keys = EncryptionKeyManager::Get(db);
41
0
  return keys.HasKey(key_name);
42
0
}
43
44
0
void EncryptionEngine::AddKeyToCache(DatabaseInstance &db, data_ptr_t key, const string &key_name, bool wipe) {
45
0
  auto &keys = EncryptionKeyManager::Get(db);
46
0
  if (!keys.HasKey(key_name)) {
47
0
    keys.AddKey(key_name, key);
48
0
  } else {
49
0
    duckdb_mbedtls::MbedTlsWrapper::AESStateMBEDTLS::SecureClearData(key,
50
0
                                                                     MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);
51
0
  }
52
0
}
53
54
0
string EncryptionEngine::AddKeyToCache(DatabaseInstance &db, data_ptr_t key) {
55
0
  auto &keys = EncryptionKeyManager::Get(db);
56
0
  const auto key_id = keys.GenerateRandomKeyID();
57
58
0
  if (!keys.HasKey(key_id)) {
59
0
    keys.AddKey(key_id, key);
60
0
  } else {
61
0
    duckdb_mbedtls::MbedTlsWrapper::AESStateMBEDTLS::SecureClearData(key,
62
0
                                                                     MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH);
63
0
  }
64
65
0
  return key_id;
66
0
}
67
68
0
void EncryptionEngine::AddTempKeyToCache(DatabaseInstance &db) {
69
  //! Add a temporary key to the cache
70
0
  const auto length = MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH;
71
0
  data_t temp_key[length];
72
73
  // we cannot generate temporary keys with read-only enabled
74
0
  auto metadata = make_uniq<EncryptionStateMetadata>(EncryptionTypes::GCM, length, EncryptionTypes::V0_1);
75
0
  auto encryption_state = db.GetEncryptionUtil(false)->CreateEncryptionState(std::move(metadata));
76
0
  encryption_state->GenerateRandomData(temp_key, length);
77
78
0
  string key_id = "temp_key";
79
0
  AddKeyToCache(db, temp_key, key_id);
80
0
}
81
82
void EncryptionEngine::EncryptBlock(AttachedDatabase &attached_db, const string &key_id, FileBuffer &block,
83
0
                                    FileBuffer &temp_buffer_manager, uint64_t delta) {
84
0
  auto &db = attached_db.GetDatabase();
85
0
  data_ptr_t block_offset_internal = temp_buffer_manager.InternalBuffer();
86
0
  auto encrypt_key = GetKeyFromCache(db, key_id);
87
0
  auto version = attached_db.GetStorageManager().GetEncryptionVersion();
88
0
  auto cipher = attached_db.GetStorageManager().GetCipher();
89
0
  auto metadata = make_uniq<EncryptionStateMetadata>(cipher, MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH, version);
90
0
  auto encryption_state = db.GetEncryptionUtil(attached_db.IsReadOnly())->CreateEncryptionState(std::move(metadata));
91
92
0
  EncryptionTag tag;
93
0
  EncryptionNonce nonce(cipher, version);
94
0
  encryption_state->GenerateRandomData(nonce.data(), nonce.size());
95
96
  //! store the nonce at the start of the block
97
0
  memcpy(block_offset_internal, nonce.data(), nonce.size());
98
0
  encryption_state->InitializeEncryption(nonce, encrypt_key);
99
100
0
  auto checksum_offset = block.InternalBuffer() + delta;
101
0
  auto encryption_checksum_offset = block_offset_internal + delta;
102
0
  auto size = block.size + Storage::DEFAULT_BLOCK_HEADER_SIZE;
103
104
  //! encrypt the data including the checksum
105
0
  auto aes_res = encryption_state->Process(checksum_offset, size, encryption_checksum_offset, size);
106
107
0
  if (aes_res != size) {
108
0
    throw IOException("Block encryption failure: in- and output size differ (%llu/%llu)", size, aes_res);
109
0
  }
110
111
  //! Finalize and extract the tag
112
0
  encryption_state->Finalize(block.InternalBuffer() + delta, 0, tag.data(), tag.size());
113
114
  //! store the generated tag *behind* the nonce (but still at the beginning of the block)
115
0
  memcpy(block_offset_internal + nonce.size(), tag.data(), tag.size());
116
0
}
117
118
void EncryptionEngine::DecryptBlock(AttachedDatabase &attached_db, const string &key_id, data_ptr_t internal_buffer,
119
0
                                    uint64_t block_size, uint64_t delta) {
120
  //! initialize encryption state
121
0
  auto &db = attached_db.GetDatabase();
122
0
  auto version = attached_db.GetStorageManager().GetEncryptionVersion();
123
0
  auto cipher = attached_db.GetStorageManager().GetCipher();
124
0
  auto metadata = make_uniq<EncryptionStateMetadata>(cipher, MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH, version);
125
0
  auto decrypt_key = GetKeyFromCache(db, key_id);
126
0
  auto encryption_state = db.GetEncryptionUtil(attached_db.IsReadOnly())->CreateEncryptionState(std::move(metadata));
127
128
  //! load the stored nonce and tag
129
0
  EncryptionTag tag;
130
0
  EncryptionNonce nonce(cipher, version);
131
0
  memcpy(nonce.data(), internal_buffer, nonce.size());
132
0
  memcpy(tag.data(), internal_buffer + nonce.size(), tag.size());
133
134
  //! Initialize the decryption
135
0
  encryption_state->InitializeDecryption(nonce, decrypt_key);
136
137
0
  auto checksum_offset = internal_buffer + delta;
138
0
  auto size = block_size + Storage::DEFAULT_BLOCK_HEADER_SIZE;
139
140
  //! decrypt the block including the checksum
141
0
  auto aes_res = encryption_state->Process(checksum_offset, size, checksum_offset, size);
142
143
0
  if (aes_res != block_size + Storage::DEFAULT_BLOCK_HEADER_SIZE) {
144
0
    throw IOException("Block decryption failure: in- and output size differ (%llu/%llu)", size, aes_res);
145
0
  }
146
147
  //! check the tag
148
0
  encryption_state->Finalize(internal_buffer + delta, 0, tag.data(), tag.size());
149
0
}
150
151
void EncryptionEngine::EncryptTemporaryBuffer(DatabaseInstance &db, data_ptr_t buffer, idx_t buffer_size,
152
0
                                              data_ptr_t metadata) {
153
0
  if (!ContainsKey(db, "temp_key")) {
154
0
    AddTempKeyToCache(db);
155
0
  }
156
157
0
  auto temp_key = GetKeyFromCache(db, "temp_key");
158
  // we cannot encrypt temp buffers in read-only mode
159
0
  auto encryption_util = db.GetEncryptionUtil(false);
160
  // we hard-code GCM here for now, it's the safest and we don't know what is configured here
161
0
  auto state_metadata = make_uniq<EncryptionStateMetadata>(
162
0
      EncryptionTypes::GCM, MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH, EncryptionTypes::V0_1);
163
0
  auto encryption_state = encryption_util->CreateEncryptionState(std::move(state_metadata));
164
165
  // zero-out the metadata buffer
166
0
  memset(metadata, 0, DEFAULT_ENCRYPTED_BUFFER_HEADER_SIZE);
167
168
0
  EncryptionTag tag;
169
0
  EncryptionNonce nonce(EncryptionTypes::CipherType::GCM, EncryptionTypes::V0_1);
170
171
0
  encryption_state->GenerateRandomData(nonce.data(), nonce.size());
172
173
  //! store the nonce at the start of metadata buffer
174
0
  memcpy(metadata, nonce.data(), nonce.size());
175
0
  encryption_state->InitializeEncryption(nonce, temp_key);
176
177
0
  auto aes_res = encryption_state->Process(buffer, buffer_size, buffer, buffer_size);
178
179
0
  if (aes_res != buffer_size) {
180
0
    throw IOException("Temporary buffer encryption failure: in- and output size differ (%llu/%llu)", buffer_size,
181
0
                      aes_res);
182
0
  }
183
184
  //! Finalize and extract the tag
185
0
  encryption_state->Finalize(buffer, 0, tag.data(), tag.size());
186
187
  //! store the generated tag after consequetively the nonce
188
0
  memcpy(metadata + nonce.size(), tag.data(), tag.size());
189
190
  // check if tag is correctly stored
191
0
  D_ASSERT(memcmp(tag.data(), metadata + nonce.size(), tag.size()) == 0);
192
0
}
193
194
static void DecryptBuffer(EncryptionState &encryption_state, const_data_ptr_t temp_key, data_ptr_t buffer,
195
0
                          idx_t buffer_size, data_ptr_t metadata) {
196
  //! load the stored nonce and tag
197
0
  EncryptionTag tag;
198
0
  EncryptionNonce nonce(encryption_state.metadata->GetCipher(), encryption_state.metadata->GetVersion());
199
0
  memcpy(nonce.data(), metadata, nonce.size());
200
0
  memcpy(tag.data(), metadata + nonce.size(), tag.size());
201
202
  //! Initialize the decryption
203
0
  encryption_state.InitializeDecryption(nonce, temp_key);
204
205
0
  auto aes_res = encryption_state.Process(buffer, buffer_size, buffer, buffer_size);
206
207
0
  if (aes_res != buffer_size) {
208
0
    throw IOException("Buffer decryption failure: in- and output size differ (%llu/%llu)", buffer_size, aes_res);
209
0
  }
210
211
  //! check the tag
212
0
  encryption_state.Finalize(buffer, 0, tag.data(), tag.size());
213
0
}
214
215
void EncryptionEngine::DecryptTemporaryBuffer(DatabaseInstance &db, data_ptr_t buffer, idx_t buffer_size,
216
0
                                              data_ptr_t metadata) {
217
  //! initialize encryption state
218
0
  auto encryption_util = db.GetEncryptionUtil(false);
219
0
  auto temp_key = GetKeyFromCache(db, "temp_key");
220
0
  auto state_metadata = make_uniq<EncryptionStateMetadata>(
221
0
      EncryptionTypes::GCM, MainHeader::DEFAULT_ENCRYPTION_KEY_LENGTH, EncryptionTypes::EncryptionVersion::V0_1);
222
0
  auto encryption_state = encryption_util->CreateEncryptionState(std::move(state_metadata));
223
224
0
  DecryptBuffer(*encryption_state, temp_key, buffer, buffer_size, metadata);
225
0
}
226
227
} // namespace duckdb