Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief cryptographic interface using the gcrypt library
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include "crypto.h"
11 :
12 : #include "gcrypt_operations.h"
13 : #include "gpg.h"
14 : #include "helper.h"
15 :
16 : #include <errno.h>
17 : #include <gcrypt.h>
18 : #include <kdbassert.h>
19 : #include <kdberrors.h>
20 : #include <kdbtypes.h>
21 : #include <pthread.h>
22 : #include <stdlib.h>
23 :
24 : #define KEY_BUFFER_SIZE (ELEKTRA_CRYPTO_GCRY_KEYSIZE + ELEKTRA_CRYPTO_GCRY_BLOCKSIZE)
25 :
26 : // initialize the gcrypt threading subsystem
27 : // NOTE: old versions of libgcrypt require the functions defined in this macro!
28 : GCRY_THREAD_OPTION_PTHREAD_IMPL;
29 :
30 :
31 : /**
32 : * @brief derive the cryptographic key and IV for a given (Elektra) Key k
33 : * @param config KeySet holding the plugin/backend configuration
34 : * @param errorKey holds an error description in case of failure
35 : * @param masterKey holds the decrypted master password from the plugin configuration
36 : * @param k the (Elektra)-Key to be encrypted
37 : * @param cKey (Elektra)-Key holding the cryptographic material
38 : * @param cIv (Elektra)-Key holding the initialization vector
39 : * @retval -1 on failure. errorKey holds the error description.
40 : * @retval 1 on success
41 : */
42 6 : static int getKeyIvForEncryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, Key * cKey, Key * cIv)
43 : {
44 : gcry_error_t gcry_err;
45 : kdb_octet_t salt[ELEKTRA_CRYPTO_DEFAULT_SALT_LEN];
46 : kdb_octet_t keyBuffer[KEY_BUFFER_SIZE];
47 6 : char * saltHexString = NULL;
48 :
49 6 : ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL");
50 :
51 : // generate the salt
52 6 : gcry_create_nonce (salt, sizeof (salt));
53 6 : const int encodingResult = ELEKTRA_PLUGIN_FUNCTION (base64Encode) (errorKey, salt, sizeof (salt), &saltHexString);
54 6 : if (encodingResult < 0)
55 : {
56 : // error in libinvoke - errorKey has been set by base64Encode
57 : return -1;
58 : }
59 6 : if (!saltHexString)
60 : {
61 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
62 0 : return -1;
63 : }
64 6 : keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, saltHexString);
65 6 : elektraFree (saltHexString);
66 :
67 : // read iteration count
68 6 : const kdb_unsigned_long_t iterations = ELEKTRA_PLUGIN_FUNCTION (getIterationCount) (errorKey, config);
69 :
70 : // generate/derive the cryptographic key and the IV
71 6 : if ((gcry_err = gcry_kdf_derive (keyValue (masterKey), keyGetValueSize (masterKey), GCRY_KDF_PBKDF2, GCRY_MD_SHA512, salt,
72 : sizeof (salt), iterations, KEY_BUFFER_SIZE, keyBuffer)))
73 : {
74 0 : ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Failed to create a cryptographic key for encryption. Reason: %s",
75 : gcry_strerror (gcry_err));
76 0 : return -1;
77 : }
78 :
79 6 : keySetBinary (cKey, keyBuffer, ELEKTRA_CRYPTO_GCRY_KEYSIZE);
80 6 : keySetBinary (cIv, keyBuffer + ELEKTRA_CRYPTO_GCRY_KEYSIZE, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
81 6 : return 1;
82 : }
83 :
84 : /**
85 : * @brief derive the cryptographic key and IV for a given (Elektra) Key k
86 : * @param config KeySet holding the plugin/backend configuration
87 : * @param errorKey holds an error description in case of failure
88 : * @param masterKey holds the decrypted master password from the plugin configuration
89 : * @param k the (Elektra)-Key to be encrypted
90 : * @param cKey (Elektra)-Key holding the cryptographic material
91 : * @param cIv (Elektra)-Key holding the initialization vector
92 : * @retval -1 on failure. errorKey holds the error description.
93 : * @retval 1 on success
94 : */
95 6 : static int getKeyIvForDecryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, Key * cKey, Key * cIv)
96 : {
97 : gcry_error_t gcry_err;
98 : kdb_octet_t keyBuffer[KEY_BUFFER_SIZE];
99 6 : kdb_octet_t * saltBuffer = NULL;
100 6 : kdb_unsigned_long_t saltBufferLen = 0;
101 :
102 6 : ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL");
103 :
104 : // get the salt
105 6 : if (ELEKTRA_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, &saltBuffer, &saltBufferLen) != 1)
106 : {
107 : return -1; // error set by ELEKTRA_PLUGIN_FUNCTION(getSaltFromPayload)()
108 : }
109 :
110 : // get the iteration count
111 6 : const kdb_unsigned_long_t iterations = ELEKTRA_PLUGIN_FUNCTION (getIterationCount) (errorKey, config);
112 :
113 : // derive the cryptographic key and the IV
114 6 : if ((gcry_err = gcry_kdf_derive (keyValue (masterKey), keyGetValueSize (masterKey), GCRY_KDF_PBKDF2, GCRY_MD_SHA512, saltBuffer,
115 : saltBufferLen, iterations, KEY_BUFFER_SIZE, keyBuffer)))
116 : {
117 0 : ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Failed to restore the cryptographic key for decryption. Reason: %s",
118 : gcry_strerror (gcry_err));
119 0 : return -1;
120 : }
121 :
122 6 : keySetBinary (cKey, keyBuffer, ELEKTRA_CRYPTO_GCRY_KEYSIZE);
123 6 : keySetBinary (cIv, keyBuffer + ELEKTRA_CRYPTO_GCRY_KEYSIZE, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
124 6 : return 1;
125 : }
126 :
127 12 : void elektraCryptoGcryHandleDestroy (elektraCryptoHandle * handle)
128 : {
129 12 : if (handle != NULL)
130 : {
131 12 : gcry_cipher_close (*handle);
132 12 : elektraFree (handle);
133 : }
134 12 : }
135 :
136 17 : int elektraCryptoGcryInit (Key * errorKey)
137 : {
138 : // check if gcrypt has already been initialized (possibly by the application)
139 17 : if (gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P))
140 : {
141 : return 1;
142 : }
143 :
144 : // initialize the gcrypt threading subsystem
145 : // NOTE: this is a dummy call in newer versions of gcrypt, but old versions require it
146 17 : gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
147 :
148 : // initialize the rest of the gcrypt library
149 17 : if (!gcry_check_version (GCRYPT_VERSION))
150 : {
151 0 : ELEKTRA_SET_INSTALLATION_ERRORF (errorKey, "Libgcrypt version check failed, looking for version: %s", GCRYPT_VERSION);
152 0 : return -1;
153 : }
154 17 : gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
155 17 : gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
156 17 : return 1;
157 : }
158 :
159 12 : int elektraCryptoGcryHandleCreate (elektraCryptoHandle ** handle, KeySet * config, Key * errorKey, Key * masterKey, Key * k,
160 : const enum ElektraCryptoOperation op)
161 : {
162 : gcry_error_t gcry_err;
163 : unsigned char keyBuffer[64], ivBuffer[64];
164 : size_t keyLength, ivLength;
165 :
166 12 : (*handle) = NULL;
167 :
168 : // retrieve/derive the cryptographic material
169 12 : Key * key = keyNew (0);
170 12 : Key * iv = keyNew (0);
171 12 : switch (op)
172 : {
173 : case ELEKTRA_CRYPTO_ENCRYPT:
174 6 : if (getKeyIvForEncryption (config, errorKey, masterKey, k, key, iv) != 1)
175 : {
176 0 : keyDel (key);
177 0 : keyDel (iv);
178 0 : return -1;
179 : }
180 : break;
181 :
182 : case ELEKTRA_CRYPTO_DECRYPT:
183 6 : if (getKeyIvForDecryption (config, errorKey, masterKey, k, key, iv) != 1)
184 : {
185 0 : keyDel (key);
186 0 : keyDel (iv);
187 0 : return -1;
188 : }
189 : break;
190 :
191 : default: // not supported
192 0 : keyDel (key);
193 0 : keyDel (iv);
194 0 : return -1;
195 : }
196 :
197 12 : keyLength = keyGetBinary (key, keyBuffer, sizeof (keyBuffer));
198 12 : ivLength = keyGetBinary (iv, ivBuffer, sizeof (ivBuffer));
199 :
200 : // create the handle
201 12 : (*handle) = elektraMalloc (sizeof (elektraCryptoHandle));
202 12 : if (*handle == NULL)
203 : {
204 0 : memset (keyBuffer, 0, sizeof (keyBuffer));
205 0 : memset (ivBuffer, 0, sizeof (ivBuffer));
206 0 : keyDel (key);
207 0 : keyDel (iv);
208 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
209 0 : return -1;
210 : }
211 :
212 12 : if ((gcry_err = gcry_cipher_open (*handle, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0)) != 0)
213 : {
214 : goto error;
215 : }
216 :
217 12 : if ((gcry_err = gcry_cipher_setkey (**handle, keyBuffer, keyLength)) != 0)
218 : {
219 : goto error;
220 : }
221 :
222 12 : if ((gcry_err = gcry_cipher_setiv (**handle, ivBuffer, ivLength)) != 0)
223 : {
224 : goto error;
225 : }
226 :
227 12 : memset (keyBuffer, 0, sizeof (keyBuffer));
228 12 : memset (ivBuffer, 0, sizeof (ivBuffer));
229 12 : keyDel (key);
230 12 : keyDel (iv);
231 12 : return 1;
232 :
233 : error:
234 0 : memset (keyBuffer, 0, sizeof (keyBuffer));
235 0 : memset (ivBuffer, 0, sizeof (ivBuffer));
236 : // TODO: Correct??
237 0 : ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Failed to create handle. Reason: %s", gcry_strerror (gcry_err));
238 0 : gcry_cipher_close (**handle);
239 0 : elektraFree (*handle);
240 0 : (*handle) = NULL;
241 0 : keyDel (key);
242 0 : keyDel (iv);
243 0 : return -1;
244 : }
245 :
246 6 : int elektraCryptoGcryEncrypt (elektraCryptoHandle * handle, Key * k, Key * errorKey)
247 : {
248 : size_t outputLen;
249 : gcry_error_t gcry_err;
250 :
251 : // prepare the salt for payload output
252 6 : kdb_unsigned_long_t saltLen = 0;
253 6 : kdb_octet_t * salt = NULL;
254 :
255 6 : if (ELEKTRA_PLUGIN_FUNCTION (getSaltFromMetakey) (errorKey, k, &salt, &saltLen) != 1)
256 : {
257 : return -1; // error set by ELEKTRA_PLUGIN_FUNCTION(getSaltFromMetakey)()
258 : }
259 :
260 : // remove salt as metakey because it will be encoded into the crypto payload
261 6 : keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, NULL);
262 :
263 : // prepare the crypto header data
264 6 : const kdb_octet_t * content = keyValue (k);
265 6 : const kdb_unsigned_long_t contentLen = keyGetValueSize (k);
266 : kdb_octet_t flags;
267 :
268 6 : switch (keyIsString (k))
269 : {
270 : case 1: // string
271 : flags = ELEKTRA_CRYPTO_FLAG_STRING;
272 : break;
273 : case -1: // NULL pointer
274 0 : flags = ELEKTRA_CRYPTO_FLAG_NULL;
275 0 : break;
276 : default: // binary
277 2 : flags = ELEKTRA_CRYPTO_FLAG_NONE;
278 2 : break;
279 : }
280 :
281 : // prepare buffer for cipher text output
282 : // NOTE the header goes into the first block
283 6 : if (contentLen % ELEKTRA_CRYPTO_GCRY_BLOCKSIZE == 0)
284 : {
285 3 : outputLen = (contentLen / ELEKTRA_CRYPTO_GCRY_BLOCKSIZE) + 1;
286 : }
287 : else
288 : {
289 3 : outputLen = (contentLen / ELEKTRA_CRYPTO_GCRY_BLOCKSIZE) + 2;
290 : }
291 6 : outputLen *= ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
292 6 : outputLen += ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
293 6 : outputLen += sizeof (kdb_unsigned_long_t) + saltLen;
294 6 : kdb_octet_t * output = elektraMalloc (outputLen);
295 6 : if (!output)
296 : {
297 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
298 0 : elektraFree (salt);
299 0 : return -1;
300 : }
301 :
302 6 : kdb_octet_t * current = output;
303 :
304 : // output of the magic number
305 6 : memcpy (current, ELEKTRA_CRYPTO_MAGIC_NUMBER, ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN);
306 6 : current += ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
307 :
308 : // encode the salt into the crypto payload
309 6 : memcpy (current, &saltLen, sizeof (kdb_unsigned_long_t));
310 6 : current += sizeof (kdb_unsigned_long_t);
311 6 : memcpy (current, salt, saltLen);
312 6 : current += saltLen;
313 :
314 : // encrypt the header (1st block) using gcrypt's in-place encryption
315 6 : memcpy (current, &flags, sizeof (flags));
316 6 : memcpy (current + sizeof (flags), &contentLen, sizeof (contentLen));
317 6 : gcry_err = gcry_cipher_encrypt (*handle, current, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE, NULL, 0);
318 6 : if (gcry_err != 0)
319 : {
320 : // TODO: Correct??
321 0 : ELEKTRA_SET_INSTALLATION_ERRORF (errorKey, "Encryption failed. Reason: %s", gcry_strerror (gcry_err));
322 0 : memset (output, 0, outputLen);
323 0 : elektraFree (output);
324 0 : elektraFree (salt);
325 0 : return -1;
326 : }
327 6 : current += ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
328 :
329 : // encrypt the value using gcrypt's in-place encryption
330 6 : const size_t dataLen =
331 6 : outputLen - ELEKTRA_CRYPTO_GCRY_BLOCKSIZE - sizeof (kdb_unsigned_long_t) - saltLen - ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
332 6 : if (contentLen) memcpy (current, content, contentLen);
333 6 : gcry_err = gcry_cipher_encrypt (*handle, current, dataLen, NULL, 0);
334 6 : if (gcry_err != 0)
335 : {
336 : // TODO: Correct??
337 0 : ELEKTRA_SET_INSTALLATION_ERRORF (errorKey, "Encryption failed. Reason: %s", gcry_strerror (gcry_err));
338 0 : memset (output, 0, outputLen);
339 0 : elektraFree (output);
340 0 : elektraFree (salt);
341 0 : return -1;
342 : }
343 :
344 : // write back the cipher text to the key
345 6 : keySetBinary (k, output, outputLen);
346 6 : memset (output, 0, outputLen);
347 6 : elektraFree (output);
348 6 : elektraFree (salt);
349 6 : return 1;
350 : }
351 :
352 6 : int elektraCryptoGcryDecrypt (elektraCryptoHandle * handle, Key * k, Key * errorKey)
353 : {
354 : gcry_error_t gcry_err;
355 :
356 : // parse salt length from crypto payload
357 6 : kdb_unsigned_long_t saltLen = 0;
358 6 : if (ELEKTRA_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, NULL, &saltLen) != 1)
359 : {
360 : return -1; // error set by ELEKTRA_PLUGIN_FUNCTION(getSaltFromPayload)()
361 : }
362 6 : saltLen += sizeof (kdb_unsigned_long_t);
363 :
364 : // set payload pointer
365 6 : const kdb_octet_t * payload = ((kdb_octet_t *) keyValue (k)) + saltLen + ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
366 6 : const size_t payloadLen = keyGetValueSize (k) - saltLen - ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
367 :
368 : // plausibility check
369 6 : if (payloadLen % ELEKTRA_CRYPTO_GCRY_BLOCKSIZE != 0)
370 : {
371 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERROR (errorKey, "Value length is not a multiple of the block size");
372 0 : return -1;
373 : }
374 :
375 : // prepare buffer for plain text output and crypto operations
376 6 : kdb_octet_t * output = elektraMalloc (payloadLen);
377 6 : if (!output)
378 : {
379 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
380 0 : return -1;
381 : }
382 :
383 : // initialize crypto header data
384 6 : kdb_unsigned_long_t contentLen = 0;
385 6 : kdb_octet_t flags = ELEKTRA_CRYPTO_FLAG_NONE;
386 :
387 : // in-place decryption
388 6 : memcpy (output, payload, payloadLen);
389 6 : gcry_err = gcry_cipher_decrypt (*handle, output, payloadLen, NULL, 0);
390 6 : if (gcry_err != 0)
391 : {
392 0 : ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Decryption failed. Reason: %s", gcry_strerror (gcry_err));
393 0 : memset (output, 0, payloadLen);
394 0 : elektraFree (output);
395 0 : return -1;
396 : }
397 :
398 : // restore the header data
399 6 : memcpy (&flags, output, sizeof (flags));
400 6 : memcpy (&contentLen, output + sizeof (flags), sizeof (contentLen));
401 :
402 6 : const kdb_octet_t * data = output + ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
403 6 : const size_t dataLen = payloadLen - ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
404 :
405 : // validate restored content length
406 6 : if (contentLen > dataLen)
407 : {
408 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERROR (
409 : errorKey,
410 : "Restored content length is bigger than the available amount of decrypted data. The header is possibly corrupted");
411 0 : memset (output, 0, payloadLen);
412 0 : elektraFree (output);
413 0 : return -1;
414 : }
415 :
416 : // restore the key to its original status
417 6 : if ((flags & ELEKTRA_CRYPTO_FLAG_STRING) == ELEKTRA_CRYPTO_FLAG_STRING && contentLen > 0)
418 : {
419 4 : keySetString (k, (const char *) data);
420 : }
421 2 : else if ((flags & ELEKTRA_CRYPTO_FLAG_NULL) == ELEKTRA_CRYPTO_FLAG_NULL || contentLen == 0)
422 : {
423 1 : keySetBinary (k, NULL, 0);
424 : }
425 : else
426 : {
427 1 : keySetBinary (k, data, contentLen);
428 : }
429 :
430 6 : memset (output, 0, payloadLen);
431 6 : elektraFree (output);
432 6 : return 1;
433 : }
434 :
435 : /**
436 : * @brief create a random sequence of characters with given length.
437 : * @param errorKey holds an error description in case of failure.
438 : * @param length the number of random bytes to be generated.
439 : * @returns allocated buffer holding a hex-encoded random string or NULL in case of error. Must be freed by the caller.
440 : */
441 1 : char * elektraCryptoGcryCreateRandomString (Key * errorKey, const kdb_unsigned_short_t length)
442 1 : {
443 1 : char * encoded = NULL;
444 1 : kdb_octet_t buffer[length];
445 1 : gcry_create_nonce (buffer, length);
446 1 : if (ELEKTRA_PLUGIN_FUNCTION (base64Encode) (errorKey, buffer, length, &encoded) < 0)
447 : {
448 : return NULL;
449 : }
450 1 : if (!encoded)
451 : {
452 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
453 : }
454 1 : return encoded;
455 : }
|