GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/encrypt.cc Lines: 159 171 93.0 %
Date: 2019-02-03 02:48:13 Branches: 54 83 65.1 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System
3
 */
4
5
#include "cvmfs_config.h"
6
#include "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 "duplex_ssl.h"
19
#include "hash.h"
20
#include "platform.h"
21
#include "smalloc.h"
22
#include "util/pointer.h"
23
#include "util/string.h"
24
#include "util_concurrency.h"
25
#include "uuid.h"
26
27
using namespace std;  // NOLINT
28
29
namespace cipher {
30
31
100013
Key *Key::CreateRandomly(const unsigned size) {
32
100013
  Key *result = new Key();
33
100013
  result->size_ = size;
34
100013
  result->data_ = reinterpret_cast<unsigned char *>(smalloc(size));
35
  // TODO(jblomer): pin memory in RAM
36
100013
  int retval = RAND_bytes(result->data_, result->size_);
37
100013
  if (retval != 1) {
38
    // Not enough entropy
39
    delete result;
40
    result = NULL;
41
  }
42
100013
  return result;
43
}
44
45
46
3
Key *Key::CreateFromFile(const string &path) {
47
3
  int fd = open(path.c_str(), O_RDONLY);
48
3
  if (fd < 0)
49
1
    return NULL;
50
2
  platform_disable_kcache(fd);
51
52
  platform_stat64 info;
53
2
  int retval = platform_fstat(fd, &info);
54
2
  if (retval != 0) {
55
    close(fd);
56
    return NULL;
57
  }
58

2
  if ((info.st_size == 0) || (info.st_size > kMaxSize)) {
59
1
    close(fd);
60
1
    return NULL;
61
  }
62
63
1
  Key *result = new Key();
64
1
  result->size_ = info.st_size;
65
1
  result->data_ = reinterpret_cast<unsigned char *>(smalloc(result->size_));
66
1
  int nbytes = read(fd, result->data_, result->size_);
67
1
  close(fd);
68

1
  if ((nbytes < 0) || (static_cast<unsigned>(nbytes) != result->size_)) {
69
    delete result;
70
    result = NULL;
71
  }
72
1
  return result;
73
}
74
75
76
7
Key *Key::CreateFromString(const string &key) {
77
7
  unsigned size = key.size();
78

7
  if ((size == 0) || (size > kMaxSize))
79
2
    return NULL;
80
5
  UniquePtr<Key> result(new Key());
81
5
  result->size_ = size;
82
5
  result->data_ = reinterpret_cast<unsigned char *>(smalloc(size));
83
5
  memcpy(result->data_, key.data(), size);
84
5
  return result.Release();
85
}
86
87
88
100019
Key::~Key() {
89
100019
  if (data_) {
90
100019
    memset(data_, 0, size_);
91
100019
    free(data_);
92
  }
93
100019
}
94
95
96
2
bool Key::SaveToFile(const std::string &path) {
97
2
  int fd = open(path.c_str(), O_WRONLY);
98
2
  if (fd < 0)
99
1
    return false;
100
1
  platform_disable_kcache(fd);
101
102
1
  int nbytes = write(fd, data_, size_);
103
1
  close(fd);
104

1
  return (nbytes >= 0) && (static_cast<unsigned>(nbytes) == size_);
105
}
106
107
108
8
string Key::ToBase64() const {
109
8
  return Base64(string(reinterpret_cast<const char *>(data_), size_));
110
}
111
112
113
//------------------------------------------------------------------------------
114
115
116
1
MemoryKeyDatabase::MemoryKeyDatabase() {
117
  lock_ =
118
1
    reinterpret_cast<pthread_mutex_t *>(smalloc(sizeof(pthread_mutex_t)));
119
1
  int retval = pthread_mutex_init(lock_, NULL);
120
1
  assert(retval == 0);
121
}
122
123
124
1
MemoryKeyDatabase::~MemoryKeyDatabase() {
125
1
  pthread_mutex_destroy(lock_);
126
1
  free(lock_);
127
1
}
128
129
130
2
bool MemoryKeyDatabase::StoreNew(const Key *key, string *id) {
131
2
  MutexLockGuard mutex_guard(lock_);
132
  // TODO(jblomer): is this good enough for random keys? Salting? KDF2?
133
2
  shash::Any hash(shash::kShake128);
134
2
  HashMem(key->data(), key->size(), &hash);
135
2
  *id = "H" + hash.ToString();
136
2
  map<string, const Key *>::const_iterator i = database_.find(*id);
137
2
  if (i != database_.end())
138
1
    return false;
139
140
1
  database_[*id] = key;
141
1
  return true;
142
}
143
144
145
2
const Key *MemoryKeyDatabase::Find(const string &id) {
146
2
  MutexLockGuard mutex_guard(lock_);
147
2
  map<string, const Key *>::const_iterator i = database_.find(id);
148
2
  if (i != database_.end())
149
1
    return i->second;
150
1
  return NULL;
151
}
152
153
154
//------------------------------------------------------------------------------
155
156
157
19
Cipher *Cipher::Create(const Algorithms a) {
158
19
  switch (a) {
159
    case kAes256Cbc:
160
15
      return new CipherAes256Cbc();
161
    case kNone:
162
4
      return new CipherNone();
163
    default:
164
      abort();
165
  }
166
  // Never here
167
}
168
169
170
12
bool Cipher::Encrypt(
171
  const string &plaintext,
172
  const Key &key,
173
  string *ciphertext)
174
{
175
12
  ciphertext->clear();
176
12
  if (key.size() != key_size())
177
    return false;
178
179
12
  unsigned char envelope = 0 & 0x0F;
180
12
  envelope |= (algorithm() << 4) & 0xF0;
181
12
  ciphertext->push_back(envelope);
182
183
12
  *ciphertext += DoEncrypt(plaintext, key);
184
12
  return true;
185
}
186
187
188
16
bool Cipher::Decrypt(
189
  const string &ciphertext,
190
  const Key &key,
191
  string *plaintext)
192
{
193
16
  plaintext->clear();
194
16
  if (ciphertext.size() < 1)
195
1
    return false;
196
15
  unsigned char envelope = ciphertext[0];
197
15
  unsigned char version = envelope & 0x0F;
198
15
  if (version != 0)
199
1
    return false;
200
14
  unsigned char algorithm = (envelope & 0xF0) >> 4;
201
14
  if (algorithm > kNone)
202
1
    return false;
203
204
13
  UniquePtr<Cipher> cipher(Create(static_cast<Algorithms>(algorithm)));
205
13
  if (key.size() != cipher->key_size())
206
1
    return false;
207
12
  *plaintext += cipher->DoDecrypt(ciphertext.substr(1), key);
208
12
  return true;
209
}
210
211
212
//------------------------------------------------------------------------------
213
214
215
9
string CipherAes256Cbc::DoDecrypt(const string &ciphertext, const Key &key) {
216
9
  assert(key.size() == kKeySize);
217
  int retval;
218
9
  if (ciphertext.size() < kIvSize)
219
1
    return "";
220
221
  const unsigned char *iv = reinterpret_cast<const unsigned char *>(
222
8
    ciphertext.data());
223
224
  // See OpenSSL documentation for the size
225
  unsigned char *plaintext = reinterpret_cast<unsigned char *>(
226
8
    smalloc(kBlockSize + ciphertext.size() - kIvSize));
227
  int plaintext_len;
228
  int tail_len;
229
#ifdef OPENSSL_API_INTERFACE_V11
230
  EVP_CIPHER_CTX *ctx_ptr = EVP_CIPHER_CTX_new();
231
#else
232
  EVP_CIPHER_CTX ctx;
233
8
  EVP_CIPHER_CTX_init(&ctx);
234
8
  EVP_CIPHER_CTX *ctx_ptr = &ctx;
235
#endif
236
8
  retval = EVP_DecryptInit_ex(ctx_ptr, EVP_aes_256_cbc(), NULL, key.data(), iv);
237
8
  assert(retval == 1);
238
  retval = EVP_DecryptUpdate(ctx_ptr,
239
             plaintext, &plaintext_len,
240
             reinterpret_cast<const unsigned char *>(
241
               ciphertext.data() + kIvSize),
242
8
             ciphertext.length() - kIvSize);
243
8
  if (retval != 1) {
244
    free(plaintext);
245
#ifdef OPENSSL_API_INTERFACE_V11
246
    EVP_CIPHER_CTX_free(ctx_ptr);
247
#else
248
    retval = EVP_CIPHER_CTX_cleanup(&ctx);
249
    assert(retval == 1);
250
#endif
251
    return "";
252
  }
253
8
  retval = EVP_DecryptFinal_ex(ctx_ptr, plaintext + plaintext_len, &tail_len);
254
#ifdef OPENSSL_API_INTERFACE_V11
255
  EVP_CIPHER_CTX_free(ctx_ptr);
256
#else
257
8
  int retval_2 = EVP_CIPHER_CTX_cleanup(&ctx);
258
8
  assert(retval_2 == 1);
259
#endif
260
8
  if (retval != 1) {
261
2
    free(plaintext);
262
2
    return "";
263
  }
264
265
6
  plaintext_len += tail_len;
266
6
  if (plaintext_len == 0) {
267
1
    free(plaintext);
268
1
    return "";
269
  }
270
5
  string result(reinterpret_cast<char *>(plaintext), plaintext_len);
271
5
  free(plaintext);
272
5
  return result;
273
}
274
275
276
10
string CipherAes256Cbc::DoEncrypt(const string &plaintext, const Key &key) {
277
10
  assert(key.size() == kKeySize);
278
  int retval;
279
280
10
  shash::Md5 md5(GenerateIv(key));
281
  // iv size happens to be md5 digest size
282
10
  unsigned char *iv = md5.digest;
283
284
  // See OpenSSL documentation as for the size.  Additionally, we prepend the
285
  // initialization vector.
286
  unsigned char *ciphertext = reinterpret_cast<unsigned char *>(
287
10
    smalloc(kIvSize + 2 * kBlockSize + plaintext.size()));
288
10
  memcpy(ciphertext, iv, kIvSize);
289
10
  int cipher_len = 0;
290
10
  int tail_len = 0;
291
#ifdef OPENSSL_API_INTERFACE_V11
292
  EVP_CIPHER_CTX *ctx_ptr = EVP_CIPHER_CTX_new();
293
#else
294
  EVP_CIPHER_CTX ctx;
295
10
  EVP_CIPHER_CTX_init(&ctx);
296
10
  EVP_CIPHER_CTX *ctx_ptr = &ctx;
297
#endif
298
10
  retval = EVP_EncryptInit_ex(ctx_ptr, EVP_aes_256_cbc(), NULL, key.data(), iv);
299
10
  assert(retval == 1);
300
  // Older versions of OpenSSL don't allow empty input buffers
301
10
  if (!plaintext.empty()) {
302
    retval = EVP_EncryptUpdate(ctx_ptr,
303
               ciphertext + kIvSize, &cipher_len,
304
               reinterpret_cast<const unsigned char *>(plaintext.data()),
305
9
               plaintext.length());
306
9
    assert(retval == 1);
307
  }
308
  retval = EVP_EncryptFinal_ex(ctx_ptr, ciphertext + kIvSize + cipher_len,
309
10
                               &tail_len);
310
10
  assert(retval == 1);
311
#ifdef OPENSSL_API_INTERFACE_V11
312
  EVP_CIPHER_CTX_free(ctx_ptr);
313
#else
314
10
  retval = EVP_CIPHER_CTX_cleanup(&ctx);
315
10
  assert(retval == 1);
316
#endif
317
318
10
  cipher_len += tail_len;
319
10
  assert(cipher_len > 0);
320
10
  string result(reinterpret_cast<char *>(ciphertext), kIvSize + cipher_len);
321
10
  free(ciphertext);
322
10
  return result;
323
}
324
325
326
/**
327
 * The block size of AES-256-CBC happens to be the same of the MD5 digest
328
 * (128 bits).  Use the HMAC of a UUID to make it random and unpredictable.
329
 */
330
100010
shash::Md5 CipherAes256Cbc::GenerateIv(const Key &key) {
331
  // The UUID is random but not necessarily cryptographically random.  That
332
  // saves the entropy pool.
333
100010
  UniquePtr<cvmfs::Uuid> uuid(cvmfs::Uuid::Create(""));
334
100010
  assert(uuid.IsValid());
335
336
  // Now make it unpredictable, using an HMAC with the encryption key.
337
100010
  shash::Any hmac(shash::kMd5);
338
  shash::Hmac(string(reinterpret_cast<const char *>(key.data()), key.size()),
339
100010
              uuid->data(), uuid->size(), &hmac);
340
100010
  return hmac.CastToMd5();
341
}
342
343
344
//------------------------------------------------------------------------------
345
346
347
3
string CipherNone::DoDecrypt(const string &ciphertext, const Key &key) {
348
3
  return ciphertext;
349
}
350
351
352
2
string CipherNone::DoEncrypt(const string &plaintext, const Key &key) {
353
2
  return plaintext;
354
}
355
356
}  // namespace cipher