/** * @brief derive the cryptographic key and IV for a given (Elektra) Key k * @param config KeySet holding the plugin/backend configuration * @param errorKey holds an error description in case of failure * @param masterKey holds the decrypted master password from the plugin configuration * @param k the (Elektra)-Key to be encrypted * @param cKey (Elektra)-Key holding the cryptographic material * @param cIv (Elektra)-Key holding the initialization vector * @retval -1 on failure. errorKey holds the error description. * @retval 1 on success */ static int getKeyIvForDecryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, Key * cKey, Key * cIv) { kdb_octet_t keyBuffer[KEY_BUFFER_SIZE]; kdb_octet_t * saltBuffer = NULL; kdb_unsigned_long_t saltBufferLen = 0; ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL"); // get the salt if (CRYPTO_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, &saltBuffer, &saltBufferLen) != 1) { return -1; // error set by CRYPTO_PLUGIN_FUNCTION(getSaltFromPayload)() } // get the iteration count const kdb_unsigned_long_t iterations = CRYPTO_PLUGIN_FUNCTION (getIterationCount) (errorKey, config); // derive the cryptographic key and the IV pthread_mutex_lock (&mutex_ssl); if (!PKCS5_PBKDF2_HMAC_SHA1 (keyValue (masterKey), keyGetValueSize (masterKey), saltBuffer, saltBufferLen, iterations, KEY_BUFFER_SIZE, keyBuffer)) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "Failed to restore the cryptographic key for decryption. Libcrypto returned the error code: %lu", ERR_get_error ()); pthread_mutex_unlock (&mutex_ssl); return -1; } pthread_mutex_unlock (&mutex_ssl); keySetBinary (cKey, keyBuffer, ELEKTRA_CRYPTO_SSL_KEYSIZE); keySetBinary (cIv, keyBuffer + ELEKTRA_CRYPTO_SSL_KEYSIZE, ELEKTRA_CRYPTO_SSL_BLOCKSIZE); return 1; }
/** * @brief derive the cryptographic key and IV for a given (Elektra) Key k * @param config KeySet holding the plugin/backend configuration * @param errorKey holds an error description in case of failure * @param masterKey holds the decrypted master password from the plugin configuration * @param k the (Elektra)-Key to be encrypted * @param cKey (Elektra)-Key holding the cryptographic material * @param cIv (Elektra)-Key holding the initialization vector * @retval -1 on failure. errorKey holds the error description. * @retval 1 on success */ static int getKeyIvForDecryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, Key * cKey, Key * cIv) { gcry_error_t gcry_err; kdb_octet_t keyBuffer[KEY_BUFFER_SIZE]; kdb_octet_t * saltBuffer = NULL; kdb_unsigned_long_t saltBufferLen = 0; ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL"); // get the salt if (CRYPTO_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, &saltBuffer, &saltBufferLen) != 1) { return -1; // error set by CRYPTO_PLUGIN_FUNCTION(getSaltFromPayload)() } // get the iteration count const kdb_unsigned_long_t iterations = CRYPTO_PLUGIN_FUNCTION (getIterationCount) (errorKey, config); // derive the cryptographic key and the IV if ((gcry_err = gcry_kdf_derive (keyValue (masterKey), keyGetValueSize (masterKey), GCRY_KDF_PBKDF2, GCRY_MD_SHA512, saltBuffer, saltBufferLen, iterations, KEY_BUFFER_SIZE, keyBuffer))) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "Failed to restore the cryptographic key for decryption because: %s", gcry_strerror (gcry_err)); return -1; } keySetBinary (cKey, keyBuffer, ELEKTRA_CRYPTO_GCRY_KEYSIZE); keySetBinary (cIv, keyBuffer + ELEKTRA_CRYPTO_GCRY_KEYSIZE, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE); return 1; }
/** * @brief parse the hex-encoded salt from the metakey. * @param errorKey holds an error description in case of failure. * @param k holds the salt as metakey * @param salt is set to an allocated buffer containing the salt. Must be freed by the caller. * @param saltLen is set to the length of the salt. Ignored if NULL is provided. * @retval 1 on success * @retval -1 on error. errorKey holds a description. */ int CRYPTO_PLUGIN_FUNCTION (getSaltFromMetakey) (Key * errorKey, Key * k, kdb_octet_t ** salt, kdb_unsigned_long_t * saltLen) { size_t saltLenInternal = 0; const Key * meta = keyGetMeta (k, ELEKTRA_CRYPTO_META_SALT); if (!meta) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "missing salt as metakey %s in key %s", ELEKTRA_CRYPTO_META_SALT, keyName (k)); return -1; } int result = CRYPTO_PLUGIN_FUNCTION (base64Decode) (errorKey, keyString (meta), salt, &saltLenInternal); if (result == -1) { ELEKTRA_SET_ERROR (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "Salt was not stored Base64 encoded."); return -1; } else if (result == -2) { ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed"); return -1; } else if (result < -2) { // errorKey has been set by base64Decode (...) return -1; } *saltLen = saltLenInternal; return 1; }
/** * @brief derive the cryptographic key and IV for a given (Elektra) Key k * @param config KeySet holding the plugin/backend configuration * @param errorKey holds an error description in case of failure * @param masterKey holds the decrypted master password from the plugin configuration * @param k the (Elektra)-Key to be encrypted * @param cKey (Elektra)-Key holding the cryptographic material * @param cIv (Elektra)-Key holding the initialization vector * @retval -1 on failure. errorKey holds the error description. * @retval 1 on success */ static int getKeyIvForEncryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, Key * cKey, Key * cIv) { gcry_error_t gcry_err; kdb_octet_t salt[ELEKTRA_CRYPTO_DEFAULT_SALT_LEN]; kdb_octet_t keyBuffer[KEY_BUFFER_SIZE]; char * saltHexString = NULL; ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL"); // generate the salt gcry_create_nonce (salt, sizeof (salt)); const int encodingResult = CRYPTO_PLUGIN_FUNCTION (base64Encode) (errorKey, salt, sizeof (salt), &saltHexString); if (encodingResult < 0) { // error in libinvoke - errorKey has been set by base64Encode return -1; } if (!saltHexString) { ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed"); return -1; } keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, saltHexString); elektraFree (saltHexString); // read iteration count const kdb_unsigned_long_t iterations = CRYPTO_PLUGIN_FUNCTION (getIterationCount) (errorKey, config); // generate/derive the cryptographic key and the IV if ((gcry_err = gcry_kdf_derive (keyValue (masterKey), keyGetValueSize (masterKey), GCRY_KDF_PBKDF2, GCRY_MD_SHA512, salt, sizeof (salt), iterations, KEY_BUFFER_SIZE, keyBuffer))) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "Failed to create a cryptographic key for encryption because: %s", gcry_strerror (gcry_err)); return -1; } keySetBinary (cKey, keyBuffer, ELEKTRA_CRYPTO_GCRY_KEYSIZE); keySetBinary (cIv, keyBuffer + ELEKTRA_CRYPTO_GCRY_KEYSIZE, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE); return 1; }
/** * @brief create a random sequence of characters with given length. * @param errorKey holds an error description in case of failure. * @param length the number of random bytes to be generated. * @returns allocated buffer holding a hex-encoded random string or NULL in case of error. Must be freed by the caller. */ char * elektraCryptoGcryCreateRandomString (Key * errorKey, const kdb_unsigned_short_t length) { char * encoded = NULL; kdb_octet_t buffer[length]; gcry_create_nonce (buffer, length); if (CRYPTO_PLUGIN_FUNCTION (base64Encode) (errorKey, buffer, length, &encoded) < 0) { return NULL; } if (!encoded) { ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed"); } return encoded; }
/** * @brief derive the cryptographic key and IV for a given (Elektra) Key k * @param config KeySet holding the plugin/backend configuration * @param errorKey holds an error description in case of failure * @param masterKey holds the decrypted master password from the plugin configuration * @param k the (Elektra)-Key to be encrypted * @param cKey (Elektra)-Key holding the cryptographic material * @param cIv (Elektra)-Key holding the initialization vector * @retval -1 on failure. errorKey holds the error description. * @retval 1 on success */ static int getKeyIvForEncryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, Key * cKey, Key * cIv) { kdb_octet_t salt[ELEKTRA_CRYPTO_DEFAULT_SALT_LEN] = { 0 }; kdb_octet_t keyBuffer[KEY_BUFFER_SIZE] = { 0 }; char * saltHexString = NULL; ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL"); // generate the salt pthread_mutex_lock (&mutex_ssl); if (!RAND_bytes (salt, ELEKTRA_CRYPTO_DEFAULT_SALT_LEN - 1)) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "failed to generate random salt with error code %lu", ERR_get_error ()); pthread_mutex_unlock (&mutex_ssl); return -1; } pthread_mutex_unlock (&mutex_ssl); saltHexString = ELEKTRA_PLUGIN_FUNCTION (ELEKTRA_PLUGIN_NAME_C, base64Encode) (salt, sizeof (salt)); if (!saltHexString) { ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed"); return -1; } keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, saltHexString); elektraFree (saltHexString); // read iteration count const kdb_unsigned_long_t iterations = CRYPTO_PLUGIN_FUNCTION (getIterationCount) (errorKey, config); // generate/derive the cryptographic key and the IV pthread_mutex_lock (&mutex_ssl); if (!PKCS5_PBKDF2_HMAC_SHA1 (keyValue (masterKey), keyGetValueSize (masterKey), salt, sizeof (salt), iterations, KEY_BUFFER_SIZE, keyBuffer)) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "Failed to create a cryptographic key for encryption. Libcrypto returned error code: %lu", ERR_get_error ()); pthread_mutex_unlock (&mutex_ssl); return -1; } pthread_mutex_unlock (&mutex_ssl); keySetBinary (cKey, keyBuffer, ELEKTRA_CRYPTO_SSL_KEYSIZE); keySetBinary (cIv, keyBuffer + ELEKTRA_CRYPTO_SSL_KEYSIZE, ELEKTRA_CRYPTO_SSL_BLOCKSIZE); return 1; }
/** * @brief read the encrypted password form the configuration and decrypt it. * @param errorKey holds an error description in case of failure. * @param config holds the plugin configuration. * @returns the decrypted master password as (Elektra) Key or NULL in case of error. Must be freed by the caller. */ Key * CRYPTO_PLUGIN_FUNCTION (getMasterPassword) (Key * errorKey, KeySet * config) { Key * master = ksLookupByName (config, ELEKTRA_CRYPTO_PARAM_MASTER_PASSWORD, 0); if (!master) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_CONFIG_FAULT, errorKey, "missing %s in plugin configuration", ELEKTRA_CRYPTO_PARAM_MASTER_PASSWORD); return NULL; } Key * msg = keyDup (master); if (CRYPTO_PLUGIN_FUNCTION (gpgDecryptMasterPassword) (config, errorKey, msg) != 1) { keyDel (msg); return NULL; // error set by CRYPTO_PLUGIN_FUNCTION(gpgDecryptMasterPassword)() } return msg; }
static void test_gpg (void) { // Plugin configuration KeySet * conf = newPluginConfiguration (); Key * errorKey = keyNew (0); // install the gpg key char * argv[] = { "", "-a", "--import", NULL }; const size_t argc = 4; Key * msg = keyNew (0); keySetBinary (msg, test_key_asc, test_key_asc_len); succeed_if (CRYPTO_PLUGIN_FUNCTION (gpgCall) (conf, errorKey, msg, argv, argc) == 1, "failed to install the GPG test key"); keyDel (msg); keyDel (errorKey); ksDel (conf); }
int elektraCryptoGcryDecrypt (elektraCryptoHandle * handle, Key * k, Key * errorKey) { gcry_error_t gcry_err; // parse salt length from crypto payload kdb_unsigned_long_t saltLen = 0; if (CRYPTO_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, NULL, &saltLen) != 1) { return -1; // error set by CRYPTO_PLUGIN_FUNCTION(getSaltFromPayload)() } saltLen += sizeof (kdb_unsigned_long_t); // set payload pointer const kdb_octet_t * payload = ((kdb_octet_t *) keyValue (k)) + saltLen + ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN; const size_t payloadLen = keyGetValueSize (k) - saltLen - ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN; // plausibility check if (payloadLen % ELEKTRA_CRYPTO_GCRY_BLOCKSIZE != 0) { ELEKTRA_SET_ERROR (ELEKTRA_ERROR_CRYPTO_DECRYPT_FAIL, errorKey, "value length is not a multiple of the block size"); return -1; } // prepare buffer for plain text output and crypto operations kdb_octet_t * output = elektraMalloc (payloadLen); if (!output) { ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed"); return -1; } // initialize crypto header data kdb_unsigned_long_t contentLen = 0; kdb_octet_t flags = ELEKTRA_CRYPTO_FLAG_NONE; // in-place decryption memcpy (output, payload, payloadLen); gcry_err = gcry_cipher_decrypt (*handle, output, payloadLen, NULL, 0); if (gcry_err != 0) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_DECRYPT_FAIL, errorKey, "Decryption failed because: %s", gcry_strerror (gcry_err)); memset (output, 0, payloadLen); elektraFree (output); return -1; } // restore the header data memcpy (&flags, output, sizeof (flags)); memcpy (&contentLen, output + sizeof (flags), sizeof (contentLen)); const kdb_octet_t * data = output + ELEKTRA_CRYPTO_GCRY_BLOCKSIZE; const size_t dataLen = payloadLen - ELEKTRA_CRYPTO_GCRY_BLOCKSIZE; // validate restored content length if (contentLen > dataLen) { ELEKTRA_SET_ERROR ( ELEKTRA_ERROR_CRYPTO_DECRYPT_FAIL, errorKey, "restored content length is bigger than the available amount of decrypted data. The header is possibly corrupted."); memset (output, 0, payloadLen); elektraFree (output); return -1; } // restore the key to its original status if ((flags & ELEKTRA_CRYPTO_FLAG_STRING) == ELEKTRA_CRYPTO_FLAG_STRING && contentLen > 0) { keySetString (k, (const char *) data); } else if ((flags & ELEKTRA_CRYPTO_FLAG_NULL) == ELEKTRA_CRYPTO_FLAG_NULL || contentLen == 0) { keySetBinary (k, NULL, 0); } else { keySetBinary (k, data, contentLen); } memset (output, 0, payloadLen); elektraFree (output); return 1; }
int elektraCryptoGcryEncrypt (elektraCryptoHandle * handle, Key * k, Key * errorKey) { size_t outputLen; gcry_error_t gcry_err; // prepare the salt for payload output kdb_unsigned_long_t saltLen = 0; kdb_octet_t * salt = NULL; if (CRYPTO_PLUGIN_FUNCTION (getSaltFromMetakey) (errorKey, k, &salt, &saltLen) != 1) { return -1; // error set by CRYPTO_PLUGIN_FUNCTION(getSaltFromMetakey)() } // remove salt as metakey because it will be encoded into the crypto payload keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, NULL); // prepare the crypto header data const kdb_octet_t * content = keyValue (k); const kdb_unsigned_long_t contentLen = keyGetValueSize (k); kdb_octet_t flags; switch (keyIsString (k)) { case 1: // string flags = ELEKTRA_CRYPTO_FLAG_STRING; break; case -1: // NULL pointer flags = ELEKTRA_CRYPTO_FLAG_NULL; break; default: // binary flags = ELEKTRA_CRYPTO_FLAG_NONE; break; } // prepare buffer for cipher text output // NOTE the header goes into the first block if (contentLen % ELEKTRA_CRYPTO_GCRY_BLOCKSIZE == 0) { outputLen = (contentLen / ELEKTRA_CRYPTO_GCRY_BLOCKSIZE) + 1; } else { outputLen = (contentLen / ELEKTRA_CRYPTO_GCRY_BLOCKSIZE) + 2; } outputLen *= ELEKTRA_CRYPTO_GCRY_BLOCKSIZE; outputLen += ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN; outputLen += sizeof (kdb_unsigned_long_t) + saltLen; kdb_octet_t * output = elektraMalloc (outputLen); if (!output) { ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed"); elektraFree (salt); return -1; } kdb_octet_t * current = output; // output of the magic number memcpy (current, ELEKTRA_CRYPTO_MAGIC_NUMBER, ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN); current += ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN; // encode the salt into the crypto payload memcpy (current, &saltLen, sizeof (kdb_unsigned_long_t)); current += sizeof (kdb_unsigned_long_t); memcpy (current, salt, saltLen); current += saltLen; // encrypt the header (1st block) using gcrypt's in-place encryption memcpy (current, &flags, sizeof (flags)); memcpy (current + sizeof (flags), &contentLen, sizeof (contentLen)); gcry_err = gcry_cipher_encrypt (*handle, current, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE, NULL, 0); if (gcry_err != 0) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_ENCRYPT_FAIL, errorKey, "Encryption failed because: %s", gcry_strerror (gcry_err)); memset (output, 0, outputLen); elektraFree (output); elektraFree (salt); return -1; } current += ELEKTRA_CRYPTO_GCRY_BLOCKSIZE; // encrypt the value using gcrypt's in-place encryption const size_t dataLen = outputLen - ELEKTRA_CRYPTO_GCRY_BLOCKSIZE - sizeof (kdb_unsigned_long_t) - saltLen - ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN; if (contentLen) memcpy (current, content, contentLen); gcry_err = gcry_cipher_encrypt (*handle, current, dataLen, NULL, 0); if (gcry_err != 0) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_ENCRYPT_FAIL, errorKey, "Encryption failed because: %s", gcry_strerror (gcry_err)); memset (output, 0, outputLen); elektraFree (output); elektraFree (salt); return -1; } // write back the cipher text to the key keySetBinary (k, output, outputLen); memset (output, 0, outputLen); elektraFree (output); elektraFree (salt); return 1; }