1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2016 Mirantis <akupczyk@mirantis.com>
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.
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>
21 #include "include/assert.h"
22 #define dout_subsys ceph_subsys_rgw
27 std::unique_ptr<BlockCrypt> AES_256_CBC_create(CephContext* cct, const uint8_t* key, size_t len);
30 class ut_get_sink : public RGWGetDataCB {
31 std::stringstream sink;
34 virtual ~ut_get_sink() {}
36 int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) override
38 sink << boost::string_ref(bl.c_str()+bl_ofs, bl_len);
41 std::string get_sink()
47 class ut_put_sink: public RGWPutObjDataProcessor
49 std::stringstream sink;
52 virtual ~ut_put_sink(){}
53 int handle_data(bufferlist& bl, off_t ofs, void **phandle, rgw_raw_obj *pobj, bool *again) override
55 sink << boost::string_ref(bl.c_str(),bl.length());
59 int throttle_data(void *handle, const rgw_raw_obj& obj, uint64_t size, bool need_to_wait) override
63 std::string get_sink()
70 class BlockCryptNone: public BlockCrypt {
73 virtual ~BlockCryptNone(){};
74 size_t get_block_size() override
78 bool encrypt(bufferlist& input,
82 off_t stream_offset) override
85 output.append(input.c_str(), input.length());
88 bool decrypt(bufferlist& input,
92 off_t stream_offset) override
95 output.append(input.c_str(), input.length());
101 TEST(TestRGWCrypto, verify_AES_256_CBC_identity)
103 //create some input for encryption
104 const off_t test_range = 1024*1024;
105 buffer::ptr buf(test_range);
106 char* p = buf.c_str();
107 for(size_t i = 0; i < buf.length(); i++)
108 p[i] = i + i*i + (i >> 2);
113 for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
115 //make some random key
117 for(size_t i=0;i<sizeof(key);i++)
120 auto aes(AES_256_CBC_create(g_ceph_context, &key[0], 32));
121 ASSERT_NE(aes.get(), nullptr);
123 size_t block_size = aes->get_block_size();
124 ASSERT_NE(block_size, 0u);
126 for (size_t r = 97; r < 123 ; r++)
128 off_t begin = (r*r*r*r*r % test_range);
129 begin = begin - begin % block_size;
130 off_t end = begin + r*r*r*r*r*r*r % (test_range - begin);
132 end = end - end % block_size;
133 off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
134 offset = offset - offset % block_size;
136 ASSERT_EQ(begin % block_size, 0u);
137 ASSERT_LE(end, test_range);
138 ASSERT_EQ(offset % block_size, 0u);
140 bufferlist encrypted;
141 ASSERT_TRUE(aes->encrypt(input, begin, end - begin, encrypted, offset));
142 bufferlist decrypted;
143 ASSERT_TRUE(aes->decrypt(encrypted, 0, end - begin, decrypted, offset));
145 ASSERT_EQ(decrypted.length(), end - begin);
146 ASSERT_EQ(boost::string_ref(input.c_str() + begin, end - begin),
147 boost::string_ref(decrypted.c_str(), end - begin) );
153 TEST(TestRGWCrypto, verify_AES_256_CBC_identity_2)
155 //create some input for encryption
156 const off_t test_range = 1024*1024;
157 buffer::ptr buf(test_range);
158 char* p = buf.c_str();
159 for(size_t i = 0; i < buf.length(); i++)
160 p[i] = i + i*i + (i >> 2);
165 for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
167 //make some random key
169 for(size_t i=0;i<sizeof(key);i++)
172 auto aes(AES_256_CBC_create(g_ceph_context, &key[0], 32));
173 ASSERT_NE(aes.get(), nullptr);
175 size_t block_size = aes->get_block_size();
176 ASSERT_NE(block_size, 0u);
178 for (off_t end = 1; end < 6096 ; end+=3)
181 off_t offset = end*end*end*end*end % (1000*1000*1000);
182 offset = offset - offset % block_size;
184 ASSERT_EQ(begin % block_size, 0u);
185 ASSERT_LE(end, test_range);
186 ASSERT_EQ(offset % block_size, 0u);
188 bufferlist encrypted;
189 ASSERT_TRUE(aes->encrypt(input, begin, end, encrypted, offset));
190 bufferlist decrypted;
191 ASSERT_TRUE(aes->decrypt(encrypted, 0, end, decrypted, offset));
193 ASSERT_EQ(decrypted.length(), end);
194 ASSERT_EQ(boost::string_ref(input.c_str(), end),
195 boost::string_ref(decrypted.c_str(), end) );
201 TEST(TestRGWCrypto, verify_AES_256_CBC_identity_3)
203 //create some input for encryption
204 const off_t test_range = 1024*1024;
205 buffer::ptr buf(test_range);
206 char* p = buf.c_str();
207 for(size_t i = 0; i < buf.length(); i++)
208 p[i] = i + i*i + (i >> 2);
213 for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
215 //make some random key
217 for(size_t i=0;i<sizeof(key);i++)
220 auto aes(AES_256_CBC_create(g_ceph_context, &key[0], 32));
221 ASSERT_NE(aes.get(), nullptr);
223 size_t block_size = aes->get_block_size();
224 ASSERT_NE(block_size, 0u);
226 for (size_t r = 97; r < 123 ; r++)
229 off_t end = begin + r*r*r*r*r*r*r % (test_range - begin);
230 //sometimes make aligned
232 end = end - end % block_size;
233 off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
234 offset = offset - offset % block_size;
236 ASSERT_EQ(begin % block_size, 0u);
237 ASSERT_LE(end, test_range);
238 ASSERT_EQ(offset % block_size, 0u);
240 bufferlist encrypted1;
241 bufferlist encrypted2;
246 chunk = block_size + (rr/3)*(rr+17)*(rr+71)*(rr+123)*(rr+131) % 50000;
247 chunk = chunk - chunk % block_size;
248 if (pos + chunk > end)
251 ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos));
252 encrypted1.append(tmp);
259 chunk = block_size + (rr/3)*(rr+97)*(rr+151)*(rr+213)*(rr+251) % 50000;
260 chunk = chunk - chunk % block_size;
261 if (pos + chunk > end)
264 ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos));
265 encrypted2.append(tmp);
269 ASSERT_EQ(encrypted1.length(), end);
270 ASSERT_EQ(encrypted2.length(), end);
271 ASSERT_EQ(boost::string_ref(encrypted1.c_str(), end),
272 boost::string_ref(encrypted2.c_str(), end) );
278 TEST(TestRGWCrypto, verify_AES_256_CBC_size_0_15)
280 //create some input for encryption
281 const off_t test_range = 1024*1024;
282 buffer::ptr buf(test_range);
283 char* p = buf.c_str();
284 for(size_t i = 0; i < buf.length(); i++)
285 p[i] = i + i*i + (i >> 2);
290 for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
292 //make some random key
294 for(size_t i=0;i<sizeof(key);i++)
297 auto aes(AES_256_CBC_create(g_ceph_context, &key[0], 32));
298 ASSERT_NE(aes.get(), nullptr);
300 size_t block_size = aes->get_block_size();
301 ASSERT_NE(block_size, 0u);
302 for (size_t r = 97; r < 123 ; r++)
305 off_t end = begin + r*r*r*r*r*r*r % (16);
307 off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
308 offset = offset - offset % block_size;
310 ASSERT_EQ(begin % block_size, 0u);
311 ASSERT_LE(end, test_range);
312 ASSERT_EQ(offset % block_size, 0u);
314 bufferlist encrypted;
315 bufferlist decrypted;
316 ASSERT_TRUE(aes->encrypt(input, 0, end, encrypted, offset));
317 ASSERT_TRUE(aes->encrypt(encrypted, 0, end, decrypted, offset));
318 ASSERT_EQ(encrypted.length(), end);
319 ASSERT_EQ(decrypted.length(), end);
320 ASSERT_EQ(boost::string_ref(input.c_str(), end),
321 boost::string_ref(decrypted.c_str(), end) );
327 TEST(TestRGWCrypto, verify_AES_256_CBC_identity_last_block)
329 //create some input for encryption
330 const off_t test_range = 1024*1024;
331 buffer::ptr buf(test_range);
332 char* p = buf.c_str();
333 for(size_t i = 0; i < buf.length(); i++)
334 p[i] = i + i*i + (i >> 2);
339 for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
341 //make some random key
343 for(size_t i=0;i<sizeof(key);i++)
346 auto aes(AES_256_CBC_create(g_ceph_context, &key[0], 32));
347 ASSERT_NE(aes.get(), nullptr);
349 size_t block_size = aes->get_block_size();
350 ASSERT_NE(block_size, 0u);
352 for (size_t r = 97; r < 123 ; r++)
355 off_t end = r*r*r*r*r*r*r % (test_range - 16);
356 end = end - end % block_size;
357 end = end + (r+3)*(r+5)*(r+7) % 16;
359 off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
360 offset = offset - offset % block_size;
362 ASSERT_EQ(begin % block_size, 0u);
363 ASSERT_LE(end, test_range);
364 ASSERT_EQ(offset % block_size, 0u);
366 bufferlist encrypted1;
367 bufferlist encrypted2;
372 chunk = block_size + (rr/3)*(rr+17)*(rr+71)*(rr+123)*(rr+131) % 50000;
373 chunk = chunk - chunk % block_size;
374 if (pos + chunk > end)
377 ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos));
378 encrypted1.append(tmp);
384 chunk = block_size + (rr/3)*(rr+97)*(rr+151)*(rr+213)*(rr+251) % 50000;
385 chunk = chunk - chunk % block_size;
386 if (pos + chunk > end)
389 ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos));
390 encrypted2.append(tmp);
394 ASSERT_EQ(encrypted1.length(), end);
395 ASSERT_EQ(encrypted2.length(), end);
396 ASSERT_EQ(boost::string_ref(encrypted1.c_str(), end),
397 boost::string_ref(encrypted2.c_str(), end) );
403 TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_ranges)
405 //create some input for encryption
406 const off_t test_range = 1024*1024;
407 bufferptr buf(test_range);
408 char* p = buf.c_str();
409 for(size_t i = 0; i < buf.length(); i++)
410 p[i] = i + i*i + (i >> 2);
416 for(size_t i=0;i<sizeof(key);i++)
419 auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 32);
420 ASSERT_NE(cbc.get(), nullptr);
421 bufferlist encrypted;
422 ASSERT_TRUE(cbc->encrypt(input, 0, test_range, encrypted, 0));
425 for (off_t r = 93; r < 150; r++ )
427 ut_get_sink get_sink;
428 auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 32);
429 ASSERT_NE(cbc.get(), nullptr);
430 RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink, std::move(cbc) );
433 off_t begin = (r/3)*r*(r+13)*(r+23)*(r+53)*(r+71) % test_range;
434 off_t end = begin + (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - begin) - 1;
436 off_t f_begin = begin;
438 decrypt.fixup_range(f_begin, f_end);
439 decrypt.handle_data(encrypted, f_begin, f_end - f_begin + 1);
441 const std::string& decrypted = get_sink.get_sink();
442 size_t expected_len = end - begin + 1;
443 ASSERT_EQ(decrypted.length(), expected_len);
444 ASSERT_EQ(decrypted, boost::string_ref(input.c_str()+begin, expected_len));
449 TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_chunks)
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);
462 for(size_t i=0;i<sizeof(key);i++)
465 auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 32);
466 ASSERT_NE(cbc.get(), nullptr);
467 bufferlist encrypted;
468 ASSERT_TRUE(cbc->encrypt(input, 0, test_range, encrypted, 0));
470 for (off_t r = 93; r < 150; r++ )
472 ut_get_sink get_sink;
473 auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 32);
474 ASSERT_NE(cbc.get(), nullptr);
475 RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink, std::move(cbc) );
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;
481 off_t f_begin = begin;
483 decrypt.fixup_range(f_begin, f_end);
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;
492 decrypt.handle_data(encrypted, pos, size);
494 } while (pos < f_end + 1);
497 const std::string& decrypted = get_sink.get_sink();
498 size_t expected_len = end - begin + 1;
499 ASSERT_EQ(decrypted.length(), expected_len);
500 ASSERT_EQ(decrypted, boost::string_ref(input.c_str()+begin, expected_len));
505 using range_t = std::pair<off_t, off_t>;
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)
511 decrypt->fixup_range(ofs, end);
515 TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup)
517 ut_get_sink get_sink;
518 auto nonecrypt = std::unique_ptr<BlockCrypt>(new BlockCryptNone);
519 RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink,
520 std::move(nonecrypt));
521 ASSERT_EQ(fixup_range(&decrypt,0,0), range_t(0,255));
522 ASSERT_EQ(fixup_range(&decrypt,1,256), range_t(0,511));
523 ASSERT_EQ(fixup_range(&decrypt,0,255), range_t(0,255));
524 ASSERT_EQ(fixup_range(&decrypt,255,256), range_t(0,511));
525 ASSERT_EQ(fixup_range(&decrypt,511,1023), range_t(256,1023));
526 ASSERT_EQ(fixup_range(&decrypt,513,1024), range_t(512,1024+255));
530 TEST(TestRGWCrypto, verify_RGWPutObj_BlockEncrypt_chunks)
532 //create some input for encryption
533 const off_t test_range = 1024*1024;
534 bufferptr buf(test_range);
535 char* p = buf.c_str();
536 for(size_t i = 0; i < buf.length(); i++)
537 p[i] = i + i*i + (i >> 2);
543 for(size_t i=0;i<sizeof(key);i++)
546 for (off_t r = 93; r < 150; r++ )
548 ut_put_sink put_sink;
549 auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 32);
550 ASSERT_NE(cbc.get(), nullptr);
551 RGWPutObj_BlockEncrypt encrypt(g_ceph_context, &put_sink,
554 off_t test_size = (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - 1) + 1;
558 off_t size = 2 << ((pos * 17 + pos / 113 + r) % 16);
559 size = (pos + 1117) * (pos + 2229) % size + 1;
560 if (pos + size > test_size)
561 size = test_size - pos;
564 bl.append(input.c_str()+pos, size);
568 encrypt.handle_data(bl, 0, &handle, nullptr, &again);
569 encrypt.throttle_data(handle, ro, size, false);
572 } while (pos < test_size);
576 encrypt.handle_data(bl, 0, &handle, nullptr, &again);
578 ASSERT_EQ(put_sink.get_sink().length(), static_cast<size_t>(test_size));
580 cbc = AES_256_CBC_create(g_ceph_context, &key[0], 32);
581 ASSERT_NE(cbc.get(), nullptr);
583 bufferlist encrypted;
584 bufferlist decrypted;
585 encrypted.append(put_sink.get_sink());
586 ASSERT_TRUE(cbc->decrypt(encrypted, 0, test_size, decrypted, 0));
588 ASSERT_EQ(decrypted.length(), test_size);
589 ASSERT_EQ(boost::string_ref(decrypted.c_str(), test_size),
590 boost::string_ref(input.c_str(), test_size));
595 TEST(TestRGWCrypto, verify_Encrypt_Decrypt)
598 for(size_t i=0;i<sizeof(key);i++)
613 uint8_t* test_in = new uint8_t[test_size];
614 //fill with something
615 memset(test_in, test_size & 0xff, test_size);
617 ut_put_sink put_sink;
618 RGWPutObj_BlockEncrypt encrypt(g_ceph_context, &put_sink,
619 AES_256_CBC_create(g_ceph_context, &key[0], 32) );
621 bl.append((char*)test_in, test_size);
625 encrypt.handle_data(bl, 0, &handle, nullptr, &again);
626 encrypt.throttle_data(handle, ro, test_size, false);
628 encrypt.handle_data(bl, 0, &handle, nullptr, &again);
629 ASSERT_EQ(put_sink.get_sink().length(), test_size);
631 bl.append(put_sink.get_sink().data(), put_sink.get_sink().length());
632 ASSERT_EQ(bl.length(), test_size);
634 ut_get_sink get_sink;
635 RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink,
636 AES_256_CBC_create(g_ceph_context, &key[0], 32) );
639 off_t bl_end = test_size - 1;
640 decrypt.fixup_range(bl_ofs, bl_end);
641 decrypt.handle_data(bl, 0, bl.length());
643 ASSERT_EQ(get_sink.get_sink().length(), test_size);
644 ASSERT_EQ(get_sink.get_sink(), boost::string_ref((char*)test_in,test_size));
646 while (test_size < 20000);
650 int main(int argc, char **argv) {
651 vector<const char*> args;
652 argv_to_vec(argc, (const char **)argv, args);
654 auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
655 common_init_finish(g_ceph_context);
657 ::testing::InitGoogleTest(&argc, argv);
658 return RUN_ALL_TESTS();