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