335 lines
11 KiB
C
Executable File
335 lines
11 KiB
C
Executable File
/*
|
|
Name : kbkdf.c
|
|
Copyright : Samsung Electronics
|
|
Description :
|
|
The kbkdf.c library file.
|
|
"NIST Special Publication 800-108
|
|
Recommendation for Key Derivation
|
|
Using Pseudorandom Functions"
|
|
|
|
NIST SP 800-108 Key Derivation Function (KDF). Counter mode.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <crypto/kbkdf.h>
|
|
#include <crypto/hash.h>
|
|
#ifdef CONFIG_CRYPTO_FIPS /* FIPS_140_2 */
|
|
#include "fips140.h"
|
|
#endif
|
|
|
|
#define HMAC_SHA512_BACKEND_CRA_NAME "hmac(sha512-generic)"
|
|
|
|
typedef struct shash_desc hmac_ctx_t;
|
|
|
|
static int HMAC_CTX_init(hmac_ctx_t **hmac_ctx)
|
|
{
|
|
struct crypto_shash *tfm = NULL;
|
|
|
|
tfm = crypto_alloc_shash(HMAC_SHA512_BACKEND_CRA_NAME, 0, 0);
|
|
if (IS_ERR(tfm))
|
|
return PTR_ERR(tfm);
|
|
|
|
*hmac_ctx = kzalloc(sizeof(hmac_ctx_t) + crypto_shash_descsize(tfm),
|
|
GFP_KERNEL);
|
|
if (!*hmac_ctx) {
|
|
crypto_free_shash(tfm);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
(*hmac_ctx)->tfm = tfm;
|
|
(*hmac_ctx)->flags = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int HMAC_Init_ex(hmac_ctx_t *hmac_ctx, const uint8_t *Ki, unsigned int intKiLength)
|
|
{
|
|
int ret_code = 0;
|
|
|
|
ret_code = crypto_shash_setkey(hmac_ctx->tfm, Ki, intKiLength);
|
|
if(ret_code)
|
|
return ret_code;
|
|
|
|
ret_code = crypto_shash_init(hmac_ctx);
|
|
if(ret_code)
|
|
return ret_code;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int HMAC_Update(hmac_ctx_t *hmac_ctx, const uint8_t *data, unsigned int data_len)
|
|
{
|
|
return crypto_shash_update(hmac_ctx, data, data_len);
|
|
}
|
|
|
|
static int HMAC_Final(hmac_ctx_t *hmac_ctx, uint8_t *data, unsigned int *data_len)
|
|
{
|
|
int ret_code = 0;
|
|
|
|
ret_code = crypto_shash_final(hmac_ctx, data);
|
|
if (ret_code)
|
|
return ret_code;
|
|
|
|
*data_len = crypto_shash_digestsize(hmac_ctx->tfm);
|
|
return 0;
|
|
}
|
|
|
|
static void HMAC_CTX_cleanup(hmac_ctx_t *hmac_ctx)
|
|
{
|
|
if (hmac_ctx) {
|
|
crypto_free_shash(hmac_ctx->tfm);
|
|
kzfree(hmac_ctx);
|
|
}
|
|
}
|
|
|
|
static void changeEndianess(uint8_t *variable, size_t length);
|
|
|
|
/*
|
|
* @brief The KDF in Counter Mode, used HMAC with SHA512.
|
|
*
|
|
* @param [in] mode The mode of execution, see kbkdf.h header file.
|
|
* @param [in] rlen The length for Iteration Counter (i) in bytes,
|
|
* see kbkdf.h header file.
|
|
* @param [in] *Ki Key derivation key, a key that is used as an
|
|
* input to a key derivation function.
|
|
* @param [in] KiLength The length of key derivation key in bytes.
|
|
* @param [out] *Ko Keying material output from a key derivation function.
|
|
* @param [out] *L The length of requested output keying material
|
|
* in bytes, returned actual output keying material
|
|
* length in bytes that may be higher or equal the
|
|
* requested length. Max and min values of length specified:
|
|
* MIN_KDF_OUTPUT_LENGTH_BYTES and MAX_KDF_OUTPUT_LENGTH_BYTES.
|
|
* @param [in] *Label A string that identifies the purpose for the
|
|
* derived keying material, which is encoded as
|
|
* a binary string.
|
|
* @param [in] LabelLength The length of 'Label' in bytes.
|
|
* @param [in] *Context A binary string containing the information
|
|
* related to the derived keying material.
|
|
* @param [in] ContextLength The length of 'Context' in bytes.
|
|
*
|
|
* @return 0 on success, nonzero on error
|
|
*/
|
|
int crypto_calc_kdf_hmac_sha512_ctr(uint8_t mode, size_t rlen,
|
|
const uint8_t *Ki, size_t KiLength, uint8_t *Ko, uint32_t *L,
|
|
const uint8_t *Label, size_t LabelLength,
|
|
const uint8_t *Context, size_t ContextLength)
|
|
{
|
|
int ret_code;
|
|
uint32_t i;
|
|
hmac_ctx_t *hmac_ctx = NULL;
|
|
unsigned int h_len;
|
|
uint8_t *fixed_data_buff = NULL;
|
|
uint8_t *tmp_ptr;
|
|
size_t fixed_data_length;
|
|
|
|
#ifdef CONFIG_CRYPTO_FIPS /* FIPS_140_2 */
|
|
if (unlikely(in_fips_err()))
|
|
return -EACCES;
|
|
#endif
|
|
|
|
if((mode != KDF_DEFAULT &&
|
|
mode != KDF_TEST_CTRLOCATION_BEFORE_FIXED &&
|
|
mode != KDF_TEST_CTRLOCATION_MIDDLE_FIXED &&
|
|
mode != KDF_TEST_CTRLOCATION_AFTER_FIXED ) ||
|
|
(rlen != KDF_RLEN_08BIT &&
|
|
rlen != KDF_RLEN_16BIT &&
|
|
rlen != KDF_RLEN_24BIT &&
|
|
rlen != KDF_RLEN_32BIT) ||
|
|
Ki == NULL || KiLength == 0 || Ko == NULL || L == NULL )
|
|
return -EINVAL;
|
|
|
|
// Initialize HMAC engine context structure.
|
|
ret_code = HMAC_CTX_init(&hmac_ctx);
|
|
if(ret_code)
|
|
return ret_code;
|
|
|
|
ret_code = -EPERM;
|
|
do {
|
|
// Checking the minimal length (in bytes)
|
|
// of the derived keying material (L).
|
|
if( *L < MIN_KDF_OUTPUT_LENGTH_BYTES ) {
|
|
*L = MIN_KDF_OUTPUT_LENGTH_BYTES;
|
|
}
|
|
else if( *L > MAX_KDF_OUTPUT_LENGTH_BYTES ) {
|
|
*L = MAX_KDF_OUTPUT_LENGTH_BYTES;
|
|
}
|
|
else {
|
|
// Make L divisible by SHA512_DIGEST_LENGTH (Actual length)
|
|
*L = round_up(*L, SHA512_DIGEST_LENGTH);
|
|
}
|
|
|
|
if( mode == KDF_DEFAULT ) { // Default mode of KDF execution.
|
|
// Calculate full fixed data length with all records:
|
|
// i || Label || 0x00 || Context || L.
|
|
fixed_data_length = rlen + LabelLength + 1 +
|
|
ContextLength + sizeof(uint32_t);
|
|
|
|
// Allocate memory for fixed data buffer.
|
|
if((fixed_data_buff = kzalloc(fixed_data_length, GFP_KERNEL)) == NULL ) {
|
|
ret_code = -ENOMEM;
|
|
break;
|
|
}
|
|
|
|
// Fill all required records of fixed data for NIST default mode.
|
|
tmp_ptr = fixed_data_buff;
|
|
|
|
memset(tmp_ptr, 0, rlen); // i
|
|
tmp_ptr += rlen;
|
|
|
|
if( LabelLength != 0 ) { // Label
|
|
memcpy(tmp_ptr, Label, LabelLength);
|
|
tmp_ptr += LabelLength;
|
|
}
|
|
|
|
*tmp_ptr = 0x00; // 0x00 delimiter
|
|
tmp_ptr += 1;
|
|
// Context
|
|
if( ContextLength != 0 ) {
|
|
memcpy(tmp_ptr, Context, ContextLength);
|
|
tmp_ptr += ContextLength;
|
|
}
|
|
|
|
i = *L * 8; // L in bits
|
|
memcpy(tmp_ptr, (void *)&i, sizeof(uint32_t));
|
|
tmp_ptr += sizeof(uint32_t);
|
|
}
|
|
else // All NIST test modes of KDF execution.
|
|
if( mode == KDF_TEST_CTRLOCATION_BEFORE_FIXED ||
|
|
mode == KDF_TEST_CTRLOCATION_MIDDLE_FIXED ||
|
|
mode == KDF_TEST_CTRLOCATION_AFTER_FIXED ) {
|
|
// Calculate full fixed data length
|
|
// for test modes
|
|
fixed_data_length = rlen + LabelLength + ContextLength;
|
|
|
|
// Allocate memory for fixed data buffer.
|
|
if((fixed_data_buff = kzalloc(fixed_data_length, GFP_KERNEL)) == NULL ) {
|
|
ret_code = -ENOMEM;
|
|
break;
|
|
}
|
|
// Fill all required records of fixed data for all test modes.
|
|
tmp_ptr = fixed_data_buff;
|
|
if( mode == KDF_TEST_CTRLOCATION_BEFORE_FIXED ) {
|
|
// i
|
|
memset(tmp_ptr, 0, rlen);
|
|
tmp_ptr += rlen;
|
|
// Label
|
|
if( LabelLength != 0 ) {
|
|
memcpy(tmp_ptr, Label, LabelLength);
|
|
tmp_ptr += LabelLength;
|
|
}
|
|
}
|
|
else
|
|
if( mode == KDF_TEST_CTRLOCATION_MIDDLE_FIXED ||
|
|
mode == KDF_TEST_CTRLOCATION_AFTER_FIXED ) {
|
|
// Label
|
|
if( LabelLength != 0 ) {
|
|
memcpy(tmp_ptr, Label, LabelLength);
|
|
tmp_ptr += LabelLength;
|
|
}
|
|
// i
|
|
memset(tmp_ptr, 0, rlen);
|
|
tmp_ptr += rlen;
|
|
// Context
|
|
if( ContextLength != 0 ) {
|
|
memcpy(tmp_ptr, Context, ContextLength);
|
|
tmp_ptr += ContextLength;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Execute KDF in Counter Mode cycle.
|
|
for(i = 1, h_len = 0;
|
|
i <= (*L / SHA512_DIGEST_LENGTH);
|
|
i++, Ko += h_len) {
|
|
|
|
// Initialize HMAC engine context.
|
|
ret_code = HMAC_Init_ex(hmac_ctx, Ki, KiLength);
|
|
if( ret_code != 0 ) {
|
|
break;
|
|
}
|
|
// Default mode of KDF execution.
|
|
if( mode == KDF_DEFAULT ) { // i
|
|
memcpy(fixed_data_buff, (void *)&i, rlen);
|
|
}
|
|
else // All NIST test modes of KDF execution.
|
|
if( mode == KDF_TEST_CTRLOCATION_BEFORE_FIXED ||
|
|
mode == KDF_TEST_CTRLOCATION_MIDDLE_FIXED ||
|
|
mode == KDF_TEST_CTRLOCATION_AFTER_FIXED ) {
|
|
if( mode == KDF_TEST_CTRLOCATION_BEFORE_FIXED ) {
|
|
tmp_ptr = fixed_data_buff;
|
|
}
|
|
else
|
|
if( mode == KDF_TEST_CTRLOCATION_MIDDLE_FIXED ||
|
|
mode == KDF_TEST_CTRLOCATION_AFTER_FIXED ) {
|
|
tmp_ptr = fixed_data_buff + LabelLength;
|
|
}
|
|
// Update Iteration Counter (i) for test modes.
|
|
memcpy(tmp_ptr, (void *)&i, rlen);
|
|
changeEndianess(tmp_ptr, rlen);
|
|
}
|
|
|
|
ret_code = HMAC_Update(hmac_ctx, fixed_data_buff, fixed_data_length);
|
|
if( ret_code != 0 ) {
|
|
break;
|
|
}
|
|
|
|
ret_code = HMAC_Final(hmac_ctx, Ko, &h_len);
|
|
if( ret_code != 0 ) {
|
|
break;
|
|
}
|
|
}
|
|
} while(0);
|
|
|
|
// Release allocated memory.
|
|
if( fixed_data_buff != NULL )
|
|
kzfree((void*)fixed_data_buff);
|
|
|
|
// Clenup HMAC engine.
|
|
HMAC_CTX_cleanup(hmac_ctx);
|
|
|
|
return ret_code;
|
|
}
|
|
EXPORT_SYMBOL_GPL(crypto_calc_kdf_hmac_sha512_ctr);
|
|
|
|
/*
|
|
* Change endianess for variable with specified length.
|
|
*/
|
|
static void changeEndianess(uint8_t *variable, size_t length)
|
|
{
|
|
size_t i;
|
|
uint8_t byte;
|
|
uint8_t *tail;
|
|
|
|
if( variable == NULL || length < 2 )
|
|
return;
|
|
|
|
for(i = 0, tail = variable + (length - 1);
|
|
i < length - 1; i ++, tail --, variable ++) {
|
|
byte = *variable;
|
|
*variable = *tail;
|
|
*tail = byte;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int __init kdf_hmac_sha512_ctr_module_init(void)
|
|
{
|
|
/* TODO: driver register */
|
|
return 0;
|
|
}
|
|
|
|
static void __exit kdf_hmac_sha512_ctr_module_exit(void)
|
|
{
|
|
/* TODO: driver unregister */
|
|
return;
|
|
}
|
|
|
|
module_init(kdf_hmac_sha512_ctr_module_init);
|
|
module_exit(kdf_hmac_sha512_ctr_module_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("KDF in Counter Mode, used HMAC with SHA512");
|
|
MODULE_ALIAS_CRYPTO("kdf_hmac(sha512)_ctr");
|