Implementing KDF in Counter Mode for key derivation function. Moving device key to features

pull/6642/head
Yossi Levy 2018-05-08 16:59:05 +03:00
parent 582ee1df10
commit 98e83c2262
6 changed files with 112 additions and 70 deletions

View File

@ -0,0 +1,46 @@
## DeviceKey
DeviceKey is a mechanism that implements key derivation from a root of trust key. The DeviceKey mechanism generates symmetric keys that security features need. You can use these keys for encryption, authentication and more. The DeviceKey API allows key derivation without exposing the actual root of trust, to reduce the possibility of accidental exposure of the root of trust outside the device.
We have implemented DeviceKey according to NIST SP 800-108, section "KDF in Counter Mode", with AES-CMAC as the pseudorandom function.
### Root of Trust
The root of trust key, which DeviceKey uses to derive additional keys, is generated using the hardware random generator if it exists, or using a key injected to the device in the production process.
The characteristics required by this root of trust are:
- It must be unique per device.
- It must be difficult to guess.
- It must be at least 128 bits.
- It must be kept secret.
The DeviceKey feature keeps the root of trust key in internal storage, using the NVStore component. Internal storage provides protection from external physical attacks to the device.
The root of trust is generated at the first use of Devicekey if the true random number generator is available in the device. If no true random number generator is available, you must pass the injected root of trust key to the DeviceKey before you call the key derivation API.
### Key derivation API
`generate_derived_key`: This API generates a new key based on a string (salt) the caller provides. The same key is generated for the same salt. Generated keys can be 128 or 256 bits in length.
#### Root of Trust Injection API
`device_inject_root_of_trust`: You must call this API once in the lifecycle of the device, before any call to key derivation, if the device does not support True Random generator (`DEVICE_TRNG` is not defined).
#### Using DeviceKey
DeviceKey is a singleton class, meaning that the system can have only a single instance of it.
To instantiate DeviceKey, you need to call its `get_instance` member function as following:
```c++
DeviceKey &deviceKey = DeviceKey::get_instance();
```
#### Testing DeviceKey
Run the DeviceKey functionality test with the `mbed` command as following:
```
```mbed test -n features-device_key-tests-device_key-functionality```
```

View File

@ -0,0 +1,3 @@
{
"name": "device_key"
}

View File

@ -14,12 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
#include "drivers/DeviceKey.h" #include "DeviceKey.h"
#include "mbedtls/config.h" #include "mbedtls/config.h"
#include "mbedtls/cmac.h" #include "mbedtls/cmac.h"
#include "nvstore.h" #include "nvstore.h"
#include "trng_api.h" #include "trng_api.h"
#include "mbed_wait_api.h" #include "mbed_wait_api.h"
#include "stdlib.h"
#if !defined(MBEDTLS_CMAC_C) #if !defined(MBEDTLS_CMAC_C)
#error [NOT_SUPPORTED] MBEDTLS_CMAC_C needs to be enabled for this driver #error [NOT_SUPPORTED] MBEDTLS_CMAC_C needs to be enabled for this driver
@ -29,13 +30,21 @@
namespace mbed { namespace mbed {
static void buffer_zeroize(void *v, size_t n) #define DEVKEY_WRITE_UINT32_LE( dst, src ) \
{ do \
volatile unsigned char *p = (volatile unsigned char *)v; { \
while ( n-- ) { (dst)[0] = ( (src) >> 0 ) & 0xFF; \
*p++ = 0; (dst)[1] = ( (src) >> 8 ) & 0xFF; \
} (dst)[2] = ( (src) >> 16 ) & 0xFF; \
} (dst)[3] = ( (src) >> 24 ) & 0xFF; \
} while( 0 )
#define DEVKEY_WRITE_UINT8_LE( dst, src ) \
do \
{ \
(dst)[0] = (src) & 0xFF; \
} while( 0 )
DeviceKey::DeviceKey() DeviceKey::DeviceKey()
{ {
@ -145,12 +154,19 @@ int DeviceKey::read_key_from_nvstore(uint32_t *output, size_t& size)
return DEVICEKEY_SUCCESS; return DEVICEKEY_SUCCESS;
} }
// Calculate CMAC functions - wrapper for mbedtls start/update and finish int DeviceKey::get_derived_key(uint32_t *ikey_buff, size_t ikey_size, const unsigned char *isalt,
int DeviceKey::calculate_cmac(const unsigned char *input, size_t isize, uint32_t *ikey_buff, int ikey_size, size_t isalt_size, unsigned char *output, uint32_t ikey_type)
unsigned char *output)
{ {
//KDF in counter mode implementation as described in Section 5.1
//of NIST SP 800-108, Recommendation for Key Derivation Using Pseudorandom Functions
int ret; int ret;
size_t counter = 0;
char separator = 0x00;
mbedtls_cipher_context_t ctx; mbedtls_cipher_context_t ctx;
unsigned char output_len_enc[ 4 ] = {0};
unsigned char counter_enc[ 1 ] = {0};
DEVKEY_WRITE_UINT32_LE(output_len_enc, ikey_type);
mbedtls_cipher_type_t mbedtls_cipher_type = MBEDTLS_CIPHER_AES_128_ECB; mbedtls_cipher_type_t mbedtls_cipher_type = MBEDTLS_CIPHER_AES_128_ECB;
if (DEVICE_KEY_32BYTE == ikey_size) { if (DEVICE_KEY_32BYTE == ikey_size) {
@ -165,60 +181,46 @@ int DeviceKey::calculate_cmac(const unsigned char *input, size_t isize, uint32_t
goto finish; goto finish;
} }
ret = mbedtls_cipher_cmac_starts(&ctx, (unsigned char *)ikey_buff, ikey_size * 8); do {
if (ret != 0) {
goto finish;
}
ret = mbedtls_cipher_cmac_update(&ctx, input, isize); ret = mbedtls_cipher_cmac_starts(&ctx, (unsigned char *)ikey_buff, ikey_size * 8);
if (ret != 0) { if (ret != 0) {
goto finish; goto finish;
} }
ret = mbedtls_cipher_cmac_finish(&ctx, output); DEVKEY_WRITE_UINT8_LE(counter_enc, (counter+1));
if (ret != 0) {
goto finish;
}
ret = DEVICEKEY_SUCCESS; ret = mbedtls_cipher_cmac_update(&ctx, (unsigned char *)counter_enc, sizeof(counter_enc));
if (ret != 0) {
goto finish;
}
ret = mbedtls_cipher_cmac_update(&ctx, isalt, isalt_size);
if (ret != 0) {
goto finish;
}
ret = mbedtls_cipher_cmac_update(&ctx, (unsigned char *)&separator, sizeof(char));
if (ret != 0) {
goto finish;
}
ret = mbedtls_cipher_cmac_update(&ctx, (unsigned char *)&output_len_enc, sizeof(output_len_enc));
if (ret != 0) {
goto finish;
}
ret = mbedtls_cipher_cmac_finish(&ctx, output + (DEVICE_KEY_16BYTE * (counter)));
if (ret != 0) {
goto finish;
}
counter++;
} while (DEVICE_KEY_16BYTE * counter < ikey_type);
finish: finish:
mbedtls_cipher_free( &ctx ); mbedtls_cipher_free( &ctx );
return ret;
}
int DeviceKey::get_derived_key(uint32_t *ikey_buff, size_t ikey_size, const unsigned char *isalt,
size_t isalt_size, unsigned char *output, uint32_t ikey_type)
{
int ret;
unsigned char *double_size_salt = NULL;
if (DEVICE_KEY_16BYTE == ikey_type) {
ret = calculate_cmac(isalt, isalt_size, ikey_buff, ikey_size, output);
if (DEVICEKEY_SUCCESS != ret) {
goto finish;
}
}
if (DEVICE_KEY_32BYTE == ikey_type) {
ret = this->calculate_cmac(isalt, isalt_size, ikey_buff, ikey_size, output);
if (DEVICEKEY_SUCCESS != ret) {
goto finish;
}
//Double the salt size cause cmac always return just 16 bytes
double_size_salt = new unsigned char[isalt_size * 2];
memcpy(double_size_salt, isalt, isalt_size);
memcpy(double_size_salt + isalt_size, isalt, isalt_size);
ret = this->calculate_cmac(double_size_salt, isalt_size * 2, ikey_buff, ikey_size, output + 16);
}
finish:
if (double_size_salt != NULL) {
buffer_zeroize(double_size_salt, isalt_size);
delete[] double_size_salt;
}
if (DEVICEKEY_SUCCESS != ret) { if (DEVICEKEY_SUCCESS != ret) {
return DEVICEKEY_ERR_CMAC_GENERIC_FAILURE; return DEVICEKEY_ERR_CMAC_GENERIC_FAILURE;

View File

@ -94,16 +94,6 @@ private:
// Private constructor, as class is a singleton // Private constructor, as class is a singleton
DeviceKey(); DeviceKey();
/** Calculate CMAC
* @param input buffer contain some string.
* @param isize size of the supplied input string.
* @param ikey_buff input buffer holding the ROT key
* @param ikey_size size of the input key. must be 16 bytes or 32 bytes.
* @param output buffer for the CMAC result.
* @return 0 on success, negative error code on failure
*/
int calculate_cmac(const unsigned char *input, size_t isize, uint32_t *ikey_buff, int ikey_size, unsigned char *output);
/** Read a device key from the NVStore /** Read a device key from the NVStore
* @param output buffer for the returned key. * @param output buffer for the returned key.
* @param size input: the size of the output buffer. * @param size input: the size of the output buffer.
@ -119,7 +109,8 @@ private:
*/ */
int write_key_to_nvstore(uint32_t *input, size_t isize); int write_key_to_nvstore(uint32_t *input, size_t isize);
/** Get a derived key base on a salt string /** Get a derived key base on a salt string. The methods implements
* Section 5.1 in NIST SP 800-108, Recommendation for Key Derivation Using Pseudorandom Functions
* @param ikey_buff input buffer holding the ROT key * @param ikey_buff input buffer holding the ROT key
* @param ikey_size size of the input key. Must be 16 bytes or 32 bytes. * @param ikey_size size of the input key. Must be 16 bytes or 32 bytes.
* @param isalt input buffer contain some string. * @param isalt input buffer contain some string.