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 |