]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/crypto.cc
import 14.2.4 nautilus point release
[ceph.git] / ceph / src / test / crypto.cc
1 #include <errno.h>
2 #include <time.h>
3
4 #include <boost/container/small_vector.hpp>
5
6 #include "gtest/gtest.h"
7 #include "include/types.h"
8 #include "auth/Crypto.h"
9 #include "common/Clock.h"
10 #include "common/ceph_crypto.h"
11 #include "common/ceph_context.h"
12 #include "global/global_context.h"
13
14 #ifdef USE_NSS
15 # include <nspr.h>
16 # include <nss.h>
17 # include <pk11pub.h>
18 #endif // USE_NSS
19
20 class CryptoEnvironment: public ::testing::Environment {
21 public:
22 void SetUp() override {
23 ceph::crypto::init(g_ceph_context);
24 }
25 };
26
27 #ifdef USE_NSS
28 // when we say AES, we mean AES-128
29 # define AES_KEY_LEN 16
30 # define AES_BLOCK_LEN 16
31
32 static int nss_aes_operation(CK_ATTRIBUTE_TYPE op,
33 CK_MECHANISM_TYPE mechanism,
34 PK11SymKey *key,
35 SECItem *param,
36 const bufferlist& in, bufferlist& out,
37 std::string *error)
38 {
39 // sample source said this has to be at least size of input + 8,
40 // but i see 15 still fail with SEC_ERROR_OUTPUT_LEN
41 bufferptr out_tmp(in.length()+16);
42 bufferlist incopy;
43
44 SECStatus ret;
45 int written;
46 unsigned char *in_buf;
47
48 PK11Context *ectx;
49 ectx = PK11_CreateContextBySymKey(mechanism, op, key, param);
50 ceph_assert(ectx);
51
52 incopy = in; // it's a shallow copy!
53 in_buf = (unsigned char*)incopy.c_str();
54 ret = PK11_CipherOp(ectx,
55 (unsigned char*)out_tmp.c_str(), &written, out_tmp.length(),
56 in_buf, in.length());
57 if (ret != SECSuccess) {
58 PK11_DestroyContext(ectx, PR_TRUE);
59 if (error) {
60 ostringstream oss;
61 oss << "NSS AES failed: " << PR_GetError();
62 *error = oss.str();
63 }
64 return -1;
65 }
66
67 unsigned int written2;
68 ret = PK11_DigestFinal(ectx,
69 (unsigned char*)out_tmp.c_str()+written, &written2,
70 out_tmp.length()-written);
71 PK11_DestroyContext(ectx, PR_TRUE);
72 if (ret != SECSuccess) {
73 if (error) {
74 ostringstream oss;
75 oss << "NSS AES final round failed: " << PR_GetError();
76 *error = oss.str();
77 }
78 return -1;
79 }
80
81 out_tmp.set_length(written + written2);
82 out.append(out_tmp);
83 return 0;
84 }
85
86 class LegacyCryptoAESKeyHandler : public CryptoKeyHandler {
87 CK_MECHANISM_TYPE mechanism;
88 PK11SlotInfo *slot;
89 PK11SymKey *key;
90 SECItem *param;
91
92 public:
93 LegacyCryptoAESKeyHandler()
94 : CryptoKeyHandler(CryptoKeyHandler::BLOCK_SIZE_16B()),
95 mechanism(CKM_AES_CBC_PAD),
96 slot(NULL),
97 key(NULL),
98 param(NULL) {}
99 ~LegacyCryptoAESKeyHandler() override {
100 SECITEM_FreeItem(param, PR_TRUE);
101 if (key)
102 PK11_FreeSymKey(key);
103 if (slot)
104 PK11_FreeSlot(slot);
105 }
106
107 int init(const bufferptr& s, string& err) {
108 ostringstream oss;
109 const int ret = init(s, oss);
110 err = oss.str();
111 return ret;
112 }
113
114 int init(const bufferptr& s, ostringstream& err) {
115 secret = s;
116
117 slot = PK11_GetBestSlot(mechanism, NULL);
118 if (!slot) {
119 err << "cannot find NSS slot to use: " << PR_GetError();
120 return -1;
121 }
122
123 SECItem keyItem;
124 keyItem.type = siBuffer;
125 keyItem.data = (unsigned char*)secret.c_str();
126 keyItem.len = secret.length();
127 key = PK11_ImportSymKey(slot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT,
128 &keyItem, NULL);
129 if (!key) {
130 err << "cannot convert AES key for NSS: " << PR_GetError();
131 return -1;
132 }
133
134 SECItem ivItem;
135 ivItem.type = siBuffer;
136 // losing constness due to SECItem.data; IV should never be
137 // modified, regardless
138 ivItem.data = (unsigned char*)CEPH_AES_IV;
139 ivItem.len = sizeof(CEPH_AES_IV);
140
141 param = PK11_ParamFromIV(mechanism, &ivItem);
142 if (!param) {
143 err << "cannot set NSS IV param: " << PR_GetError();
144 return -1;
145 }
146
147 return 0;
148 }
149
150 using CryptoKeyHandler::encrypt;
151 using CryptoKeyHandler::decrypt;
152
153 int encrypt(const bufferlist& in,
154 bufferlist& out, std::string *error) const override {
155 return nss_aes_operation(CKA_ENCRYPT, mechanism, key, param, in, out, error);
156 }
157 int decrypt(const bufferlist& in,
158 bufferlist& out, std::string *error) const override {
159 return nss_aes_operation(CKA_DECRYPT, mechanism, key, param, in, out, error);
160 }
161 };
162
163 TEST(AES, ValidateLegacy) {
164 CryptoHandler* const newh = \
165 g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
166
167 const char secret_s[] = {
168 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
169 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
170 };
171 ceph::bufferptr secret(secret_s, sizeof(secret_s));
172
173 std::string error;
174 std::unique_ptr<CryptoKeyHandler> newkh(
175 newh->get_key_handler(secret, error));
176 ASSERT_TRUE(error.empty());
177
178 LegacyCryptoAESKeyHandler oldkh;
179 oldkh.init(secret, error);
180 ASSERT_TRUE(error.empty());
181
182 unsigned char plaintext_s[] = {
183 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
184 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
185 };
186 ceph::bufferlist plaintext;
187 plaintext.append((char *)plaintext_s, sizeof(plaintext_s));
188
189 ceph::bufferlist ciphertext;
190 int r = newkh->encrypt(plaintext, ciphertext, &error);
191 ASSERT_EQ(r, 0);
192 ASSERT_EQ(error, "");
193
194 ceph::bufferlist restored_plaintext;
195 r = oldkh.decrypt(ciphertext, restored_plaintext, &error);
196 ASSERT_EQ(r, 0);
197 ASSERT_TRUE(error.empty());
198
199 ASSERT_EQ(plaintext, restored_plaintext);
200 }
201
202 #else
203 # warning "NSS is not available. Skipping the AES.ValidateLegacy testcase!"
204 #endif // USE_NSS
205
206 TEST(AES, ValidateSecret) {
207 CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
208 int l;
209
210 for (l=0; l<16; l++) {
211 bufferptr bp(l);
212 int err;
213 err = h->validate_secret(bp);
214 EXPECT_EQ(-EINVAL, err);
215 }
216
217 for (l=16; l<50; l++) {
218 bufferptr bp(l);
219 int err;
220 err = h->validate_secret(bp);
221 EXPECT_EQ(0, err);
222 }
223 }
224
225 TEST(AES, Encrypt) {
226 CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
227 char secret_s[] = {
228 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
229 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
230 };
231 bufferptr secret(secret_s, sizeof(secret_s));
232
233 unsigned char plaintext_s[] = {
234 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
235 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
236 };
237 bufferlist plaintext;
238 plaintext.append((char *)plaintext_s, sizeof(plaintext_s));
239
240 bufferlist cipher;
241 std::string error;
242 CryptoKeyHandler *kh = h->get_key_handler(secret, error);
243 int r = kh->encrypt(plaintext, cipher, &error);
244 ASSERT_EQ(r, 0);
245 ASSERT_EQ(error, "");
246
247 unsigned char want_cipher[] = {
248 0xb3, 0x8f, 0x5b, 0xc9, 0x35, 0x4c, 0xf8, 0xc6,
249 0x13, 0x15, 0x66, 0x6f, 0x37, 0xd7, 0x79, 0x3a,
250 0x11, 0x90, 0x7b, 0xe9, 0xd8, 0x3c, 0x35, 0x70,
251 0x58, 0x7b, 0x97, 0x9b, 0x03, 0xd2, 0xa5, 0x01,
252 };
253 char cipher_s[sizeof(want_cipher)];
254
255 ASSERT_EQ(sizeof(cipher_s), cipher.length());
256 cipher.copy(0, sizeof(cipher_s), &cipher_s[0]);
257
258 int err;
259 err = memcmp(cipher_s, want_cipher, sizeof(want_cipher));
260 ASSERT_EQ(0, err);
261
262 delete kh;
263 }
264
265 TEST(AES, EncryptNoBl) {
266 CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
267 char secret_s[] = {
268 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
269 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
270 };
271 bufferptr secret(secret_s, sizeof(secret_s));
272
273 const unsigned char plaintext[] = {
274 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
275 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
276 };
277
278 std::string error;
279 std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler(secret, error));
280
281 const CryptoKey::in_slice_t plain_slice { sizeof(plaintext), plaintext };
282
283 // we need to deduce size first
284 const CryptoKey::out_slice_t probe_slice { 0, nullptr };
285 const auto needed = kh->encrypt(plain_slice, probe_slice);
286 ASSERT_GE(needed, plain_slice.length);
287
288 boost::container::small_vector<
289 // FIXME?
290 //unsigned char, sizeof(plaintext) + kh->get_block_size()> buf;
291 unsigned char, sizeof(plaintext) + 16> buf(needed);
292 const CryptoKey::out_slice_t cipher_slice { needed, buf.data() };
293 const auto cipher_size = kh->encrypt(plain_slice, cipher_slice);
294 ASSERT_EQ(cipher_size, needed);
295
296 const unsigned char want_cipher[] = {
297 0xb3, 0x8f, 0x5b, 0xc9, 0x35, 0x4c, 0xf8, 0xc6,
298 0x13, 0x15, 0x66, 0x6f, 0x37, 0xd7, 0x79, 0x3a,
299 0x11, 0x90, 0x7b, 0xe9, 0xd8, 0x3c, 0x35, 0x70,
300 0x58, 0x7b, 0x97, 0x9b, 0x03, 0xd2, 0xa5, 0x01,
301 };
302
303 ASSERT_EQ(sizeof(want_cipher), cipher_size);
304
305 const int err = memcmp(buf.data(), want_cipher, sizeof(want_cipher));
306 ASSERT_EQ(0, err);
307 }
308
309 TEST(AES, Decrypt) {
310 CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
311 char secret_s[] = {
312 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
313 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
314 };
315 bufferptr secret(secret_s, sizeof(secret_s));
316
317 unsigned char cipher_s[] = {
318 0xb3, 0x8f, 0x5b, 0xc9, 0x35, 0x4c, 0xf8, 0xc6,
319 0x13, 0x15, 0x66, 0x6f, 0x37, 0xd7, 0x79, 0x3a,
320 0x11, 0x90, 0x7b, 0xe9, 0xd8, 0x3c, 0x35, 0x70,
321 0x58, 0x7b, 0x97, 0x9b, 0x03, 0xd2, 0xa5, 0x01,
322 };
323 bufferlist cipher;
324 cipher.append((char *)cipher_s, sizeof(cipher_s));
325
326 unsigned char want_plaintext[] = {
327 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
328 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
329 };
330 char plaintext_s[sizeof(want_plaintext)];
331
332 std::string error;
333 bufferlist plaintext;
334 CryptoKeyHandler *kh = h->get_key_handler(secret, error);
335 int r = kh->decrypt(cipher, plaintext, &error);
336 ASSERT_EQ(r, 0);
337 ASSERT_EQ(error, "");
338
339 ASSERT_EQ(sizeof(plaintext_s), plaintext.length());
340 plaintext.copy(0, sizeof(plaintext_s), &plaintext_s[0]);
341
342 int err;
343 err = memcmp(plaintext_s, want_plaintext, sizeof(want_plaintext));
344 ASSERT_EQ(0, err);
345
346 delete kh;
347 }
348
349 TEST(AES, DecryptNoBl) {
350 CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
351 const char secret_s[] = {
352 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
353 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
354 };
355 bufferptr secret(secret_s, sizeof(secret_s));
356
357 const unsigned char ciphertext[] = {
358 0xb3, 0x8f, 0x5b, 0xc9, 0x35, 0x4c, 0xf8, 0xc6,
359 0x13, 0x15, 0x66, 0x6f, 0x37, 0xd7, 0x79, 0x3a,
360 0x11, 0x90, 0x7b, 0xe9, 0xd8, 0x3c, 0x35, 0x70,
361 0x58, 0x7b, 0x97, 0x9b, 0x03, 0xd2, 0xa5, 0x01,
362 };
363
364 const unsigned char want_plaintext[] = {
365 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
366 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
367 };
368 constexpr static std::size_t plain_buf_size = \
369 CryptoKey::get_max_outbuf_size(sizeof(want_plaintext));
370 unsigned char plaintext[plain_buf_size];
371
372 std::string error;
373 std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler(secret, error));
374
375 CryptoKey::in_slice_t cipher_slice { sizeof(ciphertext), ciphertext };
376 CryptoKey::out_slice_t plain_slice { sizeof(plaintext), plaintext };
377 const auto plain_size = kh->decrypt(cipher_slice, plain_slice);
378
379 ASSERT_EQ(plain_size, sizeof(want_plaintext));
380
381 const int err = memcmp(plaintext, want_plaintext, sizeof(plain_size));
382 ASSERT_EQ(0, err);
383 }
384
385 template <std::size_t TextSizeV>
386 static void aes_loop_cephx() {
387 CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
388
389 CryptoRandom random;
390
391 bufferptr secret(16);
392 random.get_bytes(secret.c_str(), secret.length());
393 std::string error;
394 std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler(secret, error));
395
396 unsigned char plaintext[TextSizeV];
397 random.get_bytes(reinterpret_cast<char*>(plaintext), sizeof(plaintext));
398
399 const CryptoKey::in_slice_t plain_slice { sizeof(plaintext), plaintext };
400
401 // we need to deduce size first
402 const CryptoKey::out_slice_t probe_slice { 0, nullptr };
403 const auto needed = kh->encrypt(plain_slice, probe_slice);
404 ASSERT_GE(needed, plain_slice.length);
405
406 boost::container::small_vector<
407 // FIXME?
408 //unsigned char, sizeof(plaintext) + kh->get_block_size()> buf;
409 unsigned char, sizeof(plaintext) + 16> buf(needed);
410
411 std::size_t cipher_size;
412 for (std::size_t i = 0; i < 1000000; i++) {
413 const CryptoKey::out_slice_t cipher_slice { needed, buf.data() };
414 cipher_size = kh->encrypt(plain_slice, cipher_slice);
415 ASSERT_EQ(cipher_size, needed);
416 }
417 }
418
419 // These magics reflects Cephx's signature size. Please consult
420 // CephxSessionHandler::_calc_signature() for more details.
421 TEST(AES, LoopCephx) {
422 aes_loop_cephx<29>();
423 }
424
425 TEST(AES, LoopCephxV2) {
426 aes_loop_cephx<32>();
427 }
428
429 static void aes_loop(const std::size_t text_size) {
430 CryptoRandom random;
431
432 bufferptr secret(16);
433 random.get_bytes(secret.c_str(), secret.length());
434
435 bufferptr orig_plaintext(text_size);
436 random.get_bytes(orig_plaintext.c_str(), orig_plaintext.length());
437
438 bufferlist plaintext;
439 plaintext.append(orig_plaintext.c_str(), orig_plaintext.length());
440
441 for (int i=0; i<10000; i++) {
442 bufferlist cipher;
443 {
444 CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
445
446 std::string error;
447 CryptoKeyHandler *kh = h->get_key_handler(secret, error);
448 int r = kh->encrypt(plaintext, cipher, &error);
449 ASSERT_EQ(r, 0);
450 ASSERT_EQ(error, "");
451
452 delete kh;
453 }
454 plaintext.clear();
455
456 {
457 CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES);
458 std::string error;
459 CryptoKeyHandler *ckh = h->get_key_handler(secret, error);
460 int r = ckh->decrypt(cipher, plaintext, &error);
461 ASSERT_EQ(r, 0);
462 ASSERT_EQ(error, "");
463
464 delete ckh;
465 }
466 }
467
468 bufferlist orig;
469 orig.append(orig_plaintext);
470 ASSERT_EQ(orig, plaintext);
471 }
472
473 TEST(AES, Loop) {
474 aes_loop(256);
475 }
476
477 // These magics reflects Cephx's signature size. Please consult
478 // CephxSessionHandler::_calc_signature() for more details.
479 TEST(AES, Loop_29) {
480 aes_loop(29);
481 }
482
483 TEST(AES, Loop_32) {
484 aes_loop(32);
485 }
486
487 void aes_loopkey(const std::size_t text_size) {
488 CryptoRandom random;
489 bufferptr k(16);
490 random.get_bytes(k.c_str(), k.length());
491 CryptoKey key(CEPH_CRYPTO_AES, ceph_clock_now(), k);
492
493 bufferlist data;
494 bufferptr r(text_size);
495 random.get_bytes(r.c_str(), r.length());
496 data.append(r);
497
498 utime_t start = ceph_clock_now();
499 int n = 100000;
500
501 for (int i=0; i<n; ++i) {
502 bufferlist encoded;
503 string error;
504 int r = key.encrypt(g_ceph_context, data, encoded, &error);
505 ASSERT_EQ(r, 0);
506 }
507
508 utime_t end = ceph_clock_now();
509 utime_t dur = end - start;
510 cout << n << " encoded in " << dur << std::endl;
511 }
512
513 TEST(AES, LoopKey) {
514 aes_loopkey(128);
515 }
516
517 // These magics reflects Cephx's signature size. Please consult
518 // CephxSessionHandler::_calc_signature() for more details.
519 TEST(AES, LoopKey_29) {
520 aes_loopkey(29);
521 }
522
523 TEST(AES, LoopKey_32) {
524 aes_loopkey(32);
525 }