CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
encrypt.cc
Go to the documentation of this file.
1 
6 #include "crypto/encrypt.h"
7 
8 #include <fcntl.h>
9 #include <openssl/evp.h>
10 #include <openssl/rand.h>
11 #include <unistd.h>
12 
13 #include <cassert>
14 #include <cstdlib>
15 #include <cstring>
16 #include <ctime>
17 
18 #include "crypto/hash.h"
19 #include "crypto/openssl_version.h"
20 #include "util/concurrency.h"
21 #include "util/exception.h"
22 #include "util/platform.h"
23 #include "util/pointer.h"
24 #include "util/smalloc.h"
25 #include "util/string.h"
26 #include "util/uuid.h"
27 
28 using namespace std; // NOLINT
29 
30 namespace cipher {
31 
32 Key *Key::CreateRandomly(const unsigned size) {
33  Key *result = new Key();
34  result->size_ = size;
35  result->data_ = reinterpret_cast<unsigned char *>(smalloc(size));
36  // TODO(jblomer): pin memory in RAM
37  int retval = RAND_bytes(result->data_, result->size_);
38  if (retval != 1) {
39  // Not enough entropy
40  delete result;
41  result = NULL;
42  }
43  return result;
44 }
45 
46 
47 Key *Key::CreateFromFile(const string &path) {
48  int fd = open(path.c_str(), O_RDONLY);
49  if (fd < 0)
50  return NULL;
52 
53  platform_stat64 info;
54  int retval = platform_fstat(fd, &info);
55  if (retval != 0) {
56  close(fd);
57  return NULL;
58  }
59  if ((info.st_size == 0) || (info.st_size > kMaxSize)) {
60  close(fd);
61  return NULL;
62  }
63 
64  Key *result = new Key();
65  result->size_ = info.st_size;
66  result->data_ = reinterpret_cast<unsigned char *>(smalloc(result->size_));
67  int nbytes = read(fd, result->data_, result->size_);
68  close(fd);
69  if ((nbytes < 0) || (static_cast<unsigned>(nbytes) != result->size_)) {
70  delete result;
71  result = NULL;
72  }
73  return result;
74 }
75 
76 
77 Key *Key::CreateFromString(const string &key) {
78  unsigned size = key.size();
79  if ((size == 0) || (size > kMaxSize))
80  return NULL;
81  UniquePtr<Key> result(new Key());
82  result->size_ = size;
83  result->data_ = reinterpret_cast<unsigned char *>(smalloc(size));
84  memcpy(result->data_, key.data(), size);
85  return result.Release();
86 }
87 
88 
89 Key::~Key() {
90  if (data_) {
91  memset(data_, 0, size_);
92  free(data_);
93  }
94 }
95 
96 
97 bool Key::SaveToFile(const std::string &path) {
98  int fd = open(path.c_str(), O_WRONLY);
99  if (fd < 0)
100  return false;
102 
103  int nbytes = write(fd, data_, size_);
104  close(fd);
105  return (nbytes >= 0) && (static_cast<unsigned>(nbytes) == size_);
106 }
107 
108 
109 string Key::ToBase64() const {
110  return Base64(string(reinterpret_cast<const char *>(data_), size_));
111 }
112 
113 
114 //------------------------------------------------------------------------------
115 
116 
117 MemoryKeyDatabase::MemoryKeyDatabase() {
118  lock_ = reinterpret_cast<pthread_mutex_t *>(smalloc(sizeof(pthread_mutex_t)));
119  int retval = pthread_mutex_init(lock_, NULL);
120  assert(retval == 0);
121 }
122 
123 
124 MemoryKeyDatabase::~MemoryKeyDatabase() {
125  pthread_mutex_destroy(lock_);
126  free(lock_);
127 }
128 
129 
130 bool MemoryKeyDatabase::StoreNew(const Key *key, string *id) {
131  MutexLockGuard mutex_guard(lock_);
132  // TODO(jblomer): is this good enough for random keys? Salting? KDF2?
134  HashMem(key->data(), key->size(), &hash);
135  *id = "H" + hash.ToString();
136  map<string, const Key *>::const_iterator i = database_.find(*id);
137  if (i != database_.end())
138  return false;
139 
140  database_[*id] = key;
141  return true;
142 }
143 
144 
145 const Key *MemoryKeyDatabase::Find(const string &id) {
146  MutexLockGuard mutex_guard(lock_);
147  map<string, const Key *>::const_iterator i = database_.find(id);
148  if (i != database_.end())
149  return i->second;
150  return NULL;
151 }
152 
153 
154 //------------------------------------------------------------------------------
155 
156 
158  switch (a) {
159  case kAes256Cbc:
160  return new CipherAes256Cbc();
161  case kNone:
162  return new CipherNone();
163  default:
164  PANIC(NULL);
165  }
166  // Never here
167 }
168 
169 
170 bool Cipher::Encrypt(const string &plaintext,
171  const Key &key,
172  string *ciphertext) {
173  ciphertext->clear();
174  if (key.size() != key_size())
175  return false;
176 
177  unsigned char envelope = 0 & 0x0F;
178  envelope |= (algorithm() << 4) & 0xF0;
179  ciphertext->push_back(envelope);
180 
181  *ciphertext += DoEncrypt(plaintext, key);
182  return true;
183 }
184 
185 
186 bool Cipher::Decrypt(const string &ciphertext,
187  const Key &key,
188  string *plaintext) {
189  plaintext->clear();
190  if (ciphertext.size() < 1)
191  return false;
192  unsigned char envelope = ciphertext[0];
193  unsigned char version = envelope & 0x0F;
194  if (version != 0)
195  return false;
196  unsigned char algorithm = (envelope & 0xF0) >> 4;
197  if (algorithm > kNone)
198  return false;
199 
200  UniquePtr<Cipher> cipher(Create(static_cast<Algorithms>(algorithm)));
201  if (key.size() != cipher->key_size())
202  return false;
203  *plaintext += cipher->DoDecrypt(ciphertext.substr(1), key);
204  return true;
205 }
206 
207 
208 //------------------------------------------------------------------------------
209 
210 
211 string CipherAes256Cbc::DoDecrypt(const string &ciphertext, const Key &key) {
212  assert(key.size() == kKeySize);
213  int retval;
214  if (ciphertext.size() < kIvSize)
215  return "";
216 
217  const unsigned char *iv = reinterpret_cast<const unsigned char *>(
218  ciphertext.data());
219 
220  // See OpenSSL documentation for the size
221  unsigned char *plaintext = reinterpret_cast<unsigned char *>(
222  smalloc(kBlockSize + ciphertext.size() - kIvSize));
223  int plaintext_len;
224  int tail_len;
225 #ifdef OPENSSL_API_INTERFACE_V11
226  EVP_CIPHER_CTX *ctx_ptr = EVP_CIPHER_CTX_new();
227 #else
228  EVP_CIPHER_CTX ctx;
229  EVP_CIPHER_CTX_init(&ctx);
230  EVP_CIPHER_CTX *ctx_ptr = &ctx;
231 #endif
232  retval = EVP_DecryptInit_ex(ctx_ptr, EVP_aes_256_cbc(), NULL, key.data(), iv);
233  assert(retval == 1);
234  retval = EVP_DecryptUpdate(
235  ctx_ptr, plaintext, &plaintext_len,
236  reinterpret_cast<const unsigned char *>(ciphertext.data() + kIvSize),
237  ciphertext.length() - kIvSize);
238  if (retval != 1) {
239  free(plaintext);
240 #ifdef OPENSSL_API_INTERFACE_V11
241  EVP_CIPHER_CTX_free(ctx_ptr);
242 #else
243  retval = EVP_CIPHER_CTX_cleanup(&ctx);
244  assert(retval == 1);
245 #endif
246  return "";
247  }
248  retval = EVP_DecryptFinal_ex(ctx_ptr, plaintext + plaintext_len, &tail_len);
249 #ifdef OPENSSL_API_INTERFACE_V11
250  EVP_CIPHER_CTX_free(ctx_ptr);
251 #else
252  int retval_2 = EVP_CIPHER_CTX_cleanup(&ctx);
253  assert(retval_2 == 1);
254 #endif
255  if (retval != 1) {
256  free(plaintext);
257  return "";
258  }
259 
260  plaintext_len += tail_len;
261  if (plaintext_len == 0) {
262  free(plaintext);
263  return "";
264  }
265  string result(reinterpret_cast<char *>(plaintext), plaintext_len);
266  free(plaintext);
267  return result;
268 }
269 
270 
271 string CipherAes256Cbc::DoEncrypt(const string &plaintext, const Key &key) {
272  assert(key.size() == kKeySize);
273  int retval;
274 
275  shash::Md5 md5(GenerateIv(key));
276  // iv size happens to be md5 digest size
277  unsigned char *iv = md5.digest;
278 
279  // See OpenSSL documentation as for the size. Additionally, we prepend the
280  // initialization vector.
281  unsigned char *ciphertext = reinterpret_cast<unsigned char *>(
282  smalloc(kIvSize + 2 * kBlockSize + plaintext.size()));
283  memcpy(ciphertext, iv, kIvSize);
284  int cipher_len = 0;
285  int tail_len = 0;
286 #ifdef OPENSSL_API_INTERFACE_V11
287  EVP_CIPHER_CTX *ctx_ptr = EVP_CIPHER_CTX_new();
288 #else
289  EVP_CIPHER_CTX ctx;
290  EVP_CIPHER_CTX_init(&ctx);
291  EVP_CIPHER_CTX *ctx_ptr = &ctx;
292 #endif
293  retval = EVP_EncryptInit_ex(ctx_ptr, EVP_aes_256_cbc(), NULL, key.data(), iv);
294  assert(retval == 1);
295  // Older versions of OpenSSL don't allow empty input buffers
296  if (!plaintext.empty()) {
297  retval = EVP_EncryptUpdate(
298  ctx_ptr, ciphertext + kIvSize, &cipher_len,
299  reinterpret_cast<const unsigned char *>(plaintext.data()),
300  plaintext.length());
301  assert(retval == 1);
302  }
303  retval = EVP_EncryptFinal_ex(ctx_ptr, ciphertext + kIvSize + cipher_len,
304  &tail_len);
305  assert(retval == 1);
306 #ifdef OPENSSL_API_INTERFACE_V11
307  EVP_CIPHER_CTX_free(ctx_ptr);
308 #else
309  retval = EVP_CIPHER_CTX_cleanup(&ctx);
310  assert(retval == 1);
311 #endif
312 
313  cipher_len += tail_len;
314  assert(cipher_len > 0);
315  string result(reinterpret_cast<char *>(ciphertext), kIvSize + cipher_len);
316  free(ciphertext);
317  return result;
318 }
319 
320 
325 shash::Md5 CipherAes256Cbc::GenerateIv(const Key &key) {
326  // The UUID is random but not necessarily cryptographically random. That
327  // saves the entropy pool.
329  assert(uuid.IsValid());
330 
331  // Now make it unpredictable, using an HMAC with the encryption key.
332  shash::Any hmac(shash::kMd5);
333  shash::Hmac(string(reinterpret_cast<const char *>(key.data()), key.size()),
334  uuid->data(), uuid->size(), &hmac);
335  return hmac.CastToMd5();
336 }
337 
338 
339 //------------------------------------------------------------------------------
340 
341 
342 string CipherNone::DoDecrypt(const string &ciphertext, const Key &key) {
343  return ciphertext;
344 }
345 
346 
347 string CipherNone::DoEncrypt(const string &plaintext, const Key &key) {
348  return plaintext;
349 }
350 
351 } // namespace cipher
struct stat64 platform_stat64
struct cvmcache_context * ctx
static Publisher * Create(const SettingsPublisher &settings)
#define PANIC(...)
Definition: exception.h:29
std::string ToString(const bool with_suffix=false) const
Definition: hash.h:241
Algorithms
Definition: encrypt.h:28
unsigned size() const
Definition: encrypt.h:48
void Hmac(const string &key, const unsigned char *buffer, const unsigned buffer_size, Any *any_digest)
Definition: hash.cc:275
assert((mem||(size==0))&&"Out Of Memory")
unsigned char digest[digest_size_]
Definition: hash.h:121
unsigned size() const
Definition: uuid.h:33
char algorithm
unsigned size_
Definition: encrypt.h:55
unsigned char * data_
Definition: encrypt.h:54
void platform_disable_kcache(int filedes)
static Uuid * Create(const std::string &store_path)
Definition: uuid.cc:23
void HashMem(const unsigned char *buffer, const unsigned buffer_size, Any *any_digest)
Definition: hash.cc:257
string Base64(const string &data)
Definition: string.cc:537
Definition: mutex.h:42
const unsigned char * data() const
Definition: encrypt.h:49
const unsigned char * data() const
Definition: uuid.h:30
int platform_fstat(int filedes, platform_stat64 *buf)
static void size_t size
Definition: smalloc.h:54