LCOV - code coverage report
Current view: top level - src/plugins/crypto - gcrypt_operations.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 130 190 68.4 %
Date: 2019-09-12 12:28:41 Functions: 8 8 100.0 %

          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             : }

Generated by: LCOV version 1.13