]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2016 Mirantis <akupczyk@mirantis.com> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | #include <iostream> | |
15 | #include "global/global_init.h" | |
16 | #include "common/ceph_argparse.h" | |
1e59de90 TL |
17 | #include "rgw_common.h" |
18 | #include "rgw_rados.h" | |
19 | #include "rgw_crypt.h" | |
7c673cae | 20 | #include <gtest/gtest.h> |
11fdf7f2 | 21 | #include "include/ceph_assert.h" |
7c673cae FG |
22 | #define dout_subsys ceph_subsys_rgw |
23 | ||
24 | using namespace std; | |
25 | ||
26 | ||
20effc67 | 27 | std::unique_ptr<BlockCrypt> AES_256_CBC_create(const DoutPrefixProvider *dpp, CephContext* cct, const uint8_t* key, size_t len); |
7c673cae | 28 | |
11fdf7f2 | 29 | class ut_get_sink : public RGWGetObj_Filter { |
7c673cae FG |
30 | std::stringstream sink; |
31 | public: | |
32 | ut_get_sink() {} | |
33 | virtual ~ut_get_sink() {} | |
34 | ||
35 | int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) override | |
36 | { | |
f67539c2 | 37 | sink << std::string_view(bl.c_str()+bl_ofs, bl_len); |
7c673cae FG |
38 | return 0; |
39 | } | |
40 | std::string get_sink() | |
41 | { | |
42 | return sink.str(); | |
43 | } | |
44 | }; | |
45 | ||
20effc67 | 46 | class ut_put_sink: public rgw::sal::DataProcessor |
7c673cae FG |
47 | { |
48 | std::stringstream sink; | |
49 | public: | |
11fdf7f2 | 50 | int process(bufferlist&& bl, uint64_t ofs) override |
7c673cae | 51 | { |
f67539c2 | 52 | sink << std::string_view(bl.c_str(),bl.length()); |
7c673cae FG |
53 | return 0; |
54 | } | |
55 | std::string get_sink() | |
56 | { | |
57 | return sink.str(); | |
58 | } | |
59 | }; | |
60 | ||
61 | ||
62 | class BlockCryptNone: public BlockCrypt { | |
a8e16298 | 63 | size_t block_size = 256; |
7c673cae FG |
64 | public: |
65 | BlockCryptNone(){}; | |
a8e16298 | 66 | BlockCryptNone(size_t sz) : block_size(sz) {} |
7c673cae FG |
67 | virtual ~BlockCryptNone(){}; |
68 | size_t get_block_size() override | |
69 | { | |
a8e16298 | 70 | return block_size; |
7c673cae FG |
71 | } |
72 | bool encrypt(bufferlist& input, | |
73 | off_t in_ofs, | |
74 | size_t size, | |
75 | bufferlist& output, | |
76 | off_t stream_offset) override | |
77 | { | |
78 | output.clear(); | |
79 | output.append(input.c_str(), input.length()); | |
80 | return true; | |
81 | } | |
82 | bool decrypt(bufferlist& input, | |
83 | off_t in_ofs, | |
84 | size_t size, | |
85 | bufferlist& output, | |
86 | off_t stream_offset) override | |
87 | { | |
88 | output.clear(); | |
89 | output.append(input.c_str(), input.length()); | |
90 | return true; | |
91 | } | |
92 | }; | |
93 | ||
7c673cae FG |
94 | TEST(TestRGWCrypto, verify_AES_256_CBC_identity) |
95 | { | |
20effc67 | 96 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
7c673cae FG |
97 | //create some input for encryption |
98 | const off_t test_range = 1024*1024; | |
99 | buffer::ptr buf(test_range); | |
100 | char* p = buf.c_str(); | |
101 | for(size_t i = 0; i < buf.length(); i++) | |
102 | p[i] = i + i*i + (i >> 2); | |
103 | ||
104 | bufferlist input; | |
105 | input.append(buf); | |
106 | ||
107 | for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17}) | |
108 | { | |
109 | //make some random key | |
110 | uint8_t key[32]; | |
111 | for(size_t i=0;i<sizeof(key);i++) | |
112 | key[i]=i*step; | |
113 | ||
20effc67 | 114 | auto aes(AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32)); |
7c673cae FG |
115 | ASSERT_NE(aes.get(), nullptr); |
116 | ||
117 | size_t block_size = aes->get_block_size(); | |
118 | ASSERT_NE(block_size, 0u); | |
119 | ||
120 | for (size_t r = 97; r < 123 ; r++) | |
121 | { | |
122 | off_t begin = (r*r*r*r*r % test_range); | |
123 | begin = begin - begin % block_size; | |
124 | off_t end = begin + r*r*r*r*r*r*r % (test_range - begin); | |
125 | if (r % 3) | |
126 | end = end - end % block_size; | |
127 | off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000); | |
128 | offset = offset - offset % block_size; | |
129 | ||
130 | ASSERT_EQ(begin % block_size, 0u); | |
131 | ASSERT_LE(end, test_range); | |
132 | ASSERT_EQ(offset % block_size, 0u); | |
133 | ||
134 | bufferlist encrypted; | |
135 | ASSERT_TRUE(aes->encrypt(input, begin, end - begin, encrypted, offset)); | |
136 | bufferlist decrypted; | |
137 | ASSERT_TRUE(aes->decrypt(encrypted, 0, end - begin, decrypted, offset)); | |
138 | ||
139 | ASSERT_EQ(decrypted.length(), end - begin); | |
f67539c2 TL |
140 | ASSERT_EQ(std::string_view(input.c_str() + begin, end - begin), |
141 | std::string_view(decrypted.c_str(), end - begin) ); | |
7c673cae FG |
142 | } |
143 | } | |
144 | } | |
145 | ||
146 | ||
147 | TEST(TestRGWCrypto, verify_AES_256_CBC_identity_2) | |
148 | { | |
20effc67 | 149 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
7c673cae FG |
150 | //create some input for encryption |
151 | const off_t test_range = 1024*1024; | |
152 | buffer::ptr buf(test_range); | |
153 | char* p = buf.c_str(); | |
154 | for(size_t i = 0; i < buf.length(); i++) | |
155 | p[i] = i + i*i + (i >> 2); | |
156 | ||
157 | bufferlist input; | |
158 | input.append(buf); | |
159 | ||
160 | for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17}) | |
161 | { | |
162 | //make some random key | |
163 | uint8_t key[32]; | |
164 | for(size_t i=0;i<sizeof(key);i++) | |
165 | key[i]=i*step; | |
166 | ||
20effc67 | 167 | auto aes(AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32)); |
7c673cae FG |
168 | ASSERT_NE(aes.get(), nullptr); |
169 | ||
170 | size_t block_size = aes->get_block_size(); | |
171 | ASSERT_NE(block_size, 0u); | |
172 | ||
173 | for (off_t end = 1; end < 6096 ; end+=3) | |
174 | { | |
175 | off_t begin = 0; | |
176 | off_t offset = end*end*end*end*end % (1000*1000*1000); | |
177 | offset = offset - offset % block_size; | |
178 | ||
179 | ASSERT_EQ(begin % block_size, 0u); | |
180 | ASSERT_LE(end, test_range); | |
181 | ASSERT_EQ(offset % block_size, 0u); | |
182 | ||
183 | bufferlist encrypted; | |
184 | ASSERT_TRUE(aes->encrypt(input, begin, end, encrypted, offset)); | |
185 | bufferlist decrypted; | |
186 | ASSERT_TRUE(aes->decrypt(encrypted, 0, end, decrypted, offset)); | |
187 | ||
188 | ASSERT_EQ(decrypted.length(), end); | |
f67539c2 TL |
189 | ASSERT_EQ(std::string_view(input.c_str(), end), |
190 | std::string_view(decrypted.c_str(), end) ); | |
7c673cae FG |
191 | } |
192 | } | |
193 | } | |
194 | ||
195 | ||
196 | TEST(TestRGWCrypto, verify_AES_256_CBC_identity_3) | |
197 | { | |
20effc67 | 198 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
7c673cae FG |
199 | //create some input for encryption |
200 | const off_t test_range = 1024*1024; | |
201 | buffer::ptr buf(test_range); | |
202 | char* p = buf.c_str(); | |
203 | for(size_t i = 0; i < buf.length(); i++) | |
204 | p[i] = i + i*i + (i >> 2); | |
205 | ||
206 | bufferlist input; | |
207 | input.append(buf); | |
208 | ||
209 | for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17}) | |
210 | { | |
211 | //make some random key | |
212 | uint8_t key[32]; | |
213 | for(size_t i=0;i<sizeof(key);i++) | |
214 | key[i]=i*step; | |
215 | ||
20effc67 | 216 | auto aes(AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32)); |
7c673cae FG |
217 | ASSERT_NE(aes.get(), nullptr); |
218 | ||
219 | size_t block_size = aes->get_block_size(); | |
220 | ASSERT_NE(block_size, 0u); | |
221 | size_t rr = 111; | |
222 | for (size_t r = 97; r < 123 ; r++) | |
223 | { | |
224 | off_t begin = 0; | |
225 | off_t end = begin + r*r*r*r*r*r*r % (test_range - begin); | |
226 | //sometimes make aligned | |
227 | if (r % 3) | |
228 | end = end - end % block_size; | |
229 | off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000); | |
230 | offset = offset - offset % block_size; | |
231 | ||
232 | ASSERT_EQ(begin % block_size, 0u); | |
233 | ASSERT_LE(end, test_range); | |
234 | ASSERT_EQ(offset % block_size, 0u); | |
235 | ||
236 | bufferlist encrypted1; | |
237 | bufferlist encrypted2; | |
238 | ||
239 | off_t pos = begin; | |
240 | off_t chunk; | |
241 | while (pos < end) { | |
242 | chunk = block_size + (rr/3)*(rr+17)*(rr+71)*(rr+123)*(rr+131) % 50000; | |
243 | chunk = chunk - chunk % block_size; | |
244 | if (pos + chunk > end) | |
245 | chunk = end - pos; | |
246 | bufferlist tmp; | |
247 | ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos)); | |
248 | encrypted1.append(tmp); | |
249 | pos += chunk; | |
250 | rr++; | |
251 | } | |
252 | ||
253 | pos = begin; | |
254 | while (pos < end) { | |
255 | chunk = block_size + (rr/3)*(rr+97)*(rr+151)*(rr+213)*(rr+251) % 50000; | |
256 | chunk = chunk - chunk % block_size; | |
257 | if (pos + chunk > end) | |
258 | chunk = end - pos; | |
259 | bufferlist tmp; | |
260 | ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos)); | |
261 | encrypted2.append(tmp); | |
262 | pos += chunk; | |
263 | rr++; | |
264 | } | |
265 | ASSERT_EQ(encrypted1.length(), end); | |
266 | ASSERT_EQ(encrypted2.length(), end); | |
f67539c2 TL |
267 | ASSERT_EQ(std::string_view(encrypted1.c_str(), end), |
268 | std::string_view(encrypted2.c_str(), end) ); | |
7c673cae FG |
269 | } |
270 | } | |
271 | } | |
272 | ||
273 | ||
274 | TEST(TestRGWCrypto, verify_AES_256_CBC_size_0_15) | |
275 | { | |
20effc67 | 276 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
7c673cae FG |
277 | //create some input for encryption |
278 | const off_t test_range = 1024*1024; | |
279 | buffer::ptr buf(test_range); | |
280 | char* p = buf.c_str(); | |
281 | for(size_t i = 0; i < buf.length(); i++) | |
282 | p[i] = i + i*i + (i >> 2); | |
283 | ||
284 | bufferlist input; | |
285 | input.append(buf); | |
286 | ||
287 | for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17}) | |
288 | { | |
289 | //make some random key | |
290 | uint8_t key[32]; | |
291 | for(size_t i=0;i<sizeof(key);i++) | |
292 | key[i]=i*step; | |
293 | ||
20effc67 | 294 | auto aes(AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32)); |
7c673cae FG |
295 | ASSERT_NE(aes.get(), nullptr); |
296 | ||
297 | size_t block_size = aes->get_block_size(); | |
298 | ASSERT_NE(block_size, 0u); | |
299 | for (size_t r = 97; r < 123 ; r++) | |
300 | { | |
301 | off_t begin = 0; | |
302 | off_t end = begin + r*r*r*r*r*r*r % (16); | |
303 | ||
304 | off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000); | |
305 | offset = offset - offset % block_size; | |
306 | ||
307 | ASSERT_EQ(begin % block_size, 0u); | |
308 | ASSERT_LE(end, test_range); | |
309 | ASSERT_EQ(offset % block_size, 0u); | |
310 | ||
311 | bufferlist encrypted; | |
312 | bufferlist decrypted; | |
313 | ASSERT_TRUE(aes->encrypt(input, 0, end, encrypted, offset)); | |
314 | ASSERT_TRUE(aes->encrypt(encrypted, 0, end, decrypted, offset)); | |
315 | ASSERT_EQ(encrypted.length(), end); | |
316 | ASSERT_EQ(decrypted.length(), end); | |
f67539c2 TL |
317 | ASSERT_EQ(std::string_view(input.c_str(), end), |
318 | std::string_view(decrypted.c_str(), end) ); | |
7c673cae FG |
319 | } |
320 | } | |
321 | } | |
322 | ||
323 | ||
324 | TEST(TestRGWCrypto, verify_AES_256_CBC_identity_last_block) | |
325 | { | |
20effc67 | 326 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
7c673cae FG |
327 | //create some input for encryption |
328 | const off_t test_range = 1024*1024; | |
329 | buffer::ptr buf(test_range); | |
330 | char* p = buf.c_str(); | |
331 | for(size_t i = 0; i < buf.length(); i++) | |
332 | p[i] = i + i*i + (i >> 2); | |
333 | ||
334 | bufferlist input; | |
335 | input.append(buf); | |
336 | ||
337 | for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17}) | |
338 | { | |
339 | //make some random key | |
340 | uint8_t key[32]; | |
341 | for(size_t i=0;i<sizeof(key);i++) | |
342 | key[i]=i*step; | |
343 | ||
20effc67 | 344 | auto aes(AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32)); |
7c673cae FG |
345 | ASSERT_NE(aes.get(), nullptr); |
346 | ||
347 | size_t block_size = aes->get_block_size(); | |
348 | ASSERT_NE(block_size, 0u); | |
349 | size_t rr = 111; | |
350 | for (size_t r = 97; r < 123 ; r++) | |
351 | { | |
352 | off_t begin = 0; | |
353 | off_t end = r*r*r*r*r*r*r % (test_range - 16); | |
354 | end = end - end % block_size; | |
355 | end = end + (r+3)*(r+5)*(r+7) % 16; | |
356 | ||
357 | off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000); | |
358 | offset = offset - offset % block_size; | |
359 | ||
360 | ASSERT_EQ(begin % block_size, 0u); | |
361 | ASSERT_LE(end, test_range); | |
362 | ASSERT_EQ(offset % block_size, 0u); | |
363 | ||
364 | bufferlist encrypted1; | |
365 | bufferlist encrypted2; | |
366 | ||
367 | off_t pos = begin; | |
368 | off_t chunk; | |
369 | while (pos < end) { | |
370 | chunk = block_size + (rr/3)*(rr+17)*(rr+71)*(rr+123)*(rr+131) % 50000; | |
371 | chunk = chunk - chunk % block_size; | |
372 | if (pos + chunk > end) | |
373 | chunk = end - pos; | |
374 | bufferlist tmp; | |
375 | ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos)); | |
376 | encrypted1.append(tmp); | |
377 | pos += chunk; | |
378 | rr++; | |
379 | } | |
380 | pos = begin; | |
381 | while (pos < end) { | |
382 | chunk = block_size + (rr/3)*(rr+97)*(rr+151)*(rr+213)*(rr+251) % 50000; | |
383 | chunk = chunk - chunk % block_size; | |
384 | if (pos + chunk > end) | |
385 | chunk = end - pos; | |
386 | bufferlist tmp; | |
387 | ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos)); | |
388 | encrypted2.append(tmp); | |
389 | pos += chunk; | |
390 | rr++; | |
391 | } | |
392 | ASSERT_EQ(encrypted1.length(), end); | |
393 | ASSERT_EQ(encrypted2.length(), end); | |
f67539c2 TL |
394 | ASSERT_EQ(std::string_view(encrypted1.c_str(), end), |
395 | std::string_view(encrypted2.c_str(), end) ); | |
7c673cae FG |
396 | } |
397 | } | |
398 | } | |
399 | ||
400 | ||
401 | TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_ranges) | |
402 | { | |
20effc67 | 403 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
7c673cae FG |
404 | //create some input for encryption |
405 | const off_t test_range = 1024*1024; | |
406 | bufferptr buf(test_range); | |
407 | char* p = buf.c_str(); | |
408 | for(size_t i = 0; i < buf.length(); i++) | |
409 | p[i] = i + i*i + (i >> 2); | |
410 | ||
411 | bufferlist input; | |
412 | input.append(buf); | |
413 | ||
414 | uint8_t key[32]; | |
415 | for(size_t i=0;i<sizeof(key);i++) | |
416 | key[i] = i; | |
417 | ||
20effc67 | 418 | auto cbc = AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32); |
7c673cae FG |
419 | ASSERT_NE(cbc.get(), nullptr); |
420 | bufferlist encrypted; | |
421 | ASSERT_TRUE(cbc->encrypt(input, 0, test_range, encrypted, 0)); | |
422 | ||
423 | ||
424 | for (off_t r = 93; r < 150; r++ ) | |
425 | { | |
426 | ut_get_sink get_sink; | |
20effc67 | 427 | auto cbc = AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32); |
7c673cae | 428 | ASSERT_NE(cbc.get(), nullptr); |
aee94f69 | 429 | RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink, std::move(cbc), {}); |
7c673cae FG |
430 | |
431 | //random ranges | |
432 | off_t begin = (r/3)*r*(r+13)*(r+23)*(r+53)*(r+71) % test_range; | |
433 | off_t end = begin + (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - begin) - 1; | |
434 | ||
435 | off_t f_begin = begin; | |
436 | off_t f_end = end; | |
437 | decrypt.fixup_range(f_begin, f_end); | |
438 | decrypt.handle_data(encrypted, f_begin, f_end - f_begin + 1); | |
439 | decrypt.flush(); | |
440 | const std::string& decrypted = get_sink.get_sink(); | |
441 | size_t expected_len = end - begin + 1; | |
442 | ASSERT_EQ(decrypted.length(), expected_len); | |
f67539c2 | 443 | ASSERT_EQ(decrypted, std::string_view(input.c_str()+begin, expected_len)); |
7c673cae FG |
444 | } |
445 | } | |
446 | ||
447 | ||
448 | TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_chunks) | |
449 | { | |
20effc67 | 450 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
7c673cae FG |
451 | //create some input for encryption |
452 | const off_t test_range = 1024*1024; | |
453 | bufferptr buf(test_range); | |
454 | char* p = buf.c_str(); | |
455 | for(size_t i = 0; i < buf.length(); i++) | |
456 | p[i] = i + i*i + (i >> 2); | |
457 | ||
458 | bufferlist input; | |
459 | input.append(buf); | |
460 | ||
461 | uint8_t key[32]; | |
462 | for(size_t i=0;i<sizeof(key);i++) | |
463 | key[i] = i; | |
464 | ||
20effc67 | 465 | auto cbc = AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32); |
7c673cae FG |
466 | ASSERT_NE(cbc.get(), nullptr); |
467 | bufferlist encrypted; | |
468 | ASSERT_TRUE(cbc->encrypt(input, 0, test_range, encrypted, 0)); | |
469 | ||
470 | for (off_t r = 93; r < 150; r++ ) | |
471 | { | |
472 | ut_get_sink get_sink; | |
20effc67 | 473 | auto cbc = AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32); |
7c673cae | 474 | ASSERT_NE(cbc.get(), nullptr); |
aee94f69 | 475 | RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink, std::move(cbc), {}); |
7c673cae FG |
476 | |
477 | //random | |
478 | off_t begin = (r/3)*r*(r+13)*(r+23)*(r+53)*(r+71) % test_range; | |
479 | off_t end = begin + (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - begin) - 1; | |
480 | ||
481 | off_t f_begin = begin; | |
482 | off_t f_end = end; | |
483 | decrypt.fixup_range(f_begin, f_end); | |
484 | off_t pos = f_begin; | |
485 | do | |
486 | { | |
487 | off_t size = 2 << ((pos * 17 + pos / 113 + r) % 16); | |
488 | size = (pos + 1117) * (pos + 2229) % size + 1; | |
489 | if (pos + size > f_end + 1) | |
490 | size = f_end + 1 - pos; | |
491 | ||
492 | decrypt.handle_data(encrypted, pos, size); | |
493 | pos = pos + size; | |
494 | } while (pos < f_end + 1); | |
495 | decrypt.flush(); | |
496 | ||
497 | const std::string& decrypted = get_sink.get_sink(); | |
498 | size_t expected_len = end - begin + 1; | |
499 | ASSERT_EQ(decrypted.length(), expected_len); | |
f67539c2 | 500 | ASSERT_EQ(decrypted, std::string_view(input.c_str()+begin, expected_len)); |
7c673cae FG |
501 | } |
502 | } | |
503 | ||
504 | ||
505 | using range_t = std::pair<off_t, off_t>; | |
506 | ||
507 | // call filter->fixup_range() and return the range as a pair. this makes it easy | |
508 | // to fit on a single line for ASSERT_EQ() | |
509 | range_t fixup_range(RGWGetObj_BlockDecrypt *decrypt, off_t ofs, off_t end) | |
510 | { | |
511 | decrypt->fixup_range(ofs, end); | |
512 | return {ofs, end}; | |
513 | } | |
514 | ||
515 | TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup) | |
516 | { | |
20effc67 | 517 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
7c673cae FG |
518 | ut_get_sink get_sink; |
519 | auto nonecrypt = std::unique_ptr<BlockCrypt>(new BlockCryptNone); | |
20effc67 | 520 | RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink, |
aee94f69 | 521 | std::move(nonecrypt), {}); |
7c673cae FG |
522 | ASSERT_EQ(fixup_range(&decrypt,0,0), range_t(0,255)); |
523 | ASSERT_EQ(fixup_range(&decrypt,1,256), range_t(0,511)); | |
524 | ASSERT_EQ(fixup_range(&decrypt,0,255), range_t(0,255)); | |
525 | ASSERT_EQ(fixup_range(&decrypt,255,256), range_t(0,511)); | |
526 | ASSERT_EQ(fixup_range(&decrypt,511,1023), range_t(256,1023)); | |
527 | ASSERT_EQ(fixup_range(&decrypt,513,1024), range_t(512,1024+255)); | |
528 | } | |
529 | ||
a8e16298 TL |
530 | std::vector<size_t> create_mp_parts(size_t obj_size, size_t mp_part_len){ |
531 | std::vector<size_t> parts_len; | |
532 | size_t part_size; | |
533 | size_t ofs=0; | |
534 | ||
535 | while (ofs < obj_size){ | |
536 | part_size = std::min(mp_part_len, (obj_size - ofs)); | |
537 | ofs += part_size; | |
538 | parts_len.push_back(part_size); | |
539 | } | |
540 | return parts_len; | |
541 | } | |
542 | ||
543 | const size_t part_size = 5*1024*1024; | |
544 | const size_t obj_size = 30*1024*1024; | |
545 | ||
546 | TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup_simple) | |
547 | { | |
20effc67 | 548 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
a8e16298 TL |
549 | |
550 | ut_get_sink get_sink; | |
11fdf7f2 | 551 | auto nonecrypt = std::make_unique<BlockCryptNone>(4096); |
aee94f69 TL |
552 | RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink, |
553 | std::move(nonecrypt), | |
554 | create_mp_parts(obj_size, part_size)); | |
a8e16298 TL |
555 | ASSERT_EQ(fixup_range(&decrypt,0,0), range_t(0,4095)); |
556 | ASSERT_EQ(fixup_range(&decrypt,1,4096), range_t(0,8191)); | |
557 | ASSERT_EQ(fixup_range(&decrypt,0,4095), range_t(0,4095)); | |
558 | ASSERT_EQ(fixup_range(&decrypt,4095,4096), range_t(0,8191)); | |
559 | ||
560 | // ranges are end-end inclusive, we request bytes just spanning short of first | |
561 | // part to exceeding the first part, part_size - 1 is aligned to a 4095 boundary | |
562 | ASSERT_EQ(fixup_range(&decrypt, 0, part_size - 2), range_t(0, part_size -1)); | |
563 | ASSERT_EQ(fixup_range(&decrypt, 0, part_size - 1), range_t(0, part_size -1)); | |
564 | ASSERT_EQ(fixup_range(&decrypt, 0, part_size), range_t(0, part_size + 4095)); | |
565 | ASSERT_EQ(fixup_range(&decrypt, 0, part_size + 1), range_t(0, part_size + 4095)); | |
566 | ||
567 | // request bytes spanning 2 parts | |
568 | ASSERT_EQ(fixup_range(&decrypt, part_size -2, part_size + 2), | |
569 | range_t(part_size - 4096, part_size + 4095)); | |
570 | ||
571 | // request last byte | |
572 | ASSERT_EQ(fixup_range(&decrypt, obj_size - 1, obj_size -1), | |
573 | range_t(obj_size - 4096, obj_size -1)); | |
574 | ||
575 | } | |
576 | ||
577 | TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup_non_aligned_obj_size) | |
578 | { | |
20effc67 | 579 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
a8e16298 | 580 | |
aee94f69 TL |
581 | const size_t na_obj_size = obj_size + 1; |
582 | ||
a8e16298 | 583 | ut_get_sink get_sink; |
11fdf7f2 | 584 | auto nonecrypt = std::make_unique<BlockCryptNone>(4096); |
aee94f69 TL |
585 | RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink, |
586 | std::move(nonecrypt), | |
587 | create_mp_parts(na_obj_size, part_size)); | |
a8e16298 TL |
588 | |
589 | // these should be unaffected here | |
590 | ASSERT_EQ(fixup_range(&decrypt, 0, part_size - 2), range_t(0, part_size -1)); | |
591 | ASSERT_EQ(fixup_range(&decrypt, 0, part_size - 1), range_t(0, part_size -1)); | |
592 | ASSERT_EQ(fixup_range(&decrypt, 0, part_size), range_t(0, part_size + 4095)); | |
593 | ASSERT_EQ(fixup_range(&decrypt, 0, part_size + 1), range_t(0, part_size + 4095)); | |
594 | ||
595 | ||
596 | // request last 2 bytes; spanning 2 parts | |
597 | ASSERT_EQ(fixup_range(&decrypt, na_obj_size -2 , na_obj_size -1), | |
598 | range_t(na_obj_size - 1 - 4096, na_obj_size - 1)); | |
599 | ||
600 | // request last byte, spans last 1B part only | |
601 | ASSERT_EQ(fixup_range(&decrypt, na_obj_size -1, na_obj_size - 1), | |
602 | range_t(na_obj_size - 1, na_obj_size -1)); | |
603 | ||
604 | } | |
605 | ||
606 | TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup_non_aligned_part_size) | |
607 | { | |
20effc67 | 608 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
a8e16298 | 609 | |
aee94f69 TL |
610 | const size_t na_part_size = part_size + 1; |
611 | ||
a8e16298 | 612 | ut_get_sink get_sink; |
11fdf7f2 | 613 | auto nonecrypt = std::make_unique<BlockCryptNone>(4096); |
aee94f69 TL |
614 | RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink, |
615 | std::move(nonecrypt), | |
616 | create_mp_parts(obj_size, na_part_size)); | |
a8e16298 TL |
617 | |
618 | // na_part_size -2, ie. part_size -1 is aligned to 4095 boundary | |
619 | ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size - 2), range_t(0, na_part_size -2)); | |
620 | // even though na_part_size -1 should not align to a 4095 boundary, the range | |
621 | // should not span the next part | |
622 | ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size - 1), range_t(0, na_part_size -1)); | |
623 | ||
624 | ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size), range_t(0, na_part_size + 4095)); | |
625 | ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size + 1), range_t(0, na_part_size + 4095)); | |
626 | ||
627 | // request spanning 2 parts | |
628 | ASSERT_EQ(fixup_range(&decrypt, na_part_size - 2, na_part_size + 2), | |
629 | range_t(na_part_size - 1 - 4096, na_part_size + 4095)); | |
630 | ||
631 | // request last byte, this will be interesting, since this a multipart upload | |
632 | // with 5MB+1 size, the last part is actually 5 bytes short of 5 MB, which | |
633 | // should be considered for the ranges alignment; an easier way to look at | |
634 | // this will be that the last offset aligned to a 5MiB part will be 5MiB - | |
635 | // 4095, this is a part that is 5MiB - 5 B | |
636 | ASSERT_EQ(fixup_range(&decrypt, obj_size - 1, obj_size -1), | |
637 | range_t(obj_size +5 -4096, obj_size -1)); | |
638 | ||
639 | } | |
640 | ||
641 | TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup_non_aligned) | |
642 | { | |
20effc67 | 643 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
a8e16298 | 644 | |
aee94f69 TL |
645 | const size_t na_part_size = part_size + 1; |
646 | const size_t na_obj_size = obj_size + 7; // (6*(5MiB + 1) + 1) for the last 1B overflow | |
647 | ||
a8e16298 | 648 | ut_get_sink get_sink; |
11fdf7f2 | 649 | auto nonecrypt = std::make_unique<BlockCryptNone>(4096); |
aee94f69 TL |
650 | RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink, |
651 | std::move(nonecrypt), | |
652 | create_mp_parts(na_obj_size, na_part_size)); | |
a8e16298 TL |
653 | |
654 | // na_part_size -2, ie. part_size -1 is aligned to 4095 boundary | |
655 | ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size - 2), range_t(0, na_part_size -2)); | |
656 | // even though na_part_size -1 should not align to a 4095 boundary, the range | |
657 | // should not span the next part | |
658 | ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size - 1), range_t(0, na_part_size -1)); | |
659 | ||
660 | ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size), range_t(0, na_part_size + 4095)); | |
661 | ASSERT_EQ(fixup_range(&decrypt, 0, na_part_size + 1), range_t(0, na_part_size + 4095)); | |
662 | ||
663 | // request last byte, spans last 1B part only | |
664 | ASSERT_EQ(fixup_range(&decrypt, na_obj_size -1, na_obj_size - 1), | |
665 | range_t(na_obj_size - 1, na_obj_size -1)); | |
666 | ||
667 | ASSERT_EQ(fixup_range(&decrypt, na_obj_size -2, na_obj_size -1), | |
668 | range_t(na_obj_size - 2, na_obj_size -1)); | |
669 | ||
670 | } | |
671 | ||
672 | TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup_invalid_ranges) | |
673 | { | |
20effc67 | 674 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
a8e16298 TL |
675 | |
676 | ut_get_sink get_sink; | |
11fdf7f2 | 677 | auto nonecrypt = std::make_unique<BlockCryptNone>(4096); |
aee94f69 TL |
678 | RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink, |
679 | std::move(nonecrypt), | |
680 | create_mp_parts(obj_size, part_size)); | |
a8e16298 | 681 | |
a8e16298 TL |
682 | |
683 | // the ranges below would be mostly unreachable in current code as rgw | |
684 | // would've returned a 411 before reaching, but we're just doing this to make | |
685 | // sure we don't have invalid access | |
686 | ASSERT_EQ(fixup_range(&decrypt, obj_size - 1, obj_size + 100), | |
687 | range_t(obj_size - 4096, obj_size - 1)); | |
688 | ASSERT_EQ(fixup_range(&decrypt, obj_size, obj_size + 1), | |
689 | range_t(obj_size - 1, obj_size - 1)); | |
690 | ASSERT_EQ(fixup_range(&decrypt, obj_size+1, obj_size + 100), | |
691 | range_t(obj_size - 1, obj_size - 1)); | |
692 | ||
693 | } | |
7c673cae FG |
694 | |
695 | TEST(TestRGWCrypto, verify_RGWPutObj_BlockEncrypt_chunks) | |
696 | { | |
20effc67 | 697 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
7c673cae FG |
698 | //create some input for encryption |
699 | const off_t test_range = 1024*1024; | |
700 | bufferptr buf(test_range); | |
701 | char* p = buf.c_str(); | |
702 | for(size_t i = 0; i < buf.length(); i++) | |
703 | p[i] = i + i*i + (i >> 2); | |
704 | ||
705 | bufferlist input; | |
706 | input.append(buf); | |
707 | ||
708 | uint8_t key[32]; | |
709 | for(size_t i=0;i<sizeof(key);i++) | |
710 | key[i] = i; | |
711 | ||
712 | for (off_t r = 93; r < 150; r++ ) | |
713 | { | |
714 | ut_put_sink put_sink; | |
20effc67 | 715 | auto cbc = AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32); |
7c673cae | 716 | ASSERT_NE(cbc.get(), nullptr); |
20effc67 TL |
717 | RGWPutObj_BlockEncrypt encrypt(&no_dpp, g_ceph_context, &put_sink, |
718 | std::move(cbc)); | |
7c673cae FG |
719 | |
720 | off_t test_size = (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - 1) + 1; | |
721 | off_t pos = 0; | |
722 | do | |
723 | { | |
724 | off_t size = 2 << ((pos * 17 + pos / 113 + r) % 16); | |
725 | size = (pos + 1117) * (pos + 2229) % size + 1; | |
726 | if (pos + size > test_size) | |
727 | size = test_size - pos; | |
728 | ||
729 | bufferlist bl; | |
730 | bl.append(input.c_str()+pos, size); | |
11fdf7f2 | 731 | encrypt.process(std::move(bl), pos); |
7c673cae FG |
732 | |
733 | pos = pos + size; | |
734 | } while (pos < test_size); | |
11fdf7f2 | 735 | encrypt.process({}, pos); |
7c673cae FG |
736 | |
737 | ASSERT_EQ(put_sink.get_sink().length(), static_cast<size_t>(test_size)); | |
738 | ||
20effc67 | 739 | cbc = AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32); |
7c673cae FG |
740 | ASSERT_NE(cbc.get(), nullptr); |
741 | ||
742 | bufferlist encrypted; | |
743 | bufferlist decrypted; | |
744 | encrypted.append(put_sink.get_sink()); | |
745 | ASSERT_TRUE(cbc->decrypt(encrypted, 0, test_size, decrypted, 0)); | |
746 | ||
747 | ASSERT_EQ(decrypted.length(), test_size); | |
f67539c2 TL |
748 | ASSERT_EQ(std::string_view(decrypted.c_str(), test_size), |
749 | std::string_view(input.c_str(), test_size)); | |
7c673cae FG |
750 | } |
751 | } | |
752 | ||
753 | ||
754 | TEST(TestRGWCrypto, verify_Encrypt_Decrypt) | |
755 | { | |
20effc67 | 756 | const NoDoutPrefix no_dpp(g_ceph_context, dout_subsys); |
7c673cae FG |
757 | uint8_t key[32]; |
758 | for(size_t i=0;i<sizeof(key);i++) | |
759 | key[i]=i; | |
760 | ||
761 | size_t fi_a = 0; | |
762 | size_t fi_b = 1; | |
763 | size_t test_size; | |
764 | do | |
765 | { | |
766 | //fibonacci | |
767 | size_t tmp = fi_b; | |
768 | fi_b = fi_a + fi_b; | |
769 | fi_a = tmp; | |
770 | ||
771 | test_size = fi_b; | |
772 | ||
773 | uint8_t* test_in = new uint8_t[test_size]; | |
774 | //fill with something | |
775 | memset(test_in, test_size & 0xff, test_size); | |
776 | ||
777 | ut_put_sink put_sink; | |
20effc67 TL |
778 | RGWPutObj_BlockEncrypt encrypt(&no_dpp, g_ceph_context, &put_sink, |
779 | AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32)); | |
7c673cae FG |
780 | bufferlist bl; |
781 | bl.append((char*)test_in, test_size); | |
11fdf7f2 TL |
782 | encrypt.process(std::move(bl), 0); |
783 | encrypt.process({}, test_size); | |
7c673cae FG |
784 | ASSERT_EQ(put_sink.get_sink().length(), test_size); |
785 | ||
786 | bl.append(put_sink.get_sink().data(), put_sink.get_sink().length()); | |
787 | ASSERT_EQ(bl.length(), test_size); | |
788 | ||
789 | ut_get_sink get_sink; | |
20effc67 | 790 | RGWGetObj_BlockDecrypt decrypt(&no_dpp, g_ceph_context, &get_sink, |
aee94f69 TL |
791 | AES_256_CBC_create(&no_dpp, g_ceph_context, &key[0], 32), |
792 | {}); | |
7c673cae FG |
793 | |
794 | off_t bl_ofs = 0; | |
795 | off_t bl_end = test_size - 1; | |
796 | decrypt.fixup_range(bl_ofs, bl_end); | |
797 | decrypt.handle_data(bl, 0, bl.length()); | |
798 | decrypt.flush(); | |
799 | ASSERT_EQ(get_sink.get_sink().length(), test_size); | |
f67539c2 | 800 | ASSERT_EQ(get_sink.get_sink(), std::string_view((char*)test_in,test_size)); |
7c673cae FG |
801 | } |
802 | while (test_size < 20000); | |
803 | } | |
804 | ||
805 | ||
806 | int main(int argc, char **argv) { | |
20effc67 | 807 | auto args = argv_to_vec(argc, argv); |
11fdf7f2 TL |
808 | auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, |
809 | CODE_ENVIRONMENT_UTILITY, | |
810 | CINIT_FLAG_NO_DEFAULT_CONFIG_FILE); | |
7c673cae FG |
811 | common_init_finish(g_ceph_context); |
812 | ||
813 | ::testing::InitGoogleTest(&argc, argv); | |
814 | return RUN_ALL_TESTS(); | |
815 | } | |
816 |