]> git.proxmox.com Git - ceph.git/blob - ceph/src/auth/Crypto.cc
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / auth / Crypto.cc
1 // vim: ts=8 sw=2 smarttab
2 /*
3 * Ceph - scalable distributed file system
4 *
5 * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
6 *
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.
11 *
12 */
13
14 #include <sstream>
15 #include "Crypto.h"
16 #ifdef USE_CRYPTOPP
17 # include <cryptopp/modes.h>
18 # include <cryptopp/aes.h>
19 # include <cryptopp/filters.h>
20 #elif defined(USE_NSS)
21 # include <nspr.h>
22 # include <nss.h>
23 # include <pk11pub.h>
24 #endif
25
26 #include "include/assert.h"
27 #include "common/Clock.h"
28 #include "common/armor.h"
29 #include "common/ceph_crypto.h"
30 #include "common/hex.h"
31 #include "common/safe_io.h"
32 #include "include/ceph_fs.h"
33 #include "include/compat.h"
34 #include "common/Formatter.h"
35 #include "common/debug.h"
36 #include <errno.h>
37
38 int get_random_bytes(char *buf, int len)
39 {
40 int fd = TEMP_FAILURE_RETRY(::open("/dev/urandom", O_RDONLY));
41 if (fd < 0)
42 return -errno;
43 int ret = safe_read_exact(fd, buf, len);
44 VOID_TEMP_FAILURE_RETRY(::close(fd));
45 return ret;
46 }
47
48 static int get_random_bytes(int len, bufferlist& bl)
49 {
50 char buf[len];
51 get_random_bytes(buf, len);
52 bl.append(buf, len);
53 return 0;
54 }
55
56 uint64_t get_random(uint64_t min_val, uint64_t max_val)
57 {
58 uint64_t r;
59 get_random_bytes((char *)&r, sizeof(r));
60 r = min_val + r % (max_val - min_val + 1);
61 return r;
62 }
63
64
65 // ---------------------------------------------------
66
67 class CryptoNoneKeyHandler : public CryptoKeyHandler {
68 public:
69 int encrypt(const bufferlist& in,
70 bufferlist& out, std::string *error) const override {
71 out = in;
72 return 0;
73 }
74 int decrypt(const bufferlist& in,
75 bufferlist& out, std::string *error) const override {
76 out = in;
77 return 0;
78 }
79 };
80
81 class CryptoNone : public CryptoHandler {
82 public:
83 CryptoNone() { }
84 ~CryptoNone() override {}
85 int get_type() const override {
86 return CEPH_CRYPTO_NONE;
87 }
88 int create(bufferptr& secret) override {
89 return 0;
90 }
91 int validate_secret(const bufferptr& secret) override {
92 return 0;
93 }
94 CryptoKeyHandler *get_key_handler(const bufferptr& secret, string& error) override {
95 return new CryptoNoneKeyHandler;
96 }
97 };
98
99
100 // ---------------------------------------------------
101
102
103 class CryptoAES : public CryptoHandler {
104 public:
105 CryptoAES() { }
106 ~CryptoAES() override {}
107 int get_type() const override {
108 return CEPH_CRYPTO_AES;
109 }
110 int create(bufferptr& secret) override;
111 int validate_secret(const bufferptr& secret) override;
112 CryptoKeyHandler *get_key_handler(const bufferptr& secret, string& error) override;
113 };
114
115 #ifdef USE_CRYPTOPP
116 # define AES_KEY_LEN ((size_t)CryptoPP::AES::DEFAULT_KEYLENGTH)
117 # define AES_BLOCK_LEN ((size_t)CryptoPP::AES::BLOCKSIZE)
118
119 class CryptoAESKeyHandler : public CryptoKeyHandler {
120 public:
121 CryptoPP::AES::Encryption *enc_key;
122 CryptoPP::AES::Decryption *dec_key;
123
124 CryptoAESKeyHandler()
125 : enc_key(NULL),
126 dec_key(NULL) {}
127 ~CryptoAESKeyHandler() {
128 delete enc_key;
129 delete dec_key;
130 }
131
132 int init(const bufferptr& s, ostringstream& err) {
133 secret = s;
134
135 enc_key = new CryptoPP::AES::Encryption(
136 (byte*)secret.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH);
137 dec_key = new CryptoPP::AES::Decryption(
138 (byte*)secret.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH);
139
140 return 0;
141 }
142
143 int encrypt(const bufferlist& in,
144 bufferlist& out, std::string *error) const {
145 string ciphertext;
146 CryptoPP::StringSink *sink = new CryptoPP::StringSink(ciphertext);
147 CryptoPP::CBC_Mode_ExternalCipher::Encryption cbc(
148 *enc_key, (const byte*)CEPH_AES_IV);
149 CryptoPP::StreamTransformationFilter stfEncryptor(cbc, sink);
150
151 for (std::list<bufferptr>::const_iterator it = in.buffers().begin();
152 it != in.buffers().end(); ++it) {
153 const unsigned char *in_buf = (const unsigned char *)it->c_str();
154 stfEncryptor.Put(in_buf, it->length());
155 }
156 try {
157 stfEncryptor.MessageEnd();
158 } catch (CryptoPP::Exception& e) {
159 if (error) {
160 ostringstream oss;
161 oss << "encryptor.MessageEnd::Exception: " << e.GetWhat();
162 *error = oss.str();
163 }
164 return -1;
165 }
166 out.append((const char *)ciphertext.c_str(), ciphertext.length());
167 return 0;
168 }
169
170 int decrypt(const bufferlist& in,
171 bufferlist& out, std::string *error) const {
172 string decryptedtext;
173 CryptoPP::StringSink *sink = new CryptoPP::StringSink(decryptedtext);
174 CryptoPP::CBC_Mode_ExternalCipher::Decryption cbc(
175 *dec_key, (const byte*)CEPH_AES_IV );
176 CryptoPP::StreamTransformationFilter stfDecryptor(cbc, sink);
177 for (std::list<bufferptr>::const_iterator it = in.buffers().begin();
178 it != in.buffers().end(); ++it) {
179 const unsigned char *in_buf = (const unsigned char *)it->c_str();
180 stfDecryptor.Put(in_buf, it->length());
181 }
182
183 try {
184 stfDecryptor.MessageEnd();
185 } catch (CryptoPP::Exception& e) {
186 if (error) {
187 ostringstream oss;
188 oss << "decryptor.MessageEnd::Exception: " << e.GetWhat();
189 *error = oss.str();
190 }
191 return -1;
192 }
193
194 out.append((const char *)decryptedtext.c_str(), decryptedtext.length());
195 return 0;
196 }
197 };
198
199 #elif defined(USE_NSS)
200 // when we say AES, we mean AES-128
201 # define AES_KEY_LEN 16
202 # define AES_BLOCK_LEN 16
203
204 static int nss_aes_operation(CK_ATTRIBUTE_TYPE op,
205 CK_MECHANISM_TYPE mechanism,
206 PK11SymKey *key,
207 SECItem *param,
208 const bufferlist& in, bufferlist& out,
209 std::string *error)
210 {
211 // sample source said this has to be at least size of input + 8,
212 // but i see 15 still fail with SEC_ERROR_OUTPUT_LEN
213 bufferptr out_tmp(in.length()+16);
214 bufferlist incopy;
215
216 SECStatus ret;
217 int written;
218 unsigned char *in_buf;
219
220 PK11Context *ectx;
221 ectx = PK11_CreateContextBySymKey(mechanism, op, key, param);
222 assert(ectx);
223
224 incopy = in; // it's a shallow copy!
225 in_buf = (unsigned char*)incopy.c_str();
226 ret = PK11_CipherOp(ectx,
227 (unsigned char*)out_tmp.c_str(), &written, out_tmp.length(),
228 in_buf, in.length());
229 if (ret != SECSuccess) {
230 PK11_DestroyContext(ectx, PR_TRUE);
231 if (error) {
232 ostringstream oss;
233 oss << "NSS AES failed: " << PR_GetError();
234 *error = oss.str();
235 }
236 return -1;
237 }
238
239 unsigned int written2;
240 ret = PK11_DigestFinal(ectx,
241 (unsigned char*)out_tmp.c_str()+written, &written2,
242 out_tmp.length()-written);
243 PK11_DestroyContext(ectx, PR_TRUE);
244 if (ret != SECSuccess) {
245 if (error) {
246 ostringstream oss;
247 oss << "NSS AES final round failed: " << PR_GetError();
248 *error = oss.str();
249 }
250 return -1;
251 }
252
253 out_tmp.set_length(written + written2);
254 out.append(out_tmp);
255 return 0;
256 }
257
258 class CryptoAESKeyHandler : public CryptoKeyHandler {
259 CK_MECHANISM_TYPE mechanism;
260 PK11SlotInfo *slot;
261 PK11SymKey *key;
262 SECItem *param;
263
264 public:
265 CryptoAESKeyHandler()
266 : mechanism(CKM_AES_CBC_PAD),
267 slot(NULL),
268 key(NULL),
269 param(NULL) {}
270 ~CryptoAESKeyHandler() override {
271 SECITEM_FreeItem(param, PR_TRUE);
272 if (key)
273 PK11_FreeSymKey(key);
274 if (slot)
275 PK11_FreeSlot(slot);
276 }
277
278 int init(const bufferptr& s, ostringstream& err) {
279 secret = s;
280
281 slot = PK11_GetBestSlot(mechanism, NULL);
282 if (!slot) {
283 err << "cannot find NSS slot to use: " << PR_GetError();
284 return -1;
285 }
286
287 SECItem keyItem;
288 keyItem.type = siBuffer;
289 keyItem.data = (unsigned char*)secret.c_str();
290 keyItem.len = secret.length();
291 key = PK11_ImportSymKey(slot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT,
292 &keyItem, NULL);
293 if (!key) {
294 err << "cannot convert AES key for NSS: " << PR_GetError();
295 return -1;
296 }
297
298 SECItem ivItem;
299 ivItem.type = siBuffer;
300 // losing constness due to SECItem.data; IV should never be
301 // modified, regardless
302 ivItem.data = (unsigned char*)CEPH_AES_IV;
303 ivItem.len = sizeof(CEPH_AES_IV);
304
305 param = PK11_ParamFromIV(mechanism, &ivItem);
306 if (!param) {
307 err << "cannot set NSS IV param: " << PR_GetError();
308 return -1;
309 }
310
311 return 0;
312 }
313
314 int encrypt(const bufferlist& in,
315 bufferlist& out, std::string *error) const override {
316 return nss_aes_operation(CKA_ENCRYPT, mechanism, key, param, in, out, error);
317 }
318 int decrypt(const bufferlist& in,
319 bufferlist& out, std::string *error) const override {
320 return nss_aes_operation(CKA_DECRYPT, mechanism, key, param, in, out, error);
321 }
322 };
323
324 #else
325 # error "No supported crypto implementation found."
326 #endif
327
328
329
330 // ------------------------------------------------------------
331
332 int CryptoAES::create(bufferptr& secret)
333 {
334 bufferlist bl;
335 int r = get_random_bytes(AES_KEY_LEN, bl);
336 if (r < 0)
337 return r;
338 secret = buffer::ptr(bl.c_str(), bl.length());
339 return 0;
340 }
341
342 int CryptoAES::validate_secret(const bufferptr& secret)
343 {
344 if (secret.length() < (size_t)AES_KEY_LEN) {
345 return -EINVAL;
346 }
347
348 return 0;
349 }
350
351 CryptoKeyHandler *CryptoAES::get_key_handler(const bufferptr& secret,
352 string& error)
353 {
354 CryptoAESKeyHandler *ckh = new CryptoAESKeyHandler;
355 ostringstream oss;
356 if (ckh->init(secret, oss) < 0) {
357 error = oss.str();
358 delete ckh;
359 return NULL;
360 }
361 return ckh;
362 }
363
364
365
366
367 // --
368
369
370 // ---------------------------------------------------
371
372
373 void CryptoKey::encode(bufferlist& bl) const
374 {
375 ::encode(type, bl);
376 ::encode(created, bl);
377 __u16 len = secret.length();
378 ::encode(len, bl);
379 bl.append(secret);
380 }
381
382 void CryptoKey::decode(bufferlist::iterator& bl)
383 {
384 ::decode(type, bl);
385 ::decode(created, bl);
386 __u16 len;
387 ::decode(len, bl);
388 bufferptr tmp;
389 bl.copy_deep(len, tmp);
390 if (_set_secret(type, tmp) < 0)
391 throw buffer::malformed_input("malformed secret");
392 }
393
394 int CryptoKey::set_secret(int type, const bufferptr& s, utime_t c)
395 {
396 int r = _set_secret(type, s);
397 if (r < 0)
398 return r;
399 this->created = c;
400 return 0;
401 }
402
403 int CryptoKey::_set_secret(int t, const bufferptr& s)
404 {
405 if (s.length() == 0) {
406 secret = s;
407 ckh.reset();
408 return 0;
409 }
410
411 CryptoHandler *ch = CryptoHandler::create(t);
412 if (ch) {
413 int ret = ch->validate_secret(s);
414 if (ret < 0) {
415 delete ch;
416 return ret;
417 }
418 string error;
419 ckh.reset(ch->get_key_handler(s, error));
420 delete ch;
421 if (error.length()) {
422 return -EIO;
423 }
424 } else {
425 return -EOPNOTSUPP;
426 }
427 type = t;
428 secret = s;
429 return 0;
430 }
431
432 int CryptoKey::create(CephContext *cct, int t)
433 {
434 CryptoHandler *ch = CryptoHandler::create(t);
435 if (!ch) {
436 if (cct)
437 lderr(cct) << "ERROR: cct->get_crypto_handler(type=" << t << ") returned NULL" << dendl;
438 return -EOPNOTSUPP;
439 }
440 bufferptr s;
441 int r = ch->create(s);
442 delete ch;
443 if (r < 0)
444 return r;
445
446 r = _set_secret(t, s);
447 if (r < 0)
448 return r;
449 created = ceph_clock_now();
450 return r;
451 }
452
453 void CryptoKey::print(std::ostream &out) const
454 {
455 out << encode_base64();
456 }
457
458 void CryptoKey::to_str(std::string& s) const
459 {
460 int len = secret.length() * 4;
461 char buf[len];
462 hex2str(secret.c_str(), secret.length(), buf, len);
463 s = buf;
464 }
465
466 void CryptoKey::encode_formatted(string label, Formatter *f, bufferlist &bl)
467 {
468 f->open_object_section(label.c_str());
469 f->dump_string("key", encode_base64());
470 f->close_section();
471 f->flush(bl);
472 }
473
474 void CryptoKey::encode_plaintext(bufferlist &bl)
475 {
476 bl.append(encode_base64());
477 }
478
479
480 // ------------------
481
482 CryptoHandler *CryptoHandler::create(int type)
483 {
484 switch (type) {
485 case CEPH_CRYPTO_NONE:
486 return new CryptoNone;
487 case CEPH_CRYPTO_AES:
488 return new CryptoAES;
489 default:
490 return NULL;
491 }
492 }