LCOV - code coverage report
Current view: top level - src/plugins/crypto - gcrypt_operations.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 145 208 69.7 %
Date: 2022-05-21 16:19:22 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          15 : static int getKeyIvForEncryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, Key * cKey, Key * cIv)
      43             : {
      44          15 :         gcry_error_t gcry_err;
      45          15 :         kdb_octet_t salt[ELEKTRA_CRYPTO_DEFAULT_SALT_LEN];
      46          15 :         kdb_octet_t keyBuffer[KEY_BUFFER_SIZE];
      47          15 :         char * saltHexString = NULL;
      48             : 
      49          15 :         ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL");
      50             : 
      51             :         // generate the salt
      52          15 :         gcry_create_nonce (salt, sizeof (salt));
      53          15 :         const int encodingResult = ELEKTRA_PLUGIN_FUNCTION (base64Encode) (errorKey, salt, sizeof (salt), &saltHexString);
      54          15 :         if (encodingResult < 0)
      55             :         {
      56             :                 // error in libinvoke - errorKey has been set by base64Encode
      57             :                 return -1;
      58             :         }
      59          15 :         if (!saltHexString)
      60             :         {
      61           0 :                 ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey);
      62           0 :                 return -1;
      63             :         }
      64          15 :         keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, saltHexString);
      65          15 :         elektraFree (saltHexString);
      66             : 
      67             :         // read iteration count
      68          15 :         const kdb_unsigned_long_t iterations = ELEKTRA_PLUGIN_FUNCTION (getIterationCount) (errorKey, config);
      69             : 
      70             :         // generate/derive the cryptographic key and the IV
      71          15 :         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          15 :         keySetBinary (cKey, keyBuffer, ELEKTRA_CRYPTO_GCRY_KEYSIZE);
      80          15 :         keySetBinary (cIv, keyBuffer + ELEKTRA_CRYPTO_GCRY_KEYSIZE, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
      81          15 :         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          16 : static int getKeyIvForDecryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, Key * cKey, Key * cIv)
      96             : {
      97          16 :         gcry_error_t gcry_err;
      98          16 :         kdb_octet_t keyBuffer[KEY_BUFFER_SIZE];
      99          16 :         kdb_octet_t * saltBuffer = NULL;
     100          16 :         kdb_unsigned_long_t saltBufferLen = 0;
     101             : 
     102          16 :         ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL");
     103             : 
     104             :         // get the salt
     105          16 :         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          16 :         const kdb_unsigned_long_t iterations = ELEKTRA_PLUGIN_FUNCTION (getIterationCount) (errorKey, config);
     112             : 
     113             :         // derive the cryptographic key and the IV
     114          16 :         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          16 :         keySetBinary (cKey, keyBuffer, ELEKTRA_CRYPTO_GCRY_KEYSIZE);
     123          16 :         keySetBinary (cIv, keyBuffer + ELEKTRA_CRYPTO_GCRY_KEYSIZE, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
     124          16 :         return 1;
     125             : }
     126             : 
     127          31 : void elektraCryptoGcryHandleDestroy (elektraCryptoHandle * handle)
     128             : {
     129          31 :         if (handle != NULL)
     130             :         {
     131          31 :                 gcry_cipher_close (*handle);
     132          31 :                 elektraFree (handle);
     133             :         }
     134          31 : }
     135             : 
     136          32 : int elektraCryptoGcryInit (Key * errorKey)
     137             : {
     138             :         // check if gcrypt has already been initialized (possibly by the application)
     139          32 :         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          32 :         gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
     147             : 
     148             :         // initialize the rest of the gcrypt library
     149          32 :         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          32 :         gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
     155          32 :         gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
     156          32 :         return 1;
     157             : }
     158             : 
     159          31 : int elektraCryptoGcryHandleCreate (elektraCryptoHandle ** handle, KeySet * config, Key * errorKey, Key * masterKey, Key * k,
     160             :                                    const enum ElektraCryptoOperation op)
     161             : {
     162          31 :         gcry_error_t gcry_err;
     163          31 :         unsigned char keyBuffer[64], ivBuffer[64];
     164          31 :         size_t keyLength, ivLength;
     165             : 
     166          31 :         (*handle) = NULL;
     167             : 
     168             :         // retrieve/derive the cryptographic material
     169          31 :         Key * key = keyNew ("/", KEY_END);
     170          31 :         Key * iv = keyNew ("/", KEY_END);
     171          31 :         switch (op)
     172             :         {
     173          15 :         case ELEKTRA_CRYPTO_ENCRYPT:
     174          15 :                 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          16 :         case ELEKTRA_CRYPTO_DECRYPT:
     183          16 :                 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           0 :         default: // not supported
     192           0 :                 keyDel (key);
     193           0 :                 keyDel (iv);
     194           0 :                 return -1;
     195             :         }
     196             : 
     197          31 :         keyLength = keyGetBinary (key, keyBuffer, sizeof (keyBuffer));
     198          31 :         ivLength = keyGetBinary (iv, ivBuffer, sizeof (ivBuffer));
     199             : 
     200             :         // create the handle
     201          31 :         (*handle) = elektraMalloc (sizeof (elektraCryptoHandle));
     202          31 :         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);
     209           0 :                 return -1;
     210             :         }
     211             : 
     212          31 :         if ((gcry_err = gcry_cipher_open (*handle, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0)) != 0)
     213             :         {
     214             :                 goto error;
     215             :         }
     216             : 
     217          31 :         if ((gcry_err = gcry_cipher_setkey (**handle, keyBuffer, keyLength)) != 0)
     218             :         {
     219             :                 goto error;
     220             :         }
     221             : 
     222          31 :         if ((gcry_err = gcry_cipher_setiv (**handle, ivBuffer, ivLength)) != 0)
     223             :         {
     224             :                 goto error;
     225             :         }
     226             : 
     227          31 :         memset (keyBuffer, 0, sizeof (keyBuffer));
     228          31 :         memset (ivBuffer, 0, sizeof (ivBuffer));
     229          31 :         keyDel (key);
     230          31 :         keyDel (iv);
     231          31 :         return 1;
     232             : 
     233           0 : error:
     234           0 :         memset (keyBuffer, 0, sizeof (keyBuffer));
     235           0 :         memset (ivBuffer, 0, sizeof (ivBuffer));
     236           0 :         ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Failed to setup libgcrypt. Reason: %s", gcry_strerror (gcry_err));
     237           0 :         gcry_cipher_close (**handle);
     238           0 :         elektraFree (*handle);
     239           0 :         (*handle) = NULL;
     240           0 :         keyDel (key);
     241           0 :         keyDel (iv);
     242           0 :         return -1;
     243             : }
     244             : 
     245          15 : int elektraCryptoGcryEncrypt (elektraCryptoHandle * handle, Key * k, Key * errorKey)
     246             : {
     247          15 :         size_t outputLen;
     248          15 :         gcry_error_t gcry_err;
     249             : 
     250             :         // prepare the salt for payload output
     251          15 :         kdb_unsigned_long_t saltLen = 0;
     252          15 :         kdb_octet_t * salt = NULL;
     253             : 
     254          15 :         if (ELEKTRA_PLUGIN_FUNCTION (getSaltFromMetakey) (errorKey, k, &salt, &saltLen) != 1)
     255             :         {
     256             :                 return -1; // error set by ELEKTRA_PLUGIN_FUNCTION(getSaltFromMetakey)()
     257             :         }
     258             : 
     259             :         // remove salt as metakey because it will be encoded into the crypto payload
     260          15 :         keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, NULL);
     261             : 
     262             :         // prepare the crypto header data
     263          15 :         const kdb_octet_t * content = keyValue (k);
     264          15 :         const kdb_unsigned_long_t contentLen = keyGetValueSize (k);
     265          15 :         kdb_octet_t flags;
     266             : 
     267          15 :         switch (keyIsString (k))
     268             :         {
     269             :         case 1: // string
     270             :                 flags = ELEKTRA_CRYPTO_FLAG_STRING;
     271             :                 break;
     272           0 :         case -1: // NULL pointer
     273           0 :                 flags = ELEKTRA_CRYPTO_FLAG_NULL;
     274           0 :                 break;
     275           4 :         default: // binary
     276           4 :                 flags = ELEKTRA_CRYPTO_FLAG_NONE;
     277           4 :                 break;
     278             :         }
     279             : 
     280             :         // prepare buffer for cipher text output
     281             :         // NOTE the header goes into the first block
     282          15 :         if (contentLen % ELEKTRA_CRYPTO_GCRY_BLOCKSIZE == 0)
     283             :         {
     284           6 :                 outputLen = (contentLen / ELEKTRA_CRYPTO_GCRY_BLOCKSIZE) + 1;
     285             :         }
     286             :         else
     287             :         {
     288           9 :                 outputLen = (contentLen / ELEKTRA_CRYPTO_GCRY_BLOCKSIZE) + 2;
     289             :         }
     290          15 :         outputLen *= ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
     291          15 :         outputLen += ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
     292          15 :         outputLen += sizeof (kdb_unsigned_long_t) + saltLen;
     293          15 :         kdb_octet_t * output = elektraCalloc (outputLen);
     294          15 :         if (!output)
     295             :         {
     296           0 :                 ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey);
     297           0 :                 elektraFree (salt);
     298           0 :                 return -1;
     299             :         }
     300             : 
     301          15 :         kdb_octet_t * current = output;
     302             : 
     303             :         // output of the magic number
     304          15 :         memcpy (current, ELEKTRA_CRYPTO_MAGIC_NUMBER, ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN);
     305          15 :         current += ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
     306             : 
     307             :         // encode the salt into the crypto payload
     308          15 :         memcpy (current, &saltLen, sizeof (kdb_unsigned_long_t));
     309          15 :         current += sizeof (kdb_unsigned_long_t);
     310          15 :         memcpy (current, salt, saltLen);
     311          15 :         current += saltLen;
     312             : 
     313             :         // encrypt the header (1st block) using gcrypt's in-place encryption
     314          15 :         memcpy (current, &flags, sizeof (flags));
     315          15 :         memcpy (current + sizeof (flags), &contentLen, sizeof (contentLen));
     316          15 :         gcry_err = gcry_cipher_encrypt (*handle, current, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE, NULL, 0);
     317          15 :         if (gcry_err != 0)
     318             :         {
     319           0 :                 ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Encryption failed. Reason: %s", gcry_strerror (gcry_err));
     320           0 :                 memset (output, 0, outputLen);
     321           0 :                 elektraFree (output);
     322           0 :                 elektraFree (salt);
     323           0 :                 return -1;
     324             :         }
     325          15 :         current += ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
     326             : 
     327             :         // encrypt the value using gcrypt's in-place encryption
     328          30 :         const size_t dataLen =
     329          15 :                 outputLen - ELEKTRA_CRYPTO_GCRY_BLOCKSIZE - sizeof (kdb_unsigned_long_t) - saltLen - ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
     330          15 :         if (contentLen) memcpy (current, content, contentLen);
     331          15 :         gcry_err = gcry_cipher_encrypt (*handle, current, dataLen, NULL, 0);
     332          15 :         if (gcry_err != 0)
     333             :         {
     334           0 :                 ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Encryption failed. Reason: %s", gcry_strerror (gcry_err));
     335           0 :                 memset (output, 0, outputLen);
     336           0 :                 elektraFree (output);
     337           0 :                 elektraFree (salt);
     338           0 :                 return -1;
     339             :         }
     340             : 
     341             :         // write back the cipher text to the key
     342          15 :         keySetBinary (k, output, outputLen);
     343          15 :         memset (output, 0, outputLen);
     344          15 :         elektraFree (output);
     345          15 :         elektraFree (salt);
     346          15 :         return 1;
     347             : }
     348             : 
     349          16 : int elektraCryptoGcryDecrypt (elektraCryptoHandle * handle, Key * k, Key * errorKey)
     350             : {
     351          16 :         gcry_error_t gcry_err;
     352             : 
     353             :         // parse salt length from crypto payload
     354          16 :         kdb_unsigned_long_t saltLen = 0;
     355          16 :         if (ELEKTRA_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, NULL, &saltLen) != 1)
     356             :         {
     357             :                 return -1; // error set by ELEKTRA_PLUGIN_FUNCTION(getSaltFromPayload)()
     358             :         }
     359          16 :         saltLen += sizeof (kdb_unsigned_long_t);
     360             : 
     361             :         // set payload pointer
     362          16 :         const kdb_octet_t * payload = ((kdb_octet_t *) keyValue (k)) + saltLen + ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
     363          16 :         const size_t payloadLen = keyGetValueSize (k) - saltLen - ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
     364             : 
     365             :         // plausibility check
     366          16 :         if (payloadLen % ELEKTRA_CRYPTO_GCRY_BLOCKSIZE != 0)
     367             :         {
     368           0 :                 ELEKTRA_SET_VALIDATION_SYNTACTIC_ERROR (errorKey, "Value length is not a multiple of the block size");
     369           0 :                 return -1;
     370             :         }
     371             : 
     372             :         // prepare buffer for plain text output and crypto operations
     373          16 :         kdb_octet_t * output = elektraMalloc (payloadLen);
     374          16 :         if (!output)
     375             :         {
     376           0 :                 ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey);
     377           0 :                 return -1;
     378             :         }
     379             : 
     380             :         // initialize crypto header data
     381          16 :         kdb_unsigned_long_t contentLen = 0;
     382          16 :         kdb_octet_t flags = ELEKTRA_CRYPTO_FLAG_NONE;
     383             : 
     384             :         // in-place decryption
     385          16 :         memcpy (output, payload, payloadLen);
     386          16 :         gcry_err = gcry_cipher_decrypt (*handle, output, payloadLen, NULL, 0);
     387          16 :         if (gcry_err != 0)
     388             :         {
     389           0 :                 ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Decryption failed. Reason: %s", gcry_strerror (gcry_err));
     390           0 :                 memset (output, 0, payloadLen);
     391           0 :                 elektraFree (output);
     392           0 :                 return -1;
     393             :         }
     394             : 
     395             :         // restore the header data
     396          16 :         memcpy (&flags, output, sizeof (flags));
     397          16 :         memcpy (&contentLen, output + sizeof (flags), sizeof (contentLen));
     398             : 
     399          16 :         const kdb_octet_t * data = output + ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
     400          16 :         const size_t dataLen = payloadLen - ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
     401             : 
     402             :         // validate restored content length
     403          16 :         if (contentLen > dataLen)
     404             :         {
     405           0 :                 ELEKTRA_SET_VALIDATION_SYNTACTIC_ERROR (
     406             :                         errorKey,
     407             :                         "Restored content length is bigger than the available amount of decrypted data. The header is possibly corrupted");
     408           0 :                 memset (output, 0, payloadLen);
     409           0 :                 elektraFree (output);
     410           0 :                 return -1;
     411             :         }
     412             : 
     413             :         // restore the key to its original status
     414          16 :         if ((flags & ELEKTRA_CRYPTO_FLAG_STRING) == ELEKTRA_CRYPTO_FLAG_STRING && contentLen > 0)
     415             :         {
     416          12 :                 keySetString (k, (const char *) data);
     417             :         }
     418           4 :         else if ((flags & ELEKTRA_CRYPTO_FLAG_NULL) == ELEKTRA_CRYPTO_FLAG_NULL || contentLen == 0)
     419             :         {
     420           2 :                 keySetBinary (k, NULL, 0);
     421             :         }
     422             :         else
     423             :         {
     424           2 :                 keySetBinary (k, data, contentLen);
     425             :         }
     426             : 
     427          16 :         memset (output, 0, payloadLen);
     428          16 :         elektraFree (output);
     429          16 :         return 1;
     430             : }
     431             : 
     432             : /**
     433             :  * @brief create a random sequence of characters with given length.
     434             :  * @param errorKey holds an error description in case of failure.
     435             :  * @param length the number of random bytes to be generated.
     436             :  * @returns allocated buffer holding a hex-encoded random string or NULL in case of error. Must be freed by the caller.
     437             :  */
     438           3 : char * elektraCryptoGcryCreateRandomString (Key * errorKey, const kdb_unsigned_short_t length)
     439           3 : {
     440           3 :         char * encoded = NULL;
     441           3 :         kdb_octet_t buffer[length];
     442           3 :         gcry_create_nonce (buffer, length);
     443           3 :         if (ELEKTRA_PLUGIN_FUNCTION (base64Encode) (errorKey, buffer, length, &encoded) < 0)
     444             :         {
     445             :                 return NULL;
     446             :         }
     447           3 :         if (!encoded)
     448             :         {
     449           0 :                 ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey);
     450             :         }
     451           3 :         return encoded;
     452             : }

Generated by: LCOV version 1.13