CernVM-FS  2.9.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
encrypt.cc
Go to the documentation of this file.
1 
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/exception.h"
23 #include "util/pointer.h"
24 #include "util/string.h"
25 #include "util_concurrency.h"
26 #include "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_ =
119  reinterpret_cast<pthread_mutex_t *>(smalloc(sizeof(pthread_mutex_t)));
120  int retval = pthread_mutex_init(lock_, NULL);
121  assert(retval == 0);
122 }
123 
124 
125 MemoryKeyDatabase::~MemoryKeyDatabase() {
126  pthread_mutex_destroy(lock_);
127  free(lock_);
128 }
129 
130 
131 bool MemoryKeyDatabase::StoreNew(const Key *key, string *id) {
132  MutexLockGuard mutex_guard(lock_);
133  // TODO(jblomer): is this good enough for random keys? Salting? KDF2?
135  HashMem(key->data(), key->size(), &hash);
136  *id = "H" + hash.ToString();
137  map<string, const Key *>::const_iterator i = database_.find(*id);
138  if (i != database_.end())
139  return false;
140 
141  database_[*id] = key;
142  return true;
143 }
144 
145 
146 const Key *MemoryKeyDatabase::Find(const string &id) {
147  MutexLockGuard mutex_guard(lock_);
148  map<string, const Key *>::const_iterator i = database_.find(id);
149  if (i != database_.end())
150  return i->second;
151  return NULL;
152 }
153 
154 
155 //------------------------------------------------------------------------------
156 
157 
159  switch (a) {
160  case kAes256Cbc:
161  return new CipherAes256Cbc();
162  case kNone:
163  return new CipherNone();
164  default:
165  PANIC(NULL);
166  }
167  // Never here
168 }
169 
170 
171 bool Cipher::Encrypt(
172  const string &plaintext,
173  const Key &key,
174  string *ciphertext)
175 {
176  ciphertext->clear();
177  if (key.size() != key_size())
178  return false;
179 
180  unsigned char envelope = 0 & 0x0F;
181  envelope |= (algorithm() << 4) & 0xF0;
182  ciphertext->push_back(envelope);
183 
184  *ciphertext += DoEncrypt(plaintext, key);
185  return true;
186 }
187 
188 
189 bool Cipher::Decrypt(
190  const string &ciphertext,
191  const Key &key,
192  string *plaintext)
193 {
194  plaintext->clear();
195  if (ciphertext.size() < 1)
196  return false;
197  unsigned char envelope = ciphertext[0];
198  unsigned char version = envelope & 0x0F;
199  if (version != 0)
200  return false;
201  unsigned char algorithm = (envelope & 0xF0) >> 4;
202  if (algorithm > kNone)
203  return false;
204 
205  UniquePtr<Cipher> cipher(Create(static_cast<Algorithms>(algorithm)));
206  if (key.size() != cipher->key_size())
207  return false;
208  *plaintext += cipher->DoDecrypt(ciphertext.substr(1), key);
209  return true;
210 }
211 
212 
213 //------------------------------------------------------------------------------
214 
215 
216 string CipherAes256Cbc::DoDecrypt(const string &ciphertext, const Key &key) {
217  assert(key.size() == kKeySize);
218  int retval;
219  if (ciphertext.size() < kIvSize)
220  return "";
221 
222  const unsigned char *iv = reinterpret_cast<const unsigned char *>(
223  ciphertext.data());
224 
225  // See OpenSSL documentation for the size
226  unsigned char *plaintext = reinterpret_cast<unsigned char *>(
227  smalloc(kBlockSize + ciphertext.size() - kIvSize));
228  int plaintext_len;
229  int tail_len;
230 #ifdef OPENSSL_API_INTERFACE_V11
231  EVP_CIPHER_CTX *ctx_ptr = EVP_CIPHER_CTX_new();
232 #else
233  EVP_CIPHER_CTX ctx;
234  EVP_CIPHER_CTX_init(&ctx);
235  EVP_CIPHER_CTX *ctx_ptr = &ctx;
236 #endif
237  retval = EVP_DecryptInit_ex(ctx_ptr, EVP_aes_256_cbc(), NULL, key.data(), iv);
238  assert(retval == 1);
239  retval = EVP_DecryptUpdate(ctx_ptr,
240  plaintext, &plaintext_len,
241  reinterpret_cast<const unsigned char *>(
242  ciphertext.data() + kIvSize),
243  ciphertext.length() - kIvSize);
244  if (retval != 1) {
245  free(plaintext);
246 #ifdef OPENSSL_API_INTERFACE_V11
247  EVP_CIPHER_CTX_free(ctx_ptr);
248 #else
249  retval = EVP_CIPHER_CTX_cleanup(&ctx);
250  assert(retval == 1);
251 #endif
252  return "";
253  }
254  retval = EVP_DecryptFinal_ex(ctx_ptr, plaintext + plaintext_len, &tail_len);
255 #ifdef OPENSSL_API_INTERFACE_V11
256  EVP_CIPHER_CTX_free(ctx_ptr);
257 #else
258  int retval_2 = EVP_CIPHER_CTX_cleanup(&ctx);
259  assert(retval_2 == 1);
260 #endif
261  if (retval != 1) {
262  free(plaintext);
263  return "";
264  }
265 
266  plaintext_len += tail_len;
267  if (plaintext_len == 0) {
268  free(plaintext);
269  return "";
270  }
271  string result(reinterpret_cast<char *>(plaintext), plaintext_len);
272  free(plaintext);
273  return result;
274 }
275 
276 
277 string CipherAes256Cbc::DoEncrypt(const string &plaintext, const Key &key) {
278  assert(key.size() == kKeySize);
279  int retval;
280 
281  shash::Md5 md5(GenerateIv(key));
282  // iv size happens to be md5 digest size
283  unsigned char *iv = md5.digest;
284 
285  // See OpenSSL documentation as for the size. Additionally, we prepend the
286  // initialization vector.
287  unsigned char *ciphertext = reinterpret_cast<unsigned char *>(
288  smalloc(kIvSize + 2 * kBlockSize + plaintext.size()));
289  memcpy(ciphertext, iv, kIvSize);
290  int cipher_len = 0;
291  int tail_len = 0;
292 #ifdef OPENSSL_API_INTERFACE_V11
293  EVP_CIPHER_CTX *ctx_ptr = EVP_CIPHER_CTX_new();
294 #else
295  EVP_CIPHER_CTX ctx;
296  EVP_CIPHER_CTX_init(&ctx);
297  EVP_CIPHER_CTX *ctx_ptr = &ctx;
298 #endif
299  retval = EVP_EncryptInit_ex(ctx_ptr, EVP_aes_256_cbc(), NULL, key.data(), iv);
300  assert(retval == 1);
301  // Older versions of OpenSSL don't allow empty input buffers
302  if (!plaintext.empty()) {
303  retval = EVP_EncryptUpdate(ctx_ptr,
304  ciphertext + kIvSize, &cipher_len,
305  reinterpret_cast<const unsigned char *>(plaintext.data()),
306  plaintext.length());
307  assert(retval == 1);
308  }
309  retval = EVP_EncryptFinal_ex(ctx_ptr, ciphertext + kIvSize + cipher_len,
310  &tail_len);
311  assert(retval == 1);
312 #ifdef OPENSSL_API_INTERFACE_V11
313  EVP_CIPHER_CTX_free(ctx_ptr);
314 #else
315  retval = EVP_CIPHER_CTX_cleanup(&ctx);
316  assert(retval == 1);
317 #endif
318 
319  cipher_len += tail_len;
320  assert(cipher_len > 0);
321  string result(reinterpret_cast<char *>(ciphertext), kIvSize + cipher_len);
322  free(ciphertext);
323  return result;
324 }
325 
326 
331 shash::Md5 CipherAes256Cbc::GenerateIv(const Key &key) {
332  // The UUID is random but not necessarily cryptographically random. That
333  // saves the entropy pool.
335  assert(uuid.IsValid());
336 
337  // Now make it unpredictable, using an HMAC with the encryption key.
338  shash::Any hmac(shash::kMd5);
339  shash::Hmac(string(reinterpret_cast<const char *>(key.data()), key.size()),
340  uuid->data(), uuid->size(), &hmac);
341  return hmac.CastToMd5();
342 }
343 
344 
345 //------------------------------------------------------------------------------
346 
347 
348 string CipherNone::DoDecrypt(const string &ciphertext, const Key &key) {
349  return ciphertext;
350 }
351 
352 
353 string CipherNone::DoEncrypt(const string &plaintext, const Key &key) {
354  return plaintext;
355 }
356 
357 } // namespace cipher
struct stat64 platform_stat64
struct cvmcache_context * ctx
static Publisher * Create(const SettingsPublisher &settings)
#define PANIC(...)
Definition: exception.h:26
std::string ToString(const bool with_suffix=false) const
Definition: hash.h:245
Algorithms
Definition: encrypt.h:27
unsigned size() const
Definition: encrypt.h:47
void Hmac(const string &key, const unsigned char *buffer, const unsigned buffer_size, Any *any_digest)
Definition: hash.cc:274
assert((mem||(size==0))&&"Out Of Memory")
unsigned char digest[digest_size_]
Definition: hash.h:122
unsigned size() const
Definition: uuid.h:31
char algorithm
unsigned size_
Definition: encrypt.h:54
unsigned char * data_
Definition: encrypt.h:53
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:255
string Base64(const string &data)
Definition: string.cc:474
const unsigned char * data() const
Definition: encrypt.h:48
const unsigned char * data() const
Definition: uuid.h:28
int platform_fstat(int filedes, platform_stat64 *buf)
static void size_t size
Definition: smalloc.h:47