Example #1
0
/**
 * @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;
}
Example #3
0
/**
 * @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;
}
Example #6
0
/**
 * @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;
}
Example #7
0
/**
 * @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;
}
Example #8
0
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;
}