LCOV - code coverage report
Current view: top level - src/plugins/crypto - botan_operations.cpp (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 3 120 2.5 %
Date: 2019-09-12 12:28:41 Functions: 1 6 16.7 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief cryptographic interface using the Botan library
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include <botan/auto_rng.h>
      11             : #include <botan/filters.h>
      12             : #include <botan/hmac.h>
      13             : #include <botan/init.h>
      14             : #include <botan/lookup.h>
      15             : #include <botan/pbkdf2.h>
      16             : #include <botan/pipe.h>
      17             : #include <botan/sha2_32.h>
      18             : #include <botan/symkey.h>
      19             : #include <kdbplugin.h>
      20             : #include <memory>
      21             : 
      22             : using namespace ckdb;
      23             : using namespace Botan;
      24             : using std::unique_ptr;
      25             : 
      26             : extern "C" {
      27             : 
      28             : #include "botan_operations.h"
      29             : #include "crypto.h"
      30             : #include "gpg.h"
      31             : #include "helper.h"
      32             : #include <kdbassert.h>
      33             : #include <kdberrors.h>
      34             : #include <string.h>
      35             : 
      36             : /**
      37             :  * @brief derive the cryptographic key and IV for a given (Elektra) Key k
      38             :  * @param config KeySet holding the plugin/backend configuration
      39             :  * @param errorKey holds an error description in case of failure
      40             :  * @param masterKey holds the decrypted master password from the plugin configuration
      41             :  * @param k the (Elektra)-Key to be encrypted
      42             :  * @param cKey holds a unique pointer to an allocated SymmetricKey.
      43             :  * @param cIv holds a unique pointer to an allocated InitializationVector.
      44             :  * @retval -1 on failure. errorKey holds the error description.
      45             :  * @retval 1 on success
      46             :  */
      47           0 : static int getKeyIvForEncryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, unique_ptr<SymmetricKey> & cKey,
      48             :                                   unique_ptr<InitializationVector> & cIv)
      49             : {
      50             :         byte salt[ELEKTRA_CRYPTO_DEFAULT_SALT_LEN];
      51           0 :         char * saltHexString = NULL;
      52           0 :         const size_t requiredKeyBytes = ELEKTRA_CRYPTO_BOTAN_KEYSIZE + ELEKTRA_CRYPTO_BOTAN_BLOCKSIZE;
      53             : 
      54           0 :         ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL");
      55             : 
      56             :         try
      57             :         {
      58             :                 // generate the salt
      59           0 :                 AutoSeeded_RNG rng;
      60           0 :                 rng.randomize (salt, sizeof (salt));
      61           0 :                 const int encodingResult = ELEKTRA_PLUGIN_FUNCTION (base64Encode) (errorKey, salt, sizeof (salt), &saltHexString);
      62           0 :                 if (encodingResult < 0)
      63             :                 {
      64             :                         // error in libinvoke - errorKey has been set by base64Encode
      65             :                         return -1;
      66             :                 }
      67           0 :                 if (!saltHexString)
      68             :                 {
      69           0 :                         ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
      70             :                         return -1;
      71             :                 }
      72           0 :                 keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, saltHexString);
      73           0 :                 elektraFree (saltHexString);
      74             : 
      75             :                 // read iteration count
      76           0 :                 const kdb_unsigned_long_t iterations = ELEKTRA_PLUGIN_FUNCTION (getIterationCount) (errorKey, config);
      77             : 
      78             :                 // generate/derive the cryptographic key and the IV
      79           0 :                 PKCS5_PBKDF2 pbkdf (new HMAC (new SHA_256));
      80             :                 OctetString derived = pbkdf.derive_key (
      81           0 :                         requiredKeyBytes, std::string (reinterpret_cast<const char *> (keyValue (masterKey)), keyGetValueSize (masterKey)),
      82           0 :                         salt, sizeof (salt), iterations);
      83             : 
      84           0 :                 cKey = unique_ptr<SymmetricKey> (new SymmetricKey (derived.begin (), ELEKTRA_CRYPTO_BOTAN_KEYSIZE));
      85           0 :                 cIv = unique_ptr<InitializationVector> (
      86           0 :                         new InitializationVector (derived.begin () + ELEKTRA_CRYPTO_BOTAN_KEYSIZE, ELEKTRA_CRYPTO_BOTAN_BLOCKSIZE));
      87             : 
      88           0 :                 return 1;
      89             :         }
      90           0 :         catch (std::exception const & e)
      91             :         {
      92           0 :                 ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Failed to create a cryptographic key for encryption. Reason: %s", e.what ());
      93             :                 return -1;
      94             :         }
      95             : }
      96             : 
      97             : /**
      98             :  * @brief derive the cryptographic key and IV for a given (Elektra) Key k
      99             :  * @param config KeySet holding the plugin/backend configuration
     100             :  * @param errorKey holds an error description in case of failure
     101             :  * @param masterKey holds the decrypted master password from the plugin configuration
     102             :  * @param k the (Elektra)-Key to be encrypted
     103             :  * @param cKey holds a unique pointer to an allocated SymmetricKey.
     104             :  * @param cIv holds a unique pointer to an allocated InitializationVector.
     105             :  * @retval -1 on failure. errorKey holds the error description.
     106             :  * @retval 1 on success
     107             :  */
     108           0 : static int getKeyIvForDecryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, unique_ptr<SymmetricKey> & cKey,
     109             :                                   unique_ptr<InitializationVector> & cIv)
     110             : {
     111           0 :         const size_t requiredKeyBytes = ELEKTRA_CRYPTO_BOTAN_KEYSIZE + ELEKTRA_CRYPTO_BOTAN_BLOCKSIZE;
     112             :         kdb_octet_t * saltBuffer;
     113           0 :         kdb_unsigned_long_t saltBufferLen = 0;
     114             : 
     115           0 :         ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL");
     116             : 
     117             :         // get the salt
     118           0 :         if (ELEKTRA_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, &saltBuffer, &saltBufferLen) != 1)
     119             :         {
     120             :                 return -1; // error set by ELEKTRA_PLUGIN_FUNCTION(getSaltFromPayload)()
     121             :         }
     122             : 
     123             :         // get the iteration count
     124           0 :         const kdb_unsigned_long_t iterations = ELEKTRA_PLUGIN_FUNCTION (getIterationCount) (errorKey, config);
     125             : 
     126             :         try
     127             :         {
     128             :                 // derive the cryptographic key and the IV
     129           0 :                 PKCS5_PBKDF2 pbkdf (new HMAC (new SHA_256));
     130             :                 OctetString derived = pbkdf.derive_key (
     131           0 :                         requiredKeyBytes, std::string (reinterpret_cast<const char *> (keyValue (masterKey)), keyGetValueSize (masterKey)),
     132           0 :                         saltBuffer, saltBufferLen, iterations);
     133             : 
     134           0 :                 cKey = unique_ptr<SymmetricKey> (new SymmetricKey (derived.begin (), ELEKTRA_CRYPTO_BOTAN_KEYSIZE));
     135           0 :                 cIv = unique_ptr<InitializationVector> (
     136           0 :                         new InitializationVector (derived.begin () + ELEKTRA_CRYPTO_BOTAN_KEYSIZE, ELEKTRA_CRYPTO_BOTAN_BLOCKSIZE));
     137             : 
     138           0 :                 return 1;
     139             :         }
     140           0 :         catch (std::exception const & e)
     141             :         {
     142           0 :                 ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Failed to restore the cryptographic key for decryption. Reason: %s", e.what ());
     143             :                 return -1;
     144             :         }
     145             : }
     146             : 
     147          16 : int elektraCryptoBotanInit (Key * errorKey)
     148             : {
     149             :         try
     150             :         {
     151          48 :                 LibraryInitializer::initialize ();
     152             :         }
     153           0 :         catch (std::exception const & e)
     154             :         {
     155           0 :                 ELEKTRA_SET_PLUGIN_MISBEHAVIOR_ERRORF (errorKey, "Botan initialization failed. Reason: %s", e.what ());
     156             :                 return -1; // failure
     157             :         }
     158          16 :         return 1; // success
     159             : }
     160             : 
     161           0 : int elektraCryptoBotanEncrypt (KeySet * pluginConfig, Key * k, Key * errorKey, Key * masterKey)
     162             : {
     163             :         // get cryptographic material
     164           0 :         unique_ptr<SymmetricKey> cryptoKey;
     165           0 :         unique_ptr<InitializationVector> cryptoIv;
     166           0 :         if (getKeyIvForEncryption (pluginConfig, errorKey, masterKey, k, cryptoKey, cryptoIv) != 1)
     167             :         {
     168             :                 return -1;
     169             :         }
     170             : 
     171             :         // prepare the salt for payload output
     172           0 :         kdb_unsigned_long_t saltLen = 0;
     173           0 :         kdb_octet_t * salt = NULL;
     174             : 
     175           0 :         if (ELEKTRA_PLUGIN_FUNCTION (getSaltFromMetakey) (errorKey, k, &salt, &saltLen) != 1)
     176             :         {
     177             :                 return -1; // error set by ELEKTRA_PLUGIN_FUNCTION(getSaltFromMetakey)()
     178             :         }
     179             : 
     180             :         // remove salt as metakey because it will be encoded into the crypto payload
     181           0 :         keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, NULL);
     182             : 
     183             :         try
     184             :         {
     185             :                 // setup pipe and crypto filter
     186           0 :                 Pipe encryptor (get_cipher (ELEKTRA_CRYPTO_BOTAN_ALGORITHM, *cryptoKey, *cryptoIv, ENCRYPTION));
     187             :                 kdb_octet_t flags;
     188             : 
     189           0 :                 switch (keyIsString (k))
     190             :                 {
     191             :                 case 1: // string
     192           0 :                         flags = ELEKTRA_CRYPTO_FLAG_STRING;
     193           0 :                         break;
     194             :                 case -1: // NULL pointer
     195           0 :                         flags = ELEKTRA_CRYPTO_FLAG_NULL;
     196           0 :                         break;
     197             :                 default: // binary
     198           0 :                         flags = ELEKTRA_CRYPTO_FLAG_NONE;
     199           0 :                         break;
     200             :                 }
     201             : 
     202             :                 // encryption process
     203           0 :                 encryptor.start_msg ();
     204           0 :                 encryptor.write (static_cast<const byte *> (&flags), sizeof (kdb_octet_t));
     205             : 
     206           0 :                 if (flags == ELEKTRA_CRYPTO_FLAG_STRING)
     207             :                 {
     208           0 :                         const std::string stringVal (keyString (k));
     209           0 :                         encryptor.write (stringVal);
     210             :                 }
     211           0 :                 else if (flags == ELEKTRA_CRYPTO_FLAG_NONE && keyGetValueSize (k) > 0)
     212             :                 {
     213           0 :                         encryptor.write (static_cast<const byte *> (keyValue (k)), keyGetValueSize (k));
     214             :                 }
     215           0 :                 encryptor.end_msg ();
     216             : 
     217             :                 // write the salt and the encrypted data back to the Key
     218           0 :                 const size_t msgLength = encryptor.remaining ();
     219           0 :                 if (msgLength > 0)
     220             :                 {
     221             :                         auto buffer = unique_ptr<byte[]>{
     222           0 :                                 new byte[msgLength + ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN + sizeof (kdb_unsigned_long_t) + saltLen]
     223           0 :                         };
     224           0 :                         size_t bufferIndex = 0;
     225             : 
     226           0 :                         memcpy (&buffer[bufferIndex], ELEKTRA_CRYPTO_MAGIC_NUMBER, ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN);
     227           0 :                         bufferIndex += ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
     228           0 :                         memcpy (&buffer[bufferIndex], &saltLen, sizeof (kdb_unsigned_long_t));
     229           0 :                         bufferIndex += sizeof (kdb_unsigned_long_t);
     230           0 :                         memcpy (&buffer[bufferIndex], salt, saltLen);
     231           0 :                         bufferIndex += saltLen;
     232             : 
     233           0 :                         const size_t buffered = encryptor.read (&buffer[bufferIndex], msgLength);
     234           0 :                         keySetBinary (k, &buffer[0], buffered + bufferIndex);
     235             :                 }
     236             :         }
     237           0 :         catch (std::exception const & e)
     238             :         {
     239           0 :                 ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Encryption failed. Reason: %s", e.what ());
     240           0 :                 elektraFree (salt);
     241             :                 return -1; // failure
     242             :         }
     243             : 
     244           0 :         elektraFree (salt);
     245             :         return 1; // success
     246             : }
     247             : 
     248           0 : int elektraCryptoBotanDecrypt (KeySet * pluginConfig, Key * k, Key * errorKey, Key * masterKey)
     249             : {
     250             :         // get cryptographic material
     251           0 :         unique_ptr<SymmetricKey> cryptoKey;
     252           0 :         unique_ptr<InitializationVector> cryptoIv;
     253           0 :         if (getKeyIvForDecryption (pluginConfig, errorKey, masterKey, k, cryptoKey, cryptoIv) != 1)
     254             :         {
     255             :                 return -1;
     256             :         }
     257             : 
     258             :         // parse salt length from crypto payload
     259           0 :         kdb_unsigned_long_t saltLen = 0;
     260           0 :         if (ELEKTRA_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, NULL, &saltLen) != 1)
     261             :         {
     262             :                 return -1; // error set by ELEKTRA_PLUGIN_FUNCTION(getSaltFromPayload)()
     263             :         }
     264           0 :         saltLen += sizeof (kdb_unsigned_long_t);
     265             : 
     266             :         // set payload pointer
     267           0 :         const byte * payload = reinterpret_cast<const byte *> (keyValue (k)) + saltLen + ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
     268           0 :         const size_t payloadLen = keyGetValueSize (k) - saltLen - ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
     269             : 
     270             :         try
     271             :         {
     272             :                 // setup pipe and crypto filter
     273           0 :                 Pipe decryptor (get_cipher (ELEKTRA_CRYPTO_BOTAN_ALGORITHM, *cryptoKey, *cryptoIv, DECRYPTION));
     274           0 :                 kdb_octet_t flags = ELEKTRA_CRYPTO_FLAG_NONE;
     275             : 
     276             :                 // decrypt the content
     277           0 :                 decryptor.process_msg (payload, payloadLen);
     278             : 
     279           0 :                 if (decryptor.remaining () > 0)
     280             :                 {
     281             :                         // decode the "header" flags
     282           0 :                         const size_t flagLength = decryptor.read (static_cast<byte *> (&flags), sizeof (kdb_octet_t));
     283           0 :                         if (flagLength != sizeof (kdb_octet_t))
     284             :                         {
     285           0 :                                 throw std::length_error ("Failed to restore the original data type of the value.");
     286             :                         }
     287             :                 }
     288             : 
     289           0 :                 const size_t msgLength = decryptor.remaining ();
     290           0 :                 if (msgLength > 0)
     291             :                 {
     292           0 :                         if (flags == ELEKTRA_CRYPTO_FLAG_STRING)
     293             :                         {
     294           0 :                                 const std::string stringVal = decryptor.read_all_as_string ();
     295           0 :                                 keySetString (k, stringVal.c_str ());
     296             :                         }
     297             :                         else
     298             :                         {
     299           0 :                                 auto buffer = unique_ptr<byte[]>{ new byte[msgLength] };
     300           0 :                                 const size_t buffered = decryptor.read (&buffer[0], msgLength);
     301           0 :                                 keySetBinary (k, &buffer[0], buffered);
     302             :                         }
     303             :                 }
     304             :                 else
     305             :                 {
     306           0 :                         keySetBinary (k, NULL, 0);
     307             :                 }
     308             :         }
     309           0 :         catch (std::exception const & e)
     310             :         {
     311           0 :                 ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Decryption failed. Reason: %s", e.what ());
     312             :                 return -1; // failure
     313             :         }
     314             : 
     315           0 :         return 1; // success
     316             : }
     317             : 
     318             : /**
     319             :  * @brief create a random sequence of characters with given length.
     320             :  * @param errorKey holds an error description in case of failure.
     321             :  * @param length the number of random bytes to be generated.
     322             :  * @returns allocated buffer holding a hex-encoded random string or NULL in case of error. Must be freed by the caller.
     323             :  */
     324           0 : char * elektraCryptoBotanCreateRandomString (Key * errorKey, const kdb_unsigned_short_t length)
     325             : {
     326             :         try
     327             :         {
     328           0 :                 char * hexString = NULL;
     329           0 :                 auto buffer = unique_ptr<kdb_octet_t[]>{ new kdb_octet_t[length] };
     330           0 :                 AutoSeeded_RNG rng;
     331           0 :                 rng.randomize (&buffer[0], length);
     332           0 :                 if (ELEKTRA_PLUGIN_FUNCTION (base64Encode) (errorKey, &buffer[0], length, &hexString) < 0)
     333             :                 {
     334             :                         // error in libinvoke - errorKey has been set by base64Encode
     335             :                         return 0;
     336             :                 }
     337           0 :                 return hexString;
     338             :         }
     339           0 :         catch (std::exception const & e)
     340             :         {
     341           0 :                 ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Failed to generate random string. Reason: %s", e.what ());
     342             :                 return 0;
     343             :         }
     344             : }
     345             : 
     346             : } // extern "C"

Generated by: LCOV version 1.13