1 // vim: ts=8 sw=2 smarttab
3 * Ceph - scalable distributed file system
5 * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
7 * This is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License version 2.1, as published by the Free Software
10 * Foundation. See file COPYING.
20 # include <cryptopp/modes.h>
21 # include <cryptopp/aes.h>
22 # include <cryptopp/filters.h>
23 #elif defined(USE_NSS)
29 #include "include/assert.h"
30 #include "common/Clock.h"
31 #include "common/armor.h"
32 #include "common/ceph_crypto.h"
33 #include "common/hex.h"
34 #include "common/safe_io.h"
35 #include "include/ceph_fs.h"
36 #include "include/compat.h"
37 #include "common/Formatter.h"
38 #include "common/debug.h"
41 int get_random_bytes(char *buf
, int len
)
43 int fd
= TEMP_FAILURE_RETRY(::open("/dev/urandom", O_RDONLY
|O_CLOEXEC
));
46 int ret
= safe_read_exact(fd
, buf
, len
);
47 VOID_TEMP_FAILURE_RETRY(::close(fd
));
51 static int get_random_bytes(int len
, bufferlist
& bl
)
54 get_random_bytes(buf
, len
);
59 uint64_t get_random(uint64_t min_val
, uint64_t max_val
)
62 get_random_bytes((char *)&r
, sizeof(r
));
63 r
= min_val
+ r
% (max_val
- min_val
+ 1);
68 // ---------------------------------------------------
70 class CryptoNoneKeyHandler
: public CryptoKeyHandler
{
72 int encrypt(const bufferlist
& in
,
73 bufferlist
& out
, std::string
*error
) const override
{
77 int decrypt(const bufferlist
& in
,
78 bufferlist
& out
, std::string
*error
) const override
{
84 class CryptoNone
: public CryptoHandler
{
87 ~CryptoNone() override
{}
88 int get_type() const override
{
89 return CEPH_CRYPTO_NONE
;
91 int create(bufferptr
& secret
) override
{
94 int validate_secret(const bufferptr
& secret
) override
{
97 CryptoKeyHandler
*get_key_handler(const bufferptr
& secret
, string
& error
) override
{
98 return new CryptoNoneKeyHandler
;
103 // ---------------------------------------------------
106 class CryptoAES
: public CryptoHandler
{
109 ~CryptoAES() override
{}
110 int get_type() const override
{
111 return CEPH_CRYPTO_AES
;
113 int create(bufferptr
& secret
) override
;
114 int validate_secret(const bufferptr
& secret
) override
;
115 CryptoKeyHandler
*get_key_handler(const bufferptr
& secret
, string
& error
) override
;
119 # define AES_KEY_LEN ((size_t)CryptoPP::AES::DEFAULT_KEYLENGTH)
120 # define AES_BLOCK_LEN ((size_t)CryptoPP::AES::BLOCKSIZE)
122 class CryptoAESKeyHandler
: public CryptoKeyHandler
{
124 CryptoPP::AES::Encryption
*enc_key
;
125 CryptoPP::AES::Decryption
*dec_key
;
127 CryptoAESKeyHandler()
130 ~CryptoAESKeyHandler() {
135 int init(const bufferptr
& s
, ostringstream
& err
) {
138 enc_key
= new CryptoPP::AES::Encryption(
139 (byte
*)secret
.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH
);
140 dec_key
= new CryptoPP::AES::Decryption(
141 (byte
*)secret
.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH
);
146 int encrypt(const bufferlist
& in
,
147 bufferlist
& out
, std::string
*error
) const {
149 CryptoPP::StringSink
*sink
= new CryptoPP::StringSink(ciphertext
);
150 CryptoPP::CBC_Mode_ExternalCipher::Encryption
cbc(
151 *enc_key
, (const byte
*)CEPH_AES_IV
);
152 CryptoPP::StreamTransformationFilter
stfEncryptor(cbc
, sink
);
154 for (std::list
<bufferptr
>::const_iterator it
= in
.buffers().begin();
155 it
!= in
.buffers().end(); ++it
) {
156 const unsigned char *in_buf
= (const unsigned char *)it
->c_str();
157 stfEncryptor
.Put(in_buf
, it
->length());
160 stfEncryptor
.MessageEnd();
161 } catch (CryptoPP::Exception
& e
) {
164 oss
<< "encryptor.MessageEnd::Exception: " << e
.GetWhat();
169 out
.append((const char *)ciphertext
.c_str(), ciphertext
.length());
173 int decrypt(const bufferlist
& in
,
174 bufferlist
& out
, std::string
*error
) const {
175 string decryptedtext
;
176 CryptoPP::StringSink
*sink
= new CryptoPP::StringSink(decryptedtext
);
177 CryptoPP::CBC_Mode_ExternalCipher::Decryption
cbc(
178 *dec_key
, (const byte
*)CEPH_AES_IV
);
179 CryptoPP::StreamTransformationFilter
stfDecryptor(cbc
, sink
);
180 for (std::list
<bufferptr
>::const_iterator it
= in
.buffers().begin();
181 it
!= in
.buffers().end(); ++it
) {
182 const unsigned char *in_buf
= (const unsigned char *)it
->c_str();
183 stfDecryptor
.Put(in_buf
, it
->length());
187 stfDecryptor
.MessageEnd();
188 } catch (CryptoPP::Exception
& e
) {
191 oss
<< "decryptor.MessageEnd::Exception: " << e
.GetWhat();
197 out
.append((const char *)decryptedtext
.c_str(), decryptedtext
.length());
202 #elif defined(USE_NSS)
203 // when we say AES, we mean AES-128
204 # define AES_KEY_LEN 16
205 # define AES_BLOCK_LEN 16
207 static int nss_aes_operation(CK_ATTRIBUTE_TYPE op
,
208 CK_MECHANISM_TYPE mechanism
,
211 const bufferlist
& in
, bufferlist
& out
,
214 // sample source said this has to be at least size of input + 8,
215 // but i see 15 still fail with SEC_ERROR_OUTPUT_LEN
216 bufferptr
out_tmp(in
.length()+16);
221 unsigned char *in_buf
;
224 ectx
= PK11_CreateContextBySymKey(mechanism
, op
, key
, param
);
227 incopy
= in
; // it's a shallow copy!
228 in_buf
= (unsigned char*)incopy
.c_str();
229 ret
= PK11_CipherOp(ectx
,
230 (unsigned char*)out_tmp
.c_str(), &written
, out_tmp
.length(),
231 in_buf
, in
.length());
232 if (ret
!= SECSuccess
) {
233 PK11_DestroyContext(ectx
, PR_TRUE
);
236 oss
<< "NSS AES failed: " << PR_GetError();
242 unsigned int written2
;
243 ret
= PK11_DigestFinal(ectx
,
244 (unsigned char*)out_tmp
.c_str()+written
, &written2
,
245 out_tmp
.length()-written
);
246 PK11_DestroyContext(ectx
, PR_TRUE
);
247 if (ret
!= SECSuccess
) {
250 oss
<< "NSS AES final round failed: " << PR_GetError();
256 out_tmp
.set_length(written
+ written2
);
261 class CryptoAESKeyHandler
: public CryptoKeyHandler
{
262 CK_MECHANISM_TYPE mechanism
;
268 CryptoAESKeyHandler()
269 : mechanism(CKM_AES_CBC_PAD
),
273 ~CryptoAESKeyHandler() override
{
274 SECITEM_FreeItem(param
, PR_TRUE
);
276 PK11_FreeSymKey(key
);
281 int init(const bufferptr
& s
, ostringstream
& err
) {
284 slot
= PK11_GetBestSlot(mechanism
, NULL
);
286 err
<< "cannot find NSS slot to use: " << PR_GetError();
291 keyItem
.type
= siBuffer
;
292 keyItem
.data
= (unsigned char*)secret
.c_str();
293 keyItem
.len
= secret
.length();
294 key
= PK11_ImportSymKey(slot
, mechanism
, PK11_OriginUnwrap
, CKA_ENCRYPT
,
297 err
<< "cannot convert AES key for NSS: " << PR_GetError();
302 ivItem
.type
= siBuffer
;
303 // losing constness due to SECItem.data; IV should never be
304 // modified, regardless
305 ivItem
.data
= (unsigned char*)CEPH_AES_IV
;
306 ivItem
.len
= sizeof(CEPH_AES_IV
);
308 param
= PK11_ParamFromIV(mechanism
, &ivItem
);
310 err
<< "cannot set NSS IV param: " << PR_GetError();
317 int encrypt(const bufferlist
& in
,
318 bufferlist
& out
, std::string
*error
) const override
{
319 return nss_aes_operation(CKA_ENCRYPT
, mechanism
, key
, param
, in
, out
, error
);
321 int decrypt(const bufferlist
& in
,
322 bufferlist
& out
, std::string
*error
) const override
{
323 return nss_aes_operation(CKA_DECRYPT
, mechanism
, key
, param
, in
, out
, error
);
328 # error "No supported crypto implementation found."
333 // ------------------------------------------------------------
335 int CryptoAES::create(bufferptr
& secret
)
338 int r
= get_random_bytes(AES_KEY_LEN
, bl
);
341 secret
= buffer::ptr(bl
.c_str(), bl
.length());
345 int CryptoAES::validate_secret(const bufferptr
& secret
)
347 if (secret
.length() < (size_t)AES_KEY_LEN
) {
354 CryptoKeyHandler
*CryptoAES::get_key_handler(const bufferptr
& secret
,
357 CryptoAESKeyHandler
*ckh
= new CryptoAESKeyHandler
;
359 if (ckh
->init(secret
, oss
) < 0) {
373 // ---------------------------------------------------
376 void CryptoKey::encode(bufferlist
& bl
) const
379 ::encode(created
, bl
);
380 __u16 len
= secret
.length();
385 void CryptoKey::decode(bufferlist::iterator
& bl
)
388 ::decode(created
, bl
);
392 bl
.copy_deep(len
, tmp
);
393 if (_set_secret(type
, tmp
) < 0)
394 throw buffer::malformed_input("malformed secret");
397 int CryptoKey::set_secret(int type
, const bufferptr
& s
, utime_t c
)
399 int r
= _set_secret(type
, s
);
406 int CryptoKey::_set_secret(int t
, const bufferptr
& s
)
408 if (s
.length() == 0) {
414 CryptoHandler
*ch
= CryptoHandler::create(t
);
416 int ret
= ch
->validate_secret(s
);
422 ckh
.reset(ch
->get_key_handler(s
, error
));
424 if (error
.length()) {
435 int CryptoKey::create(CephContext
*cct
, int t
)
437 CryptoHandler
*ch
= CryptoHandler::create(t
);
440 lderr(cct
) << "ERROR: cct->get_crypto_handler(type=" << t
<< ") returned NULL" << dendl
;
444 int r
= ch
->create(s
);
449 r
= _set_secret(t
, s
);
452 created
= ceph_clock_now();
456 void CryptoKey::print(std::ostream
&out
) const
458 out
<< encode_base64();
461 void CryptoKey::to_str(std::string
& s
) const
463 int len
= secret
.length() * 4;
465 hex2str(secret
.c_str(), secret
.length(), buf
, len
);
469 void CryptoKey::encode_formatted(string label
, Formatter
*f
, bufferlist
&bl
)
471 f
->open_object_section(label
.c_str());
472 f
->dump_string("key", encode_base64());
477 void CryptoKey::encode_plaintext(bufferlist
&bl
)
479 bl
.append(encode_base64());
483 // ------------------
485 CryptoHandler
*CryptoHandler::create(int type
)
488 case CEPH_CRYPTO_NONE
:
489 return new CryptoNone
;
490 case CEPH_CRYPTO_AES
:
491 return new CryptoAES
;