Skip to content

Commit caef2fa

Browse files
avalerio-tkdargmarco-tkd
authored andcommitted
- Added datatype parameter to DBPSEncryptor constructor.
first pass at implementing default encryptor logic
1 parent 31f3492 commit caef2fa

9 files changed

Lines changed: 702 additions & 32 deletions

CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ include(GoogleTest)
125125
add_library(dbps_common_lib STATIC
126126
src/common/json_request.cpp
127127
src/common/enum_utils.cpp
128+
src/common/value_encryption_utils.cpp
128129
)
129130
target_include_directories(dbps_common_lib PUBLIC
130131
src/common
@@ -253,6 +254,13 @@ if(BUILD_TESTS)
253254
gtest_main
254255
)
255256

257+
# Value encryption utils tests
258+
add_executable(value_encryption_utils_test src/common/value_encryption_utils_test.cpp)
259+
target_link_libraries(value_encryption_utils_test
260+
dbps_common_lib
261+
gtest_main
262+
)
263+
256264
# Encryption sequencer tests
257265
add_executable(encryption_sequencer_test src/server/encryption_sequencer_test.cpp)
258266
target_link_libraries(encryption_sequencer_test
@@ -464,6 +472,7 @@ if(BUILD_TESTS)
464472
DEPENDS
465473
json_request_test
466474
enum_utils_test
475+
value_encryption_utils_test
467476
encryption_sequencer_test
468477
decoding_utils_test
469478
bytes_utils_test
@@ -483,6 +492,7 @@ if(BUILD_TESTS)
483492
# Register tests with CTest via GoogleTest discovery
484493
gtest_discover_tests(json_request_test)
485494
gtest_discover_tests(enum_utils_test)
495+
gtest_discover_tests(value_encryption_utils_test)
486496
gtest_discover_tests(encryption_sequencer_test)
487497
gtest_discover_tests(decoding_utils_test)
488498
gtest_discover_tests(bytes_utils_test)
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
#include "value_encryption_utils.h"
19+
20+
#include <stdexcept>
21+
#include <limits>
22+
#include <cstring>
23+
#include <string>
24+
#include <array>
25+
#include <variant>
26+
#include <type_traits>
27+
28+
namespace { //file-local helper functions
29+
inline void append_u32_le(std::vector<uint8_t>& out, uint32_t v) {
30+
out.push_back(static_cast<uint8_t>(v & 0xFF));
31+
out.push_back(static_cast<uint8_t>((v >> 8) & 0xFF));
32+
out.push_back(static_cast<uint8_t>((v >> 16) & 0xFF));
33+
out.push_back(static_cast<uint8_t>((v >> 24) & 0xFF));
34+
}
35+
36+
inline uint32_t read_u32_le(const std::vector<uint8_t>& in, size_t offset) {
37+
return static_cast<uint32_t>(in[offset]) |
38+
(static_cast<uint32_t>(in[offset + 1]) << 8) |
39+
(static_cast<uint32_t>(in[offset + 2]) << 16) |
40+
(static_cast<uint32_t>(in[offset + 3]) << 24);
41+
}
42+
43+
inline void append_i32_le(std::vector<uint8_t>& out, int32_t v) {
44+
append_u32_le(out, static_cast<uint32_t>(v));
45+
}
46+
47+
inline void append_u64_le(std::vector<uint8_t>& out, uint64_t v) {
48+
out.push_back(static_cast<uint8_t>(v & 0xFF));
49+
out.push_back(static_cast<uint8_t>((v >> 8) & 0xFF));
50+
out.push_back(static_cast<uint8_t>((v >> 16) & 0xFF));
51+
out.push_back(static_cast<uint8_t>((v >> 24) & 0xFF));
52+
out.push_back(static_cast<uint8_t>((v >> 32) & 0xFF));
53+
out.push_back(static_cast<uint8_t>((v >> 40) & 0xFF));
54+
out.push_back(static_cast<uint8_t>((v >> 48) & 0xFF));
55+
out.push_back(static_cast<uint8_t>((v >> 56) & 0xFF));
56+
}
57+
58+
inline void append_i64_le(std::vector<uint8_t>& out, int64_t v) {
59+
append_u64_le(out, static_cast<uint64_t>(v));
60+
}
61+
62+
inline void append_f32_le(std::vector<uint8_t>& out, float v) {
63+
uint32_t bits = 0;
64+
std::memcpy(&bits, &v, sizeof(bits));
65+
append_u32_le(out, bits);
66+
}
67+
68+
inline void append_f64_le(std::vector<uint8_t>& out, double v) {
69+
uint64_t bits = 0;
70+
std::memcpy(&bits, &v, sizeof(bits));
71+
append_u64_le(out, bits);
72+
}
73+
74+
// Helper: Convert decrypted byte-vectors into a TypedListValues according to datatype
75+
TypedListValues BuildTypedListFromDecryptedBytes(
76+
Type::type datatype,
77+
const std::vector<std::vector<uint8_t> >& elements_bytes) {
78+
switch (datatype) {
79+
case Type::INT32: {
80+
std::vector<int32_t> out;
81+
for (size_t i = 0; i < elements_bytes.size(); ++i) {
82+
const std::vector<uint8_t>& bytes = elements_bytes[i];
83+
if (bytes.size() != 4) {
84+
throw std::runtime_error("DecryptTypedListValues: invalid INT32 element size");
85+
}
86+
int32_t v = static_cast<int32_t>(bytes[0]) |
87+
(static_cast<int32_t>(bytes[1]) << 8) |
88+
(static_cast<int32_t>(bytes[2]) << 16) |
89+
(static_cast<int32_t>(bytes[3]) << 24);
90+
out.push_back(v);
91+
}
92+
return out;
93+
}
94+
case Type::INT64: {
95+
std::vector<int64_t> out;
96+
for (size_t i = 0; i < elements_bytes.size(); ++i) {
97+
const std::vector<uint8_t>& bytes = elements_bytes[i];
98+
if (bytes.size() != 8) {
99+
throw std::runtime_error("DecryptTypedListValues: invalid INT64 element size");
100+
}
101+
uint64_t u = static_cast<uint64_t>(bytes[0]) |
102+
(static_cast<uint64_t>(bytes[1]) << 8) |
103+
(static_cast<uint64_t>(bytes[2]) << 16) |
104+
(static_cast<uint64_t>(bytes[3]) << 24) |
105+
(static_cast<uint64_t>(bytes[4]) << 32) |
106+
(static_cast<uint64_t>(bytes[5]) << 40) |
107+
(static_cast<uint64_t>(bytes[6]) << 48) |
108+
(static_cast<uint64_t>(bytes[7]) << 56);
109+
out.push_back(static_cast<int64_t>(u));
110+
}
111+
return out;
112+
}
113+
case Type::FLOAT: {
114+
std::vector<float> out;
115+
for (size_t i = 0; i < elements_bytes.size(); ++i) {
116+
const std::vector<uint8_t>& bytes = elements_bytes[i];
117+
if (bytes.size() != 4) {
118+
throw std::runtime_error("DecryptTypedListValues: invalid FLOAT element size");
119+
}
120+
uint32_t bits = static_cast<uint32_t>(bytes[0]) |
121+
(static_cast<uint32_t>(bytes[1]) << 8) |
122+
(static_cast<uint32_t>(bytes[2]) << 16) |
123+
(static_cast<uint32_t>(bytes[3]) << 24);
124+
float v;
125+
std::memcpy(&v, &bits, sizeof(v));
126+
out.push_back(v);
127+
}
128+
return out;
129+
}
130+
case Type::DOUBLE: {
131+
std::vector<double> out;
132+
for (size_t i = 0; i < elements_bytes.size(); ++i) {
133+
const std::vector<uint8_t>& bytes = elements_bytes[i];
134+
if (bytes.size() != 8) {
135+
throw std::runtime_error("DecryptTypedListValues: invalid DOUBLE element size");
136+
}
137+
uint64_t bits = static_cast<uint64_t>(bytes[0]) |
138+
(static_cast<uint64_t>(bytes[1]) << 8) |
139+
(static_cast<uint64_t>(bytes[2]) << 16) |
140+
(static_cast<uint64_t>(bytes[3]) << 24) |
141+
(static_cast<uint64_t>(bytes[4]) << 32) |
142+
(static_cast<uint64_t>(bytes[5]) << 40) |
143+
(static_cast<uint64_t>(bytes[6]) << 48) |
144+
(static_cast<uint64_t>(bytes[7]) << 56);
145+
double v;
146+
std::memcpy(&v, &bits, sizeof(v));
147+
out.push_back(v);
148+
}
149+
return out;
150+
}
151+
case Type::INT96: {
152+
std::vector<std::array<uint32_t, 3> > out;
153+
for (size_t i = 0; i < elements_bytes.size(); ++i) {
154+
const std::vector<uint8_t>& bytes = elements_bytes[i];
155+
if (bytes.size() != 12) {
156+
throw std::runtime_error("DecryptTypedListValues: invalid INT96 element size");
157+
}
158+
std::array<uint32_t, 3> a;
159+
a[0] = static_cast<uint32_t>(bytes[0]) |
160+
(static_cast<uint32_t>(bytes[1]) << 8) |
161+
(static_cast<uint32_t>(bytes[2]) << 16) |
162+
(static_cast<uint32_t>(bytes[3]) << 24);
163+
a[1] = static_cast<uint32_t>(bytes[4]) |
164+
(static_cast<uint32_t>(bytes[5]) << 8) |
165+
(static_cast<uint32_t>(bytes[6]) << 16) |
166+
(static_cast<uint32_t>(bytes[7]) << 24);
167+
a[2] = static_cast<uint32_t>(bytes[8]) |
168+
(static_cast<uint32_t>(bytes[9]) << 8) |
169+
(static_cast<uint32_t>(bytes[10]) << 16) |
170+
(static_cast<uint32_t>(bytes[11]) << 24);
171+
out.push_back(a);
172+
}
173+
return out;
174+
}
175+
case Type::BYTE_ARRAY:
176+
case Type::FIXED_LEN_BYTE_ARRAY: {
177+
std::vector<std::string> out;
178+
for (size_t i = 0; i < elements_bytes.size(); ++i) {
179+
const std::vector<uint8_t>& bytes = elements_bytes[i];
180+
out.emplace_back(reinterpret_cast<const char*>(bytes.data()), bytes.size());
181+
}
182+
return out;
183+
}
184+
case Type::UNDEFINED: {
185+
std::vector<uint8_t> out;
186+
for (size_t i = 0; i < elements_bytes.size(); ++i) {
187+
const std::vector<uint8_t>& bytes = elements_bytes[i];
188+
if (bytes.size() != 1) {
189+
throw std::runtime_error("DecryptTypedListValues: invalid UNDEFINED element size");
190+
}
191+
out.push_back(bytes[0]);
192+
}
193+
return out;
194+
}
195+
default:
196+
throw std::runtime_error("DecryptTypedListValues: unsupported datatype");
197+
}
198+
}
199+
} // namespace (anon)
200+
201+
namespace dbps::value_encryption_utils {
202+
203+
std::vector<uint8_t> ConcatenateEncryptedValues(const std::vector<EncryptedValue>& values) {
204+
if (values.size() > static_cast<size_t>(std::numeric_limits<uint32_t>::max())) {
205+
throw std::overflow_error("Too many elements to serialize into uint32 count");
206+
}
207+
208+
// Precompute capacity: 4 bytes for count + for each element (4 bytes size + payload)
209+
size_t total_capacity = 4;
210+
for (size_t i = 0; i < values.size(); ++i) {
211+
const EncryptedValue& ev = values[i];
212+
if (ev.size > static_cast<size_t>(std::numeric_limits<uint32_t>::max())) {
213+
throw std::overflow_error("Element size exceeds uint32 capacity");
214+
}
215+
if (ev.payload.size() < ev.size) {
216+
throw std::runtime_error("ConcatenateEncryptedValues: payload smaller than declared size");
217+
}
218+
total_capacity += 4 + ev.size;
219+
}
220+
221+
std::vector<uint8_t> out;
222+
out.reserve(total_capacity);
223+
224+
append_u32_le(out, static_cast<uint32_t>(values.size()));
225+
226+
for (size_t i = 0; i < values.size(); ++i) {
227+
const EncryptedValue& ev = values[i];
228+
append_u32_le(out, static_cast<uint32_t>(ev.size));
229+
// Append exactly 'size' bytes; prevalidated that payload.size() >= size
230+
out.insert(out.end(), ev.payload.begin(), ev.payload.begin() + ev.size);
231+
}
232+
233+
return out;
234+
}
235+
236+
std::vector<EncryptedValue> ParseConcatenatedEncryptedValues(const std::vector<uint8_t>& blob) {
237+
size_t offset = 0;
238+
if (blob.size() < 4) {
239+
throw std::runtime_error("Malformed input: missing element count");
240+
}
241+
uint32_t count = read_u32_le(blob, offset);
242+
offset += 4;
243+
244+
std::vector<EncryptedValue> result;
245+
result.reserve(static_cast<size_t>(count));
246+
247+
for (uint32_t i = 0; i < count; ++i) {
248+
if (blob.size() - offset < 4) {
249+
throw std::runtime_error("Malformed input: truncated size field");
250+
}
251+
uint32_t sz = read_u32_le(blob, offset);
252+
offset += 4;
253+
254+
if (blob.size() - offset < static_cast<size_t>(sz)) {
255+
throw std::runtime_error("Malformed input: truncated payload bytes");
256+
}
257+
258+
EncryptedValue ev;
259+
ev.size = static_cast<size_t>(sz);
260+
ev.payload.assign(blob.begin() + static_cast<std::ptrdiff_t>(offset),
261+
blob.begin() + static_cast<std::ptrdiff_t>(offset + ev.size));
262+
offset += ev.size;
263+
result.push_back(std::move(ev));
264+
}
265+
266+
// ensure no trailing bytes remain after parsing
267+
if (offset != blob.size()) {
268+
throw std::runtime_error("Malformed input: trailing bytes after parsing EncryptedValue entries");
269+
}
270+
return result;
271+
}
272+
273+
std::vector<EncryptedValue> EncryptTypedListValues(
274+
const TypedListValues& elements,
275+
const std::function<std::vector<uint8_t>(const std::vector<uint8_t>&)>& encrypt_byte_array) {
276+
std::vector<EncryptedValue> encrypted_elements;
277+
278+
std::visit([&](const auto& vec) {
279+
typedef std::decay_t<decltype(vec)> VecT;
280+
typedef typename VecT::value_type ElemT;
281+
282+
auto serialize = [&](const ElemT& elem) -> std::vector<uint8_t> {
283+
std::vector<uint8_t> serialized;
284+
if constexpr (std::is_same<ElemT, int32_t>::value) {
285+
append_i32_le(serialized, elem);
286+
} else if constexpr (std::is_same<ElemT, int64_t>::value) {
287+
append_i64_le(serialized, elem);
288+
} else if constexpr (std::is_same<ElemT, float>::value) {
289+
append_f32_le(serialized, elem);
290+
} else if constexpr (std::is_same<ElemT, double>::value) {
291+
append_f64_le(serialized, elem);
292+
} else if constexpr (std::is_same<ElemT, std::array<uint32_t, 3> >::value) {
293+
append_u32_le(serialized, elem[0]);
294+
append_u32_le(serialized, elem[1]);
295+
append_u32_le(serialized, elem[2]);
296+
} else if constexpr (std::is_same<ElemT, std::string>::value) {
297+
serialized.insert(serialized.end(), elem.begin(), elem.end());
298+
} else if constexpr (std::is_same<ElemT, uint8_t>::value) {
299+
serialized.push_back(elem);
300+
} else {
301+
static_assert(sizeof(ElemT) == 0, "Unsupported element type in TypedListValues");
302+
}
303+
return serialized;
304+
};
305+
306+
for (size_t i = 0; i < vec.size(); ++i) {
307+
const ElemT& elem = vec[i];
308+
std::vector<uint8_t> bytes = serialize(elem);
309+
std::vector<uint8_t> payload = encrypt_byte_array(bytes);
310+
EncryptedValue ev;
311+
ev.size = bytes.size();
312+
ev.payload = std::move(payload);
313+
encrypted_elements.push_back(std::move(ev));
314+
}
315+
}, elements);
316+
317+
return encrypted_elements;
318+
}
319+
320+
TypedListValues DecryptTypedListValues(
321+
const std::vector<EncryptedValue>& encrypted_values,
322+
Type::type datatype,
323+
const std::function<std::vector<uint8_t>(const std::vector<uint8_t>&)>& decrypt_byte_array) {
324+
325+
// 1) Decrypt each element into its raw bytes via callback (respect 'size' field)
326+
std::vector<std::vector<uint8_t> > decrypted_values;
327+
decrypted_values.reserve(encrypted_values.size());
328+
for (size_t i = 0; i < encrypted_values.size(); ++i) {
329+
const EncryptedValue& ev = encrypted_values[i];
330+
std::vector<uint8_t> slice;
331+
if (ev.size > 0) {
332+
const size_t bytes_to_process = ev.size <= ev.payload.size() ? ev.size : ev.payload.size();
333+
slice.assign(ev.payload.begin(), ev.payload.begin() + static_cast<std::ptrdiff_t>(bytes_to_process));
334+
}
335+
std::vector<uint8_t> decrypted = decrypt_byte_array(slice);
336+
decrypted_values.push_back(std::move(decrypted));
337+
}
338+
339+
// 2) Convert all decrypted bytes to a TypedListValues according to datatype
340+
TypedListValues result = BuildTypedListFromDecryptedBytes(datatype, decrypted_values);
341+
return result;
342+
}
343+
344+
} // namespace dbps::value_encryption_utils

0 commit comments

Comments
 (0)