4 #include <boost/container/small_vector.hpp>
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"
20 class CryptoEnvironment
: public ::testing::Environment
{
22 void SetUp() override
{
23 ceph::crypto::init(g_ceph_context
);
28 // when we say AES, we mean AES-128
29 # define AES_KEY_LEN 16
30 # define AES_BLOCK_LEN 16
32 static int nss_aes_operation(CK_ATTRIBUTE_TYPE op
,
33 CK_MECHANISM_TYPE mechanism
,
36 const bufferlist
& in
, bufferlist
& out
,
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);
46 unsigned char *in_buf
;
49 ectx
= PK11_CreateContextBySymKey(mechanism
, op
, key
, param
);
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(),
57 if (ret
!= SECSuccess
) {
58 PK11_DestroyContext(ectx
, PR_TRUE
);
61 oss
<< "NSS AES failed: " << PR_GetError();
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
) {
75 oss
<< "NSS AES final round failed: " << PR_GetError();
81 out_tmp
.set_length(written
+ written2
);
86 class LegacyCryptoAESKeyHandler
: public CryptoKeyHandler
{
87 CK_MECHANISM_TYPE mechanism
;
93 LegacyCryptoAESKeyHandler()
94 : CryptoKeyHandler(CryptoKeyHandler::BLOCK_SIZE_16B()),
95 mechanism(CKM_AES_CBC_PAD
),
99 ~LegacyCryptoAESKeyHandler() override
{
100 SECITEM_FreeItem(param
, PR_TRUE
);
102 PK11_FreeSymKey(key
);
107 int init(const bufferptr
& s
, string
& err
) {
109 const int ret
= init(s
, oss
);
114 int init(const bufferptr
& s
, ostringstream
& err
) {
117 slot
= PK11_GetBestSlot(mechanism
, NULL
);
119 err
<< "cannot find NSS slot to use: " << PR_GetError();
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
,
130 err
<< "cannot convert AES key for NSS: " << PR_GetError();
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
);
141 param
= PK11_ParamFromIV(mechanism
, &ivItem
);
143 err
<< "cannot set NSS IV param: " << PR_GetError();
150 using CryptoKeyHandler::encrypt
;
151 using CryptoKeyHandler::decrypt
;
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
);
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
);
163 TEST(AES
, ValidateLegacy
) {
164 CryptoHandler
* const newh
= \
165 g_ceph_context
->get_crypto_handler(CEPH_CRYPTO_AES
);
167 const char secret_s
[] = {
168 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
169 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
171 ceph::bufferptr
secret(secret_s
, sizeof(secret_s
));
174 std::unique_ptr
<CryptoKeyHandler
> newkh(
175 newh
->get_key_handler(secret
, error
));
176 ASSERT_TRUE(error
.empty());
178 LegacyCryptoAESKeyHandler oldkh
;
179 oldkh
.init(secret
, error
);
180 ASSERT_TRUE(error
.empty());
182 unsigned char plaintext_s
[] = {
183 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
184 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
186 ceph::bufferlist plaintext
;
187 plaintext
.append((char *)plaintext_s
, sizeof(plaintext_s
));
189 ceph::bufferlist ciphertext
;
190 int r
= newkh
->encrypt(plaintext
, ciphertext
, &error
);
192 ASSERT_EQ(error
, "");
194 ceph::bufferlist restored_plaintext
;
195 r
= oldkh
.decrypt(ciphertext
, restored_plaintext
, &error
);
197 ASSERT_TRUE(error
.empty());
199 ASSERT_EQ(plaintext
, restored_plaintext
);
203 # warning "NSS is not available. Skipping the AES.ValidateLegacy testcase!"
206 TEST(AES
, ValidateSecret
) {
207 CryptoHandler
*h
= g_ceph_context
->get_crypto_handler(CEPH_CRYPTO_AES
);
210 for (l
=0; l
<16; l
++) {
213 err
= h
->validate_secret(bp
);
214 EXPECT_EQ(-EINVAL
, err
);
217 for (l
=16; l
<50; l
++) {
220 err
= h
->validate_secret(bp
);
226 CryptoHandler
*h
= g_ceph_context
->get_crypto_handler(CEPH_CRYPTO_AES
);
228 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
229 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
231 bufferptr
secret(secret_s
, sizeof(secret_s
));
233 unsigned char plaintext_s
[] = {
234 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
235 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
237 bufferlist plaintext
;
238 plaintext
.append((char *)plaintext_s
, sizeof(plaintext_s
));
242 CryptoKeyHandler
*kh
= h
->get_key_handler(secret
, error
);
243 int r
= kh
->encrypt(plaintext
, cipher
, &error
);
245 ASSERT_EQ(error
, "");
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,
253 char cipher_s
[sizeof(want_cipher
)];
255 ASSERT_EQ(sizeof(cipher_s
), cipher
.length());
256 cipher
.copy(0, sizeof(cipher_s
), &cipher_s
[0]);
259 err
= memcmp(cipher_s
, want_cipher
, sizeof(want_cipher
));
265 TEST(AES
, EncryptNoBl
) {
266 CryptoHandler
*h
= g_ceph_context
->get_crypto_handler(CEPH_CRYPTO_AES
);
268 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
269 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
271 bufferptr
secret(secret_s
, sizeof(secret_s
));
273 const unsigned char plaintext
[] = {
274 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
275 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
279 std::unique_ptr
<CryptoKeyHandler
> kh(h
->get_key_handler(secret
, error
));
281 const CryptoKey::in_slice_t plain_slice
{ sizeof(plaintext
), plaintext
};
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
);
288 boost::container::small_vector
<
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
);
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,
303 ASSERT_EQ(sizeof(want_cipher
), cipher_size
);
305 const int err
= memcmp(buf
.data(), want_cipher
, sizeof(want_cipher
));
310 CryptoHandler
*h
= g_ceph_context
->get_crypto_handler(CEPH_CRYPTO_AES
);
312 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
313 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
315 bufferptr
secret(secret_s
, sizeof(secret_s
));
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,
324 cipher
.append((char *)cipher_s
, sizeof(cipher_s
));
326 unsigned char want_plaintext
[] = {
327 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
328 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
330 char plaintext_s
[sizeof(want_plaintext
)];
333 bufferlist plaintext
;
334 CryptoKeyHandler
*kh
= h
->get_key_handler(secret
, error
);
335 int r
= kh
->decrypt(cipher
, plaintext
, &error
);
337 ASSERT_EQ(error
, "");
339 ASSERT_EQ(sizeof(plaintext_s
), plaintext
.length());
340 plaintext
.copy(0, sizeof(plaintext_s
), &plaintext_s
[0]);
343 err
= memcmp(plaintext_s
, want_plaintext
, sizeof(want_plaintext
));
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,
355 bufferptr
secret(secret_s
, sizeof(secret_s
));
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,
364 const unsigned char want_plaintext
[] = {
365 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
366 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
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
];
373 std::unique_ptr
<CryptoKeyHandler
> kh(h
->get_key_handler(secret
, error
));
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
);
379 ASSERT_EQ(plain_size
, sizeof(want_plaintext
));
381 const int err
= memcmp(plaintext
, want_plaintext
, sizeof(plain_size
));
385 template <std::size_t TextSizeV
>
386 static void aes_loop_cephx() {
387 CryptoHandler
*h
= g_ceph_context
->get_crypto_handler(CEPH_CRYPTO_AES
);
391 bufferptr
secret(16);
392 random
.get_bytes(secret
.c_str(), secret
.length());
394 std::unique_ptr
<CryptoKeyHandler
> kh(h
->get_key_handler(secret
, error
));
396 unsigned char plaintext
[TextSizeV
];
397 random
.get_bytes(reinterpret_cast<char*>(plaintext
), sizeof(plaintext
));
399 const CryptoKey::in_slice_t plain_slice
{ sizeof(plaintext
), plaintext
};
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
);
406 boost::container::small_vector
<
408 //unsigned char, sizeof(plaintext) + kh->get_block_size()> buf;
409 unsigned char, sizeof(plaintext
) + 16> buf(needed
);
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
);
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>();
425 TEST(AES
, LoopCephxV2
) {
426 aes_loop_cephx
<32>();
429 static void aes_loop(const std::size_t text_size
) {
432 bufferptr
secret(16);
433 random
.get_bytes(secret
.c_str(), secret
.length());
435 bufferptr
orig_plaintext(text_size
);
436 random
.get_bytes(orig_plaintext
.c_str(), orig_plaintext
.length());
438 bufferlist plaintext
;
439 plaintext
.append(orig_plaintext
.c_str(), orig_plaintext
.length());
441 for (int i
=0; i
<10000; i
++) {
444 CryptoHandler
*h
= g_ceph_context
->get_crypto_handler(CEPH_CRYPTO_AES
);
447 CryptoKeyHandler
*kh
= h
->get_key_handler(secret
, error
);
448 int r
= kh
->encrypt(plaintext
, cipher
, &error
);
450 ASSERT_EQ(error
, "");
457 CryptoHandler
*h
= g_ceph_context
->get_crypto_handler(CEPH_CRYPTO_AES
);
459 CryptoKeyHandler
*ckh
= h
->get_key_handler(secret
, error
);
460 int r
= ckh
->decrypt(cipher
, plaintext
, &error
);
462 ASSERT_EQ(error
, "");
469 orig
.append(orig_plaintext
);
470 ASSERT_EQ(orig
, plaintext
);
477 // These magics reflects Cephx's signature size. Please consult
478 // CephxSessionHandler::_calc_signature() for more details.
487 void aes_loopkey(const std::size_t text_size
) {
490 random
.get_bytes(k
.c_str(), k
.length());
491 CryptoKey
key(CEPH_CRYPTO_AES
, ceph_clock_now(), k
);
494 bufferptr
r(text_size
);
495 random
.get_bytes(r
.c_str(), r
.length());
498 utime_t start
= ceph_clock_now();
501 for (int i
=0; i
<n
; ++i
) {
504 int r
= key
.encrypt(g_ceph_context
, data
, encoded
, &error
);
508 utime_t end
= ceph_clock_now();
509 utime_t dur
= end
- start
;
510 cout
<< n
<< " encoded in " << dur
<< std::endl
;
517 // These magics reflects Cephx's signature size. Please consult
518 // CephxSessionHandler::_calc_signature() for more details.
519 TEST(AES
, LoopKey_29
) {
523 TEST(AES
, LoopKey_32
) {