From a8febd57a144cba85df7b7210b96b6df4dfc02c3 Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Wed, 21 Feb 2018 11:48:13 +0200 Subject: [PATCH 01/17] Device key implementation --- TESTS/host_tests/devicekey_reset.py | 150 ++++++ TESTS/mbed_drivers/device_key/main.cpp | 488 ++++++++++++++++++ drivers/DeviceKey.cpp | 252 +++++++++ drivers/DeviceKey.h | 148 ++++++ .../mbedtls/inc/mbedtls/config-no-entropy.h | 1 + features/mbedtls/inc/mbedtls/config.h | 2 +- features/nvstore/source/nvstore.h | 2 + 7 files changed, 1042 insertions(+), 1 deletion(-) create mode 100644 TESTS/host_tests/devicekey_reset.py create mode 100644 TESTS/mbed_drivers/device_key/main.cpp create mode 100644 drivers/DeviceKey.cpp create mode 100644 drivers/DeviceKey.h diff --git a/TESTS/host_tests/devicekey_reset.py b/TESTS/host_tests/devicekey_reset.py new file mode 100644 index 0000000000..08379739cb --- /dev/null +++ b/TESTS/host_tests/devicekey_reset.py @@ -0,0 +1,150 @@ +""" +Copyright (c) 2018 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import time +from mbed_host_tests import BaseHostTest +from mbed_host_tests.host_tests_runner.host_test_default import DefaultTestSelector + +DEFAULT_CYCLE_PERIOD = 1.0 +MSG_VALUE_DUMMY = '0' +MSG_KEY_DEVICE_READY = 'ready' +MSG_KEY_DEVICE_FINISH = 'finish' +MSG_KEY_DEVICE_TEST_STEP1 = 'check_consistency_step1' +MSG_KEY_DEVICE_TEST_STEP2 = 'check_consistency_step2' +MSG_KEY_DEVICE_TEST_STEP3 = 'check_consistency_step3' +MSG_KEY_DEVICE_TEST_STEP4 = 'check_consistency_step4' +MSG_KEY_SYNC = '__sync' +MSG_KEY_TEST_SUITE_ENDED = 'Test suite ended' + +class DeviceKeyResetTest(BaseHostTest): + """Test for the DeviceKey driver API. + """ + + def __init__(self): + super(DeviceKeyResetTest, self).__init__() + self.reset = False + self.finish = False + self.suite_ended = False + cycle_s = self.get_config_item('program_cycle_s') + self.program_cycle_s = cycle_s if cycle_s is not None else DEFAULT_CYCLE_PERIOD + self.test_steps_sequence = self.test_steps() + # Advance the coroutine to it's first yield statement. + self.test_steps_sequence.send(None) + + def setup(self): + self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready) + self.register_callback(MSG_KEY_DEVICE_FINISH, self.cb_device_finish) + self.register_callback(MSG_KEY_TEST_SUITE_ENDED, self.cb_device_test_suit_ended) + + def cb_device_ready(self, key, value, timestamp): + """Acknowledge device rebooted correctly and feed the test execution + """ + self.reset = True + + try: + if self.test_steps_sequence.send(value): + self.notify_complete(True) + except (StopIteration, RuntimeError) as exc: + self.notify_complete(False) + + def cb_device_finish(self, key, value, timestamp): + """Acknowledge device finished a test step correctly and feed the test execution + """ + self.finish = True + + try: + if self.test_steps_sequence.send(value): + self.notify_complete(True) + except (StopIteration, RuntimeError) as exc: + self.notify_complete(False) + + def cb_device_test_suit_ended(self, key, value, timestamp): + """Acknowledge device finished a test step correctly and feed the test execution + """ + self.suite_ended = True + + try: + if self.test_steps_sequence.send(value): + self.notify_complete(True) + except (StopIteration, RuntimeError) as exc: + self.notify_complete(False) + + def test_steps(self): + """Test step 1 (16 byte key test) + """ + wait_for_communication = yield + + self.reset = False + self.send_kv(MSG_KEY_DEVICE_TEST_STEP1, MSG_VALUE_DUMMY) + time.sleep(self.program_cycle_s) + self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY) + + wait_for_communication = yield + + if self.reset == False: + raise RuntimeError('Phase 1: Platform did not reset as expected.') + + """Test step 2 (After reset) + """ + self.finish = False + self.send_kv(MSG_KEY_DEVICE_TEST_STEP2, MSG_VALUE_DUMMY) + time.sleep(self.program_cycle_s) + wait_for_communication = yield + + if self.finish == False: + raise RuntimeError('Test failed.') + + """Test Step 3 (32 byte key test) + """ + wait_for_communication = yield + + self.reset = False + self.send_kv(MSG_KEY_DEVICE_TEST_STEP3, MSG_VALUE_DUMMY) + time.sleep(self.program_cycle_s) + self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY) + + wait_for_communication = yield + + if self.reset == False: + raise RuntimeError('Phase 3: Platform did not reset as expected.') + + """Test step 4 (After reset) + """ + self.finish = False + self.send_kv(MSG_KEY_DEVICE_TEST_STEP4, MSG_VALUE_DUMMY) + time.sleep(self.program_cycle_s) + + wait_for_communication = yield + + if self.finish == False: + raise RuntimeError('Test failed.') + + """Test step 4 (After reset) + """ + self.suite_ended = False + time.sleep(self.program_cycle_s) + + wait_for_communication = yield + + if self.suite_ended == False: + raise RuntimeError('Test failed.') + + # The sequence is correct -- test passed. + yield True + + + + \ No newline at end of file diff --git a/TESTS/mbed_drivers/device_key/main.cpp b/TESTS/mbed_drivers/device_key/main.cpp new file mode 100644 index 0000000000..eac7dafeb1 --- /dev/null +++ b/TESTS/mbed_drivers/device_key/main.cpp @@ -0,0 +1,488 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DeviceKey.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "nvstore.h" + +using namespace utest::v1; + +#define MSG_VALUE_DUMMY "0" +#define MSG_VALUE_LEN 32 +#define MSG_KEY_LEN 32 + +#define MSG_KEY_DEVICE_READY "ready" +#define MSG_KEY_DEVICE_FINISH "finish" +#define MSG_KEY_DEVICE_TEST_STEP1 "check_consistency_step1" +#define MSG_KEY_DEVICE_TEST_STEP2 "check_consistency_step2" +#define MSG_KEY_DEVICE_TEST_STEP3 "check_consistency_step3" +#define MSG_KEY_DEVICE_TEST_STEP4 "check_consistency_step4" +#define MSG_KEY_DEVICE_TEST_SUITE_ENDED "Test suite ended" + +void device_key_derived_key_consistency_16_byte_key_reset_test(char *key); +void device_key_derived_key_consistency_32_byte_key_reset_test(char *key); + +/* + * Injection of a dummy key when there is no TRNG + */ +int inject_dummy_rot_key() +{ +#if !defined(DEVICE_TRNG) + uint32_t key[DEVICE_KEY_16BYTE / sizeof(uint32_t)]; + + memset(key, 0, DEVICE_KEY_16BYTE); + memcpy(key, "1234567812345678", DEVICE_KEY_16BYTE); + int size = DEVICE_KEY_16BYTE; + DeviceKey& devkey = DeviceKey::get_instance(); + return devkey.device_inject_root_of_trust(key, size); +#else + return DEVICEKEY_SUCCESS; +#endif +} + +void device_key_derived_key_reset_test() +{ + greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY); + + static char key[MSG_KEY_LEN + 1] = { }; + static char value[MSG_VALUE_LEN + 1] = { }; + memset(key, 0, MSG_KEY_LEN + 1); + memset(value, 0, MSG_VALUE_LEN + 1); + + greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); + + if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP1) == 0 || strcmp(key, MSG_KEY_DEVICE_TEST_STEP2) == 0) { + device_key_derived_key_consistency_16_byte_key_reset_test(key); + return device_key_derived_key_reset_test(); + } + + if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP3) == 0 || strcmp(key, MSG_KEY_DEVICE_TEST_STEP4) == 0) { + return device_key_derived_key_consistency_32_byte_key_reset_test(key); + } + + TEST_ASSERT_MESSAGE(false, key); //Indicates error!!! +} + +/* + * Test the consistency of derived 16 byte key result after device reset. + */ +void device_key_derived_key_consistency_16_byte_key_reset_test(char *key) +{ + unsigned char output1[DEVICE_KEY_16BYTE]; + unsigned char output2[DEVICE_KEY_16BYTE]; + unsigned char empty_buffer[DEVICE_KEY_16BYTE]; + unsigned char salt[] = "Once upon a time, I worked for the circus and I lived in Omaha."; + int key_type = DEVICE_KEY_16BYTE; + uint16_t actual_size = 0; + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + size_t salt_size = sizeof(salt); + + if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP1) == 0) { + + //Third step: Clear NVStore, create an ROT key, derive a 16 byte + //key and store it in NVStore at index 15, At the end reset the device + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memset(output1, 0, sizeof(output1)); + ret = devkey.device_key_derived_key(salt, salt_size, output1, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + bool is_empty = !memcmp(empty_buffer, output1, sizeof(output1)); + TEST_ASSERT_FALSE(is_empty); + + ret = nvstore.set(15, DEVICE_KEY_16BYTE, output1); + TEST_ASSERT_EQUAL_INT32(0, ret); + + system_reset(); + TEST_ASSERT_MESSAGE(false, "system_reset() did not reset the device as expected."); + } else if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP2) == 0) { + + //Second step: Read from NVStore at index 15 there should be a derived key there. + //Now try to derive a key for 100 times and check it is the same key like before the reset. + //At the end clear NVStore. + int ret = nvstore.get(15, DEVICE_KEY_16BYTE, output1, actual_size); + TEST_ASSERT_FALSE(NVSTORE_SUCCESS != ret) + TEST_ASSERT_EQUAL_INT(DEVICE_KEY_16BYTE, actual_size); + + for (int i = 0; i < 100; i++) { + memset(output2, 0, sizeof(output2)); + ret = devkey.device_key_derived_key(salt, salt_size, output2, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY(output1, output2, DEVICE_KEY_16BYTE); + } + + ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + greentea_send_kv(MSG_KEY_DEVICE_FINISH, MSG_VALUE_DUMMY); + } else { + TEST_ASSERT_MESSAGE(false, "Unknown test step received"); + } +} + +/* + * Test the consistency of derived 32 byte key result after device reset. + */ +void device_key_derived_key_consistency_32_byte_key_reset_test(char *key) +{ + unsigned char output1[DEVICE_KEY_32BYTE]; + unsigned char output2[DEVICE_KEY_32BYTE]; + unsigned char empty_buffer[DEVICE_KEY_32BYTE]; + unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; + int key_type = DEVICE_KEY_32BYTE; + uint16_t actual_size = 0; + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + size_t salt_size = sizeof(salt); + + if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP3) == 0) { + + //Third step: Clear NVStore, create an ROT key, derive a 32 byte + //key and store it in NVStore at index 15, At the end reset the device + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memset(output1, 0, sizeof(output1)); + ret = devkey.device_key_derived_key(salt, salt_size, output1, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + bool is_empty = !memcmp(empty_buffer, output1, sizeof(output1)); + TEST_ASSERT_FALSE(is_empty); + + ret = nvstore.set(15, DEVICE_KEY_32BYTE, output1); + TEST_ASSERT_EQUAL_INT32(0, ret); + + system_reset(); + TEST_ASSERT_MESSAGE(false, "system_reset() did not reset the device as expected."); + } else if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP4) == 0) { + + //Fourth step: Read from NVStore at index 15 there should be a derived key there. + //Now try to derive a key for 100 times and check it is the same key like before the reset. + //At the end clear NVStore. + int ret = nvstore.get(15, DEVICE_KEY_32BYTE, output1, actual_size); + TEST_ASSERT_FALSE(NVSTORE_SUCCESS != ret) + TEST_ASSERT_EQUAL_INT(DEVICE_KEY_32BYTE, actual_size); + + for (int i = 0; i < 100; i++) { + memset(output2, 0, sizeof(output2)); + ret = devkey.device_key_derived_key(salt, salt_size, output2, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY(output1, output2, DEVICE_KEY_32BYTE); + } + + ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + greentea_send_kv(MSG_KEY_DEVICE_FINISH, MSG_VALUE_DUMMY); + } else { + TEST_ASSERT_MESSAGE(false, "Unknown test step received"); + } +} + +/* + * Test that wrong size of key is rejected when trying to persist a key + */ +void device_inject_root_of_trust_wrong_size_test() +{ + DeviceKey& devkey = DeviceKey::get_instance(); + uint32_t key[DEVICE_KEY_32BYTE / sizeof(uint32_t)]; + + memcpy(key, "12345678123456788765432187654321", DEVICE_KEY_32BYTE); + + for (int i = 0; i < 50; i++) { + if (DEVICE_KEY_16BYTE == i || DEVICE_KEY_32BYTE == i) { + continue; + } + int ret = devkey.device_inject_root_of_trust(key, i); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_INVALID_KEY_SIZE, ret); + } +} + +/* + * Test that a 16 byte size key is written to persistent storage + */ +void device_inject_root_of_trust_16_byte_size_test() +{ + DeviceKey& devkey = DeviceKey::get_instance(); + uint32_t rkey[DEVICE_KEY_16BYTE / sizeof(uint32_t)]; + uint16_t actual_size; + uint32_t key[DEVICE_KEY_16BYTE / sizeof(uint32_t)]; + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memcpy(key, "1234567812345678", sizeof(key)); + ret = devkey.device_inject_root_of_trust(key, DEVICE_KEY_16BYTE); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + //Read the key from NVStore. + memset(rkey, 0, sizeof(rkey)); + ret = nvstore.get(NVSTORE_DEVICEKEY_KEY, DEVICE_KEY_16BYTE, rkey, actual_size); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + TEST_ASSERT_EQUAL_INT(DEVICE_KEY_16BYTE, actual_size); + TEST_ASSERT_EQUAL_INT32_ARRAY(key, rkey, actual_size / sizeof(uint32_t)); +} + +/* + * Test that a 32 byte size key is written to persistent storage + */ +void device_inject_root_of_trust_32_byte_size_test() +{ + DeviceKey& devkey = DeviceKey::get_instance(); + uint32_t rkey[DEVICE_KEY_32BYTE / sizeof(uint32_t)]; + uint16_t actual_size; + uint32_t key[DEVICE_KEY_32BYTE / sizeof(uint32_t)]; + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memcpy(key, "12345678123456788765432187654321", sizeof(key)); + ret = devkey.device_inject_root_of_trust(key, DEVICE_KEY_32BYTE); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + //Read the key from NVStore. + memset(rkey, 0, sizeof(rkey)); + ret = nvstore.get(NVSTORE_DEVICEKEY_KEY, DEVICE_KEY_32BYTE, rkey, actual_size); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + TEST_ASSERT_EQUAL_INT(DEVICE_KEY_32BYTE, actual_size); + TEST_ASSERT_EQUAL_INT32_ARRAY(key, rkey, actual_size / sizeof(uint32_t)); +} + +/* + * Test that a key can be written to persistent storage only once. + */ +void device_inject_root_of_trust_several_times_test() +{ + DeviceKey& devkey = DeviceKey::get_instance(); + uint32_t key[DEVICE_KEY_32BYTE / sizeof(uint32_t)]; + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memcpy(key, "12345678123456788765432187654321", DEVICE_KEY_32BYTE); + ret = devkey.device_inject_root_of_trust(key, DEVICE_KEY_32BYTE); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + //Trying to use the same key should fail. + ret = devkey.device_inject_root_of_trust(key, DEVICE_KEY_32BYTE); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_ALREADY_EXIST, ret); + + //Trying to use different key should also fail. + ret = devkey.device_inject_root_of_trust(key, DEVICE_KEY_16BYTE); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_ALREADY_EXIST, ret); +} + +/* + * Test the consistency of derived 16 byte key result. + */ +void device_key_derived_key_consistency_16_byte_key_test() +{ + unsigned char output1[DEVICE_KEY_16BYTE]; + unsigned char output2[DEVICE_KEY_16BYTE]; + unsigned char empty_buffer[DEVICE_KEY_16BYTE]; + unsigned char salt[] = "Once upon a time, I worked for the circus and I lived in Omaha."; + int key_type = DEVICE_KEY_16BYTE; + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + size_t salt_size = sizeof(salt); + memset(output1, 0, sizeof(output1)); + ret = devkey.device_key_derived_key(salt, salt_size, output1, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + bool is_empty = !memcmp(empty_buffer, output1, sizeof(output1)); + TEST_ASSERT_FALSE(is_empty); + + for (int i = 0; i < 100; i++) { + memset(output2, 0, sizeof(output2)); + ret = devkey.device_key_derived_key(salt, salt_size, output2, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY(output1, output2, DEVICE_KEY_16BYTE); + } +} + +/* + * Test the consistency of derived 32 byte key result. + */ +void device_key_derived_key_consistency_32_byte_key_test() +{ + unsigned char output1[DEVICE_KEY_32BYTE]; + unsigned char output2[DEVICE_KEY_32BYTE]; + unsigned char empty_buffer[DEVICE_KEY_32BYTE]; + unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; + int key_type = DEVICE_KEY_32BYTE; + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + size_t salt_size = sizeof(salt); + memset(output1, 0, sizeof(output1)); + ret = devkey.device_key_derived_key(salt, salt_size, output1, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + bool is_empty = !memcmp(empty_buffer, output1, sizeof(output1)); + TEST_ASSERT_FALSE(is_empty); + + for (int i = 0; i < 100; i++) { + memset(output2, 0, sizeof(output2)); + ret = devkey.device_key_derived_key(salt, salt_size, output2, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY(output1, output2, DEVICE_KEY_32BYTE); + } +} + +/* + * Test request for 16 byte key is returning a correct key size. + */ +void device_key_derived_key_key_type_16_test() +{ + unsigned char output[DEVICE_KEY_16BYTE * 2]; + unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; + unsigned char expectedString[] = "Some String"; + int key_type = DEVICE_KEY_16BYTE; + size_t salt_size = sizeof(salt); + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memset(output, 0, DEVICE_KEY_16BYTE * 2); + memcpy(output + DEVICE_KEY_16BYTE - sizeof(expectedString), expectedString, sizeof(expectedString)); + memcpy(output + DEVICE_KEY_16BYTE + 1, expectedString, sizeof(expectedString)); + + ret = devkey.device_key_derived_key(salt, salt_size, output, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + //Test that we didn't override the buffer after the 16 byte size + TEST_ASSERT_EQUAL_UINT8_ARRAY(output + DEVICE_KEY_16BYTE + 1, expectedString, sizeof(expectedString)); + //Test that we did override the buffer all 16 byte + TEST_ASSERT(memcmp(output + DEVICE_KEY_16BYTE - sizeof(expectedString), expectedString, sizeof(expectedString)) != 0); +} + +/* + * Test request for 32 byte key is returning a correct key size. + */ +void device_key_derived_key_key_type_32_test() +{ + unsigned char output[DEVICE_KEY_32BYTE * 2]; + unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; + int key_type = DEVICE_KEY_32BYTE; + size_t salt_size = sizeof(salt); + unsigned char expectedString[] = "Some String"; + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memset(output, 0, DEVICE_KEY_32BYTE * 2); + memcpy(output + DEVICE_KEY_32BYTE - sizeof(expectedString), expectedString, sizeof(expectedString)); + memcpy(output + DEVICE_KEY_32BYTE + 1, expectedString, sizeof(expectedString)); + + ret = devkey.device_key_derived_key(salt, salt_size, output, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + //Test that we didn't override the buffer after the 32 byte size + TEST_ASSERT_EQUAL_UINT8_ARRAY(output + DEVICE_KEY_32BYTE + 1, expectedString, sizeof(expectedString)); + //Test that we did override the buffer all 32 byte + TEST_ASSERT(memcmp(output + DEVICE_KEY_32BYTE - sizeof(expectedString), expectedString, sizeof(expectedString)) != 0); +} + +/* + * Test request for unknown key size returns an error + */ +void device_key_derived_key_wrong_key_type_test() +{ + unsigned char output[DEVICE_KEY_16BYTE]; + unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; + size_t salt_size = sizeof(salt); + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + + nvstore.init(); + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memset(output, 0, DEVICE_KEY_32BYTE); + ret = devkey.device_key_derived_key(salt, salt_size, output, 12);//96 bit key type is not supported + TEST_ASSERT_EQUAL_INT32(DEVICEKEY_INVALID_KEY_TYPE, ret); + +} + +utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason) +{ + greentea_case_failure_abort_handler(source, reason); + return STATUS_CONTINUE; +} + +//Currently there can be only one test that contains reset and it has to be the first test! +Case cases[] = { + Case("Device Key - derived key reset", device_key_derived_key_reset_test, greentea_failure_handler), + Case("Device Key - inject value wrong size", device_inject_root_of_trust_wrong_size_test, greentea_failure_handler), + Case("Device Key - inject value 16 byte size", device_inject_root_of_trust_16_byte_size_test, greentea_failure_handler), + Case("Device Key - inject value 32 byte size", device_inject_root_of_trust_32_byte_size_test, greentea_failure_handler), + Case("Device Key - inject value several times", device_inject_root_of_trust_several_times_test, greentea_failure_handler), + Case("Device Key - derived key consistency 16 byte key", device_key_derived_key_consistency_16_byte_key_test, greentea_failure_handler), + Case("Device Key - derived key consistency 32 byte key", device_key_derived_key_consistency_32_byte_key_test, greentea_failure_handler), + Case("Device Key - derived key key type 16", device_key_derived_key_key_type_16_test, greentea_failure_handler), + Case("Device Key - derived key key type 32", device_key_derived_key_key_type_32_test, greentea_failure_handler), + Case("Device Key - derived key wrong key type", device_key_derived_key_wrong_key_type_test, greentea_failure_handler) +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(14, "devicekey_reset"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + bool ret = Harness::run(specification); + greentea_send_kv(MSG_KEY_DEVICE_TEST_SUITE_ENDED, MSG_VALUE_DUMMY); + + return ret; +} + diff --git a/drivers/DeviceKey.cpp b/drivers/DeviceKey.cpp new file mode 100644 index 0000000000..6efa9f0265 --- /dev/null +++ b/drivers/DeviceKey.cpp @@ -0,0 +1,252 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "drivers/DeviceKey.h" +#include "mbedtls/config.h" +#include "mbedtls/cmac.h" +#include "nvstore.h" +#include "trng_api.h" + +#if !defined(MBEDTLS_CMAC_C) +#error [NOT_SUPPORTED] MBEDTLS_CMAC_C needs to be enabled for this driver +#else + +namespace mbed { + +DeviceKey::DeviceKey() +{ + return; +} + +DeviceKey::~DeviceKey() +{ + return; +} + +int DeviceKey::device_key_derived_key(const unsigned char *salt, size_t isalt_size, unsigned char *output, + uint16_t ikey_type) +{ + uint32_t key_buff[DEVICE_KEY_32BYTE / sizeof(uint32_t)]; + size_t actual_size = DEVICE_KEY_32BYTE; + + if (DEVICE_KEY_16BYTE != ikey_type && DEVICE_KEY_32BYTE != ikey_type) { + return DEVICEKEY_INVALID_KEY_TYPE; + } + + //First try to read the key from NVStore + int ret = read_key_from_nvstore(key_buff, actual_size); + if (DEVICEKEY_SUCCESS != ret && DEVICEKEY_NOT_FOUND != ret) { + return ret; + } + + if (DEVICE_KEY_16BYTE != actual_size && DEVICE_KEY_32BYTE != actual_size) { + return DEVICEKEY_READ_FAILED; + } + + //If the key was not found in NVStore we will create it by using TRNG and then save it to NVStore + if (DEVICEKEY_NOT_FOUND == ret) { + ret = generate_key_by_trng(key_buff, actual_size); + if (DEVICEKEY_SUCCESS != ret) { + return ret; + } + + ret = device_inject_root_of_trust(key_buff, actual_size); + if (DEVICEKEY_SUCCESS != ret) { + return ret; + } + } + + ret = get_derived_key(key_buff, actual_size, salt, isalt_size, output, ikey_type); + return ret; +} + +int DeviceKey::device_inject_root_of_trust(uint32_t *value, size_t isize) +{ + return write_key_to_nvstore(value, isize); +} + +int DeviceKey::write_key_to_nvstore(uint32_t *input, size_t isize) +{ + if (DEVICE_KEY_16BYTE != isize && DEVICE_KEY_32BYTE != isize) { + return DEVICEKEY_INVALID_KEY_SIZE; + } + + //First we read if key exist. If it is exists, we return DEVICEKEY_ALREADY_EXIST error + uint32_t read_key[DEVICE_KEY_32BYTE / sizeof(uint32_t)] = {0}; + size_t read_size = DEVICE_KEY_32BYTE; + int ret = read_key_from_nvstore(read_key, read_size); + if (DEVICEKEY_SUCCESS == ret) { + return DEVICEKEY_ALREADY_EXIST; + } + if (DEVICEKEY_NOT_FOUND != ret) { + return ret; + } + + NVStore& nvstore = NVStore::get_instance(); + ret = nvstore.set(NVSTORE_DEVICEKEY_KEY, (uint16_t)isize, input); + if (NVSTORE_WRITE_ERROR == ret || NVSTORE_BUFF_TOO_SMALL == ret) { + return DEVICEKEY_SAVE_FAILED; + } + + if (NVSTORE_SUCCESS != ret) { + return DEVICEKEY_NVSTORE_UNPREDICTABLE_ERROR; + } + + return DEVICEKEY_SUCCESS; +} + +int DeviceKey::read_key_from_nvstore(uint32_t *output, size_t& size) +{ + if (size > UINT16_MAX) { + return DEVICEKEY_INVALID_PARAM; + } + + uint16_t in_size = size; + uint16_t out_size = 0; + NVStore& nvstore = NVStore::get_instance(); + int nvStatus = nvstore.get(NVSTORE_DEVICEKEY_KEY, in_size, output, out_size); + if (NVSTORE_NOT_FOUND == nvStatus) { + return DEVICEKEY_NOT_FOUND; + } + + if (NVSTORE_READ_ERROR == nvStatus || NVSTORE_BUFF_TOO_SMALL == nvStatus) { + return DEVICEKEY_READ_FAILED; + } + + if (NVSTORE_SUCCESS != nvStatus) { + return DEVICEKEY_NVSTORE_UNPREDICTABLE_ERROR; + } + + size = out_size; + return DEVICEKEY_SUCCESS; +} + +// Calculate CMAC functions - wrapper for mbedtls start/update and finish +int DeviceKey::calc_cmac(const unsigned char *input, size_t isize, uint32_t *ikey_buff, int ikey_size, + unsigned char *output) +{ + int ret; + mbedtls_cipher_context_t ctx; + + mbedtls_cipher_type_t mbedtls_cipher_type = MBEDTLS_CIPHER_AES_128_ECB; + if (DEVICE_KEY_32BYTE == ikey_size) { + mbedtls_cipher_type = MBEDTLS_CIPHER_AES_256_ECB; + } + + const mbedtls_cipher_info_t *cipher_info = mbedtls_cipher_info_from_type(mbedtls_cipher_type); + + mbedtls_cipher_init(&ctx); + ret = mbedtls_cipher_setup(&ctx, cipher_info); + if (ret != 0) { + goto finish; + } + + ret = mbedtls_cipher_cmac_starts(&ctx, (unsigned char *)ikey_buff, ikey_size * 8); + if (ret != 0) { + goto finish; + } + + ret = mbedtls_cipher_cmac_update(&ctx, input, isize); + if (ret != 0) { + goto finish; + } + + ret = mbedtls_cipher_cmac_finish(&ctx, output); + if (ret != 0) { + goto finish; + } + + return DEVICEKEY_SUCCESS; + +finish: + 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 = calc_cmac(isalt, isalt_size, ikey_buff, ikey_size, output); + if (DEVICEKEY_SUCCESS != ret) { + goto finish; + } + } + + if (DEVICE_KEY_32BYTE == ikey_type) { + ret = this->calc_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->calc_cmac(double_size_salt, isalt_size * 2, ikey_buff, ikey_size, output + 16); + } + +finish: + if (double_size_salt != NULL) { + delete[] double_size_salt; + } + + if (DEVICEKEY_SUCCESS != ret) { + return DEVICEKEY_ERR_CMAC_GENERIC_FAILURE; + } + + return DEVICEKEY_SUCCESS; +} + +int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) +{ +#if defined(DEVICE_TRNG) + size_t in_size; + trng_t trng_obj; + + memset(output, 0, size); + + if (DEVICE_KEY_16BYTE > size) { + return DEVICEKEY_BUFFER_TO_SMALL; + } else if (DEVICE_KEY_16BYTE <= size && DEVICE_KEY_32BYTE > size) { + in_size = DEVICE_KEY_16BYTE; + } else { + in_size = DEVICE_KEY_32BYTE; + } + + trng_init(&trng_obj); + + int ret = trng_get_bytes(&trng_obj, (unsigned char *)output, in_size, &size); + if (DEVICEKEY_SUCCESS != ret || in_size != size) { + return DEVICEKEY_TRNG_ERROR; + } + + trng_free(&trng_obj); + return DEVICEKEY_SUCCESS; +#else + return DEVICEKEY_NO_KEY_INJECTED; +#endif +} + +} // namespace mbed + +#endif + + diff --git a/drivers/DeviceKey.h b/drivers/DeviceKey.h new file mode 100644 index 0000000000..e04c1034cf --- /dev/null +++ b/drivers/DeviceKey.h @@ -0,0 +1,148 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_DEVICEKEY_H +#define MBED_DEVICEKEY_H + +#include "stddef.h" +#include "stdint.h" +#include "platform/NonCopyable.h" + +namespace mbed { +/** \addtogroup drivers */ + +#define DEVICE_KEY_16BYTE 16 +#define DEVICE_KEY_32BYTE 32 + +enum DeviceKeyStatus { + DEVICEKEY_SUCCESS = 0, + DEVICEKEY_INVALID_KEY_SIZE = -1, + DEVICEKEY_INVALID_KEY_TYPE = -2, + DEVICEKEY_SAVE_FAILED = -3, + DEVICEKEY_ALREADY_EXIST = -4, + DEVICEKEY_NOT_FOUND = -5, + DEVICEKEY_READ_FAILED = -6, + DEVICEKEY_NVSTORE_UNPREDICTABLE_ERROR = -7, + DEVICEKEY_ERR_CMAC_GENERIC_FAILURE = -8, + DEVICEKEY_BUFFER_TO_SMALL = -9, + DEVICEKEY_NO_KEY_INJECTED = -10, + DEVICEKEY_INVALID_PARAM = -11, + DEVICEKEY_TRNG_ERROR = -12, +}; + +/** Use this singleton if you need to derive a new key from the device root of trust. + * + * + * @endcode + * @ingroup drivers + */ + +class DeviceKey : private mbed::NonCopyable { +public: + + /** + * @brief As a singleton, return the single instance of the class. + * Reason for this class being a singleton is the following: + * - Ease the use for users of this class not having to coordinate instantiations. + * - Lazy instantiation of internal data (which we can't achieve with simple static classes). + * + * @returns Singleton instance reference. + */ + static DeviceKey& get_instance() + { + // Use this implementation of singleton (Meyer's) rather than the one that allocates + // the instance on the heap, as it ensures destruction at program end (preventing warnings + // from memory checking tools such as valgrind). + static DeviceKey instance; + return instance; + } + + virtual ~DeviceKey(); + + /** Derive a new key based on the salt string. key type can be with values 16 bytes and 32 bytes + * @param isalt input buffer used to create the new key. Same input will generate always the same key + * @param isalt_size size of the data in salt buffer + * @param output buffer to receive the derived key. Size must be 16 bytes or 32 bytes + * according to the ikey_type parameter + * @param ikey_type type of the required key. Type must be 16 bytes or 32 bytes. + * @return 0 on success, negative error code on failure + */ + int device_key_derived_key(const unsigned char *isalt, size_t isalt_size, unsigned char *output, uint16_t ikey_type); + + /** Set a device key into the NVStore. In case TRNG support is missing, Call this method + * before calling device_key_derived_key. This method should be called only once! + * @param value input buffer contain the key. + * @param isize size of the supplied key. Must be 16 bytes or 32 bytes. + * @return 0 on success, negative error code on failure + */ + int device_inject_root_of_trust(uint32_t *value, size_t isize); + +private: + // Private constructor, as class is a singleton + 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 calc_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 + * @param output buffer for the returned key. + * @param size input: the size of the output buffer. + * output: the actual size of the written data + * @return 0 on success, negative error code on failure + */ + int read_key_from_nvstore(uint32_t *output, size_t& size); + + /** Set a device key into the NVStore + * @param input input buffer contain the key. + * @param isize the size of the input buffer. + * @return 0 on success, negative error code on failure + */ + int write_key_to_nvstore(uint32_t *input, size_t isize); + + /** Get a derived key base on a salt 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 isalt input buffer contain some string. + * @param isalt_size size of the supplied input string. + * @param output buffer for the derived key result. + * @param ikey_type the requested key size. Must be 16 bytes or 32 bytes. + * @return 0 on success, negative error code on failure + */ + int 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); + + /** Generate a random ROT key by using TRNG + * @param output output buffer for the generated key. + * @param size input: the size of the buffer. if size is less + * then 16 bytes the method will generate an + * error. 16-31 bytes will create a 16 byte key. + * 32 or higher will generate a 32 bytes key + * output: the actual written size to the buffer + * @return 0 on success, negative error code on failure + */ + int generate_key_by_trng(uint32_t *output, size_t& size); +}; +/** @}*/ + +} + +#endif diff --git a/features/mbedtls/inc/mbedtls/config-no-entropy.h b/features/mbedtls/inc/mbedtls/config-no-entropy.h index b4a0930b9c..a0e979f0c2 100644 --- a/features/mbedtls/inc/mbedtls/config-no-entropy.h +++ b/features/mbedtls/inc/mbedtls/config-no-entropy.h @@ -82,6 +82,7 @@ #define MBEDTLS_X509_USE_C #define MBEDTLS_X509_CRT_PARSE_C #define MBEDTLS_X509_CRL_PARSE_C +#define MBEDTLS_CMAC_C /* Miscellaneous options */ #define MBEDTLS_AES_ROM_TABLES diff --git a/features/mbedtls/inc/mbedtls/config.h b/features/mbedtls/inc/mbedtls/config.h index f1a0307ad1..fe174e0c61 100644 --- a/features/mbedtls/inc/mbedtls/config.h +++ b/features/mbedtls/inc/mbedtls/config.h @@ -1861,7 +1861,7 @@ * Requires: MBEDTLS_AES_C or MBEDTLS_DES_C * */ -//#define MBEDTLS_CMAC_C +#define MBEDTLS_CMAC_C /** * \def MBEDTLS_CTR_DRBG_C diff --git a/features/nvstore/source/nvstore.h b/features/nvstore/source/nvstore.h index 91c675e057..39859d3285 100644 --- a/features/nvstore/source/nvstore.h +++ b/features/nvstore/source/nvstore.h @@ -49,6 +49,8 @@ typedef enum { // All predefined keys used for internal features should be defined here + NVSTORE_DEVICEKEY_KEY = 4, + NVSTORE_LAST_PREDEFINED_KEY = 15, NVSTORE_NUM_PREDEFINED_KEYS } nvstore_predefined_keys_e; From 206ca6cbf539ff4f55426fbd982fb32e76a4d92b Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Mon, 16 Apr 2018 16:12:51 +0300 Subject: [PATCH 02/17] Changing methods names, adding NVSTORE_ENABLED check for compilation and fixing doxygen issues --- TESTS/mbed_drivers/device_key/main.cpp | 72 ++++++++++++++------------ drivers/DeviceKey.cpp | 17 +++--- drivers/DeviceKey.h | 12 +++-- 3 files changed, 55 insertions(+), 46 deletions(-) diff --git a/TESTS/mbed_drivers/device_key/main.cpp b/TESTS/mbed_drivers/device_key/main.cpp index eac7dafeb1..ee7a38ee90 100644 --- a/TESTS/mbed_drivers/device_key/main.cpp +++ b/TESTS/mbed_drivers/device_key/main.cpp @@ -20,6 +20,10 @@ #include "greentea-client/test_env.h" #include "nvstore.h" +#if !NVSTORE_ENABLED +#error [NOT_SUPPORTED] NVSTORE needs to be enabled for this test +#endif + using namespace utest::v1; #define MSG_VALUE_DUMMY "0" @@ -34,8 +38,8 @@ using namespace utest::v1; #define MSG_KEY_DEVICE_TEST_STEP4 "check_consistency_step4" #define MSG_KEY_DEVICE_TEST_SUITE_ENDED "Test suite ended" -void device_key_derived_key_consistency_16_byte_key_reset_test(char *key); -void device_key_derived_key_consistency_32_byte_key_reset_test(char *key); +void generate_derived_key_consistency_16_byte_key_reset_test(char *key); +void generate_derived_key_consistency_32_byte_key_reset_test(char *key); /* * Injection of a dummy key when there is no TRNG @@ -55,7 +59,7 @@ int inject_dummy_rot_key() #endif } -void device_key_derived_key_reset_test() +void generate_derived_key_reset_test() { greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY); @@ -67,12 +71,12 @@ void device_key_derived_key_reset_test() greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP1) == 0 || strcmp(key, MSG_KEY_DEVICE_TEST_STEP2) == 0) { - device_key_derived_key_consistency_16_byte_key_reset_test(key); - return device_key_derived_key_reset_test(); + generate_derived_key_consistency_16_byte_key_reset_test(key); + return generate_derived_key_reset_test(); } if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP3) == 0 || strcmp(key, MSG_KEY_DEVICE_TEST_STEP4) == 0) { - return device_key_derived_key_consistency_32_byte_key_reset_test(key); + return generate_derived_key_consistency_32_byte_key_reset_test(key); } TEST_ASSERT_MESSAGE(false, key); //Indicates error!!! @@ -81,7 +85,7 @@ void device_key_derived_key_reset_test() /* * Test the consistency of derived 16 byte key result after device reset. */ -void device_key_derived_key_consistency_16_byte_key_reset_test(char *key) +void generate_derived_key_consistency_16_byte_key_reset_test(char *key) { unsigned char output1[DEVICE_KEY_16BYTE]; unsigned char output2[DEVICE_KEY_16BYTE]; @@ -104,7 +108,7 @@ void device_key_derived_key_consistency_16_byte_key_reset_test(char *key) TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); memset(output1, 0, sizeof(output1)); - ret = devkey.device_key_derived_key(salt, salt_size, output1, key_type); + ret = devkey.generate_derived_key(salt, salt_size, output1, key_type); TEST_ASSERT_EQUAL_INT32(0, ret); bool is_empty = !memcmp(empty_buffer, output1, sizeof(output1)); TEST_ASSERT_FALSE(is_empty); @@ -125,7 +129,7 @@ void device_key_derived_key_consistency_16_byte_key_reset_test(char *key) for (int i = 0; i < 100; i++) { memset(output2, 0, sizeof(output2)); - ret = devkey.device_key_derived_key(salt, salt_size, output2, key_type); + ret = devkey.generate_derived_key(salt, salt_size, output2, key_type); TEST_ASSERT_EQUAL_INT32(0, ret); TEST_ASSERT_EQUAL_UINT8_ARRAY(output1, output2, DEVICE_KEY_16BYTE); } @@ -142,7 +146,7 @@ void device_key_derived_key_consistency_16_byte_key_reset_test(char *key) /* * Test the consistency of derived 32 byte key result after device reset. */ -void device_key_derived_key_consistency_32_byte_key_reset_test(char *key) +void generate_derived_key_consistency_32_byte_key_reset_test(char *key) { unsigned char output1[DEVICE_KEY_32BYTE]; unsigned char output2[DEVICE_KEY_32BYTE]; @@ -165,7 +169,7 @@ void device_key_derived_key_consistency_32_byte_key_reset_test(char *key) TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); memset(output1, 0, sizeof(output1)); - ret = devkey.device_key_derived_key(salt, salt_size, output1, key_type); + ret = devkey.generate_derived_key(salt, salt_size, output1, key_type); TEST_ASSERT_EQUAL_INT32(0, ret); bool is_empty = !memcmp(empty_buffer, output1, sizeof(output1)); TEST_ASSERT_FALSE(is_empty); @@ -186,7 +190,7 @@ void device_key_derived_key_consistency_32_byte_key_reset_test(char *key) for (int i = 0; i < 100; i++) { memset(output2, 0, sizeof(output2)); - ret = devkey.device_key_derived_key(salt, salt_size, output2, key_type); + ret = devkey.generate_derived_key(salt, salt_size, output2, key_type); TEST_ASSERT_EQUAL_INT32(0, ret); TEST_ASSERT_EQUAL_UINT8_ARRAY(output1, output2, DEVICE_KEY_32BYTE); } @@ -299,7 +303,7 @@ void device_inject_root_of_trust_several_times_test() /* * Test the consistency of derived 16 byte key result. */ -void device_key_derived_key_consistency_16_byte_key_test() +void generate_derived_key_consistency_16_byte_key_test() { unsigned char output1[DEVICE_KEY_16BYTE]; unsigned char output2[DEVICE_KEY_16BYTE]; @@ -317,14 +321,14 @@ void device_key_derived_key_consistency_16_byte_key_test() size_t salt_size = sizeof(salt); memset(output1, 0, sizeof(output1)); - ret = devkey.device_key_derived_key(salt, salt_size, output1, key_type); + ret = devkey.generate_derived_key(salt, salt_size, output1, key_type); TEST_ASSERT_EQUAL_INT32(0, ret); bool is_empty = !memcmp(empty_buffer, output1, sizeof(output1)); TEST_ASSERT_FALSE(is_empty); for (int i = 0; i < 100; i++) { memset(output2, 0, sizeof(output2)); - ret = devkey.device_key_derived_key(salt, salt_size, output2, key_type); + ret = devkey.generate_derived_key(salt, salt_size, output2, key_type); TEST_ASSERT_EQUAL_INT32(0, ret); TEST_ASSERT_EQUAL_UINT8_ARRAY(output1, output2, DEVICE_KEY_16BYTE); } @@ -333,7 +337,7 @@ void device_key_derived_key_consistency_16_byte_key_test() /* * Test the consistency of derived 32 byte key result. */ -void device_key_derived_key_consistency_32_byte_key_test() +void generate_derived_key_consistency_32_byte_key_test() { unsigned char output1[DEVICE_KEY_32BYTE]; unsigned char output2[DEVICE_KEY_32BYTE]; @@ -351,14 +355,14 @@ void device_key_derived_key_consistency_32_byte_key_test() size_t salt_size = sizeof(salt); memset(output1, 0, sizeof(output1)); - ret = devkey.device_key_derived_key(salt, salt_size, output1, key_type); + ret = devkey.generate_derived_key(salt, salt_size, output1, key_type); TEST_ASSERT_EQUAL_INT32(0, ret); bool is_empty = !memcmp(empty_buffer, output1, sizeof(output1)); TEST_ASSERT_FALSE(is_empty); for (int i = 0; i < 100; i++) { memset(output2, 0, sizeof(output2)); - ret = devkey.device_key_derived_key(salt, salt_size, output2, key_type); + ret = devkey.generate_derived_key(salt, salt_size, output2, key_type); TEST_ASSERT_EQUAL_INT32(0, ret); TEST_ASSERT_EQUAL_UINT8_ARRAY(output1, output2, DEVICE_KEY_32BYTE); } @@ -367,7 +371,7 @@ void device_key_derived_key_consistency_32_byte_key_test() /* * Test request for 16 byte key is returning a correct key size. */ -void device_key_derived_key_key_type_16_test() +void generate_derived_key_key_type_16_test() { unsigned char output[DEVICE_KEY_16BYTE * 2]; unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; @@ -387,7 +391,7 @@ void device_key_derived_key_key_type_16_test() memcpy(output + DEVICE_KEY_16BYTE - sizeof(expectedString), expectedString, sizeof(expectedString)); memcpy(output + DEVICE_KEY_16BYTE + 1, expectedString, sizeof(expectedString)); - ret = devkey.device_key_derived_key(salt, salt_size, output, key_type); + ret = devkey.generate_derived_key(salt, salt_size, output, key_type); TEST_ASSERT_EQUAL_INT32(0, ret); //Test that we didn't override the buffer after the 16 byte size TEST_ASSERT_EQUAL_UINT8_ARRAY(output + DEVICE_KEY_16BYTE + 1, expectedString, sizeof(expectedString)); @@ -398,7 +402,7 @@ void device_key_derived_key_key_type_16_test() /* * Test request for 32 byte key is returning a correct key size. */ -void device_key_derived_key_key_type_32_test() +void generate_derived_key_key_type_32_test() { unsigned char output[DEVICE_KEY_32BYTE * 2]; unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; @@ -418,7 +422,7 @@ void device_key_derived_key_key_type_32_test() memcpy(output + DEVICE_KEY_32BYTE - sizeof(expectedString), expectedString, sizeof(expectedString)); memcpy(output + DEVICE_KEY_32BYTE + 1, expectedString, sizeof(expectedString)); - ret = devkey.device_key_derived_key(salt, salt_size, output, key_type); + ret = devkey.generate_derived_key(salt, salt_size, output, key_type); TEST_ASSERT_EQUAL_INT32(0, ret); //Test that we didn't override the buffer after the 32 byte size TEST_ASSERT_EQUAL_UINT8_ARRAY(output + DEVICE_KEY_32BYTE + 1, expectedString, sizeof(expectedString)); @@ -429,7 +433,7 @@ void device_key_derived_key_key_type_32_test() /* * Test request for unknown key size returns an error */ -void device_key_derived_key_wrong_key_type_test() +void generate_derived_key_wrong_key_type_test() { unsigned char output[DEVICE_KEY_16BYTE]; unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; @@ -445,7 +449,7 @@ void device_key_derived_key_wrong_key_type_test() TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); memset(output, 0, DEVICE_KEY_32BYTE); - ret = devkey.device_key_derived_key(salt, salt_size, output, 12);//96 bit key type is not supported + ret = devkey.generate_derived_key(salt, salt_size, output, 12);//96 bit key type is not supported TEST_ASSERT_EQUAL_INT32(DEVICEKEY_INVALID_KEY_TYPE, ret); } @@ -458,16 +462,16 @@ utest::v1::status_t greentea_failure_handler(const Case *const source, const fai //Currently there can be only one test that contains reset and it has to be the first test! Case cases[] = { - Case("Device Key - derived key reset", device_key_derived_key_reset_test, greentea_failure_handler), - Case("Device Key - inject value wrong size", device_inject_root_of_trust_wrong_size_test, greentea_failure_handler), - Case("Device Key - inject value 16 byte size", device_inject_root_of_trust_16_byte_size_test, greentea_failure_handler), - Case("Device Key - inject value 32 byte size", device_inject_root_of_trust_32_byte_size_test, greentea_failure_handler), - Case("Device Key - inject value several times", device_inject_root_of_trust_several_times_test, greentea_failure_handler), - Case("Device Key - derived key consistency 16 byte key", device_key_derived_key_consistency_16_byte_key_test, greentea_failure_handler), - Case("Device Key - derived key consistency 32 byte key", device_key_derived_key_consistency_32_byte_key_test, greentea_failure_handler), - Case("Device Key - derived key key type 16", device_key_derived_key_key_type_16_test, greentea_failure_handler), - Case("Device Key - derived key key type 32", device_key_derived_key_key_type_32_test, greentea_failure_handler), - Case("Device Key - derived key wrong key type", device_key_derived_key_wrong_key_type_test, greentea_failure_handler) + Case("Device Key - derived key reset", generate_derived_key_reset_test, greentea_failure_handler), + Case("Device Key - inject value wrong size", device_inject_root_of_trust_wrong_size_test, greentea_failure_handler), + Case("Device Key - inject value 16 byte size", device_inject_root_of_trust_16_byte_size_test, greentea_failure_handler), + Case("Device Key - inject value 32 byte size", device_inject_root_of_trust_32_byte_size_test, greentea_failure_handler), + Case("Device Key - inject value several times", device_inject_root_of_trust_several_times_test, greentea_failure_handler), + Case("Device Key - derived key consistency 16 byte key", generate_derived_key_consistency_16_byte_key_test, greentea_failure_handler), + Case("Device Key - derived key consistency 32 byte key", generate_derived_key_consistency_32_byte_key_test, greentea_failure_handler), + Case("Device Key - derived key key type 16", generate_derived_key_key_type_16_test, greentea_failure_handler), + Case("Device Key - derived key key type 32", generate_derived_key_key_type_32_test, greentea_failure_handler), + Case("Device Key - derived key wrong key type", generate_derived_key_wrong_key_type_test, greentea_failure_handler) }; utest::v1::status_t greentea_test_setup(const size_t number_of_cases) diff --git a/drivers/DeviceKey.cpp b/drivers/DeviceKey.cpp index 6efa9f0265..fd12dc0631 100644 --- a/drivers/DeviceKey.cpp +++ b/drivers/DeviceKey.cpp @@ -24,6 +24,8 @@ #error [NOT_SUPPORTED] MBEDTLS_CMAC_C needs to be enabled for this driver #else +#if NVSTORE_ENABLED + namespace mbed { DeviceKey::DeviceKey() @@ -36,8 +38,8 @@ DeviceKey::~DeviceKey() return; } -int DeviceKey::device_key_derived_key(const unsigned char *salt, size_t isalt_size, unsigned char *output, - uint16_t ikey_type) +int DeviceKey::generate_derived_key(const unsigned char *salt, size_t isalt_size, unsigned char *output, + uint16_t ikey_type) { uint32_t key_buff[DEVICE_KEY_32BYTE / sizeof(uint32_t)]; size_t actual_size = DEVICE_KEY_32BYTE; @@ -135,8 +137,8 @@ int DeviceKey::read_key_from_nvstore(uint32_t *output, size_t& size) } // Calculate CMAC functions - wrapper for mbedtls start/update and finish -int DeviceKey::calc_cmac(const unsigned char *input, size_t isize, uint32_t *ikey_buff, int ikey_size, - unsigned char *output) +int DeviceKey::calculate_cmac(const unsigned char *input, size_t isize, uint32_t *ikey_buff, int ikey_size, + unsigned char *output) { int ret; mbedtls_cipher_context_t ctx; @@ -183,14 +185,14 @@ int DeviceKey::get_derived_key(uint32_t *ikey_buff, size_t ikey_size, const unsi unsigned char *double_size_salt = NULL; if (DEVICE_KEY_16BYTE == ikey_type) { - ret = calc_cmac(isalt, isalt_size, ikey_buff, ikey_size, output); + 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->calc_cmac(isalt, isalt_size, ikey_buff, ikey_size, output); + ret = this->calculate_cmac(isalt, isalt_size, ikey_buff, ikey_size, output); if (DEVICEKEY_SUCCESS != ret) { goto finish; } @@ -200,7 +202,7 @@ int DeviceKey::get_derived_key(uint32_t *ikey_buff, size_t ikey_size, const unsi memcpy(double_size_salt, isalt, isalt_size); memcpy(double_size_salt + isalt_size, isalt, isalt_size); - ret = this->calc_cmac(double_size_salt, isalt_size * 2, ikey_buff, ikey_size, output + 16); + ret = this->calculate_cmac(double_size_salt, isalt_size * 2, ikey_buff, ikey_size, output + 16); } finish: @@ -247,6 +249,7 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) } // namespace mbed +#endif //NVSTORE_ENABLED #endif diff --git a/drivers/DeviceKey.h b/drivers/DeviceKey.h index e04c1034cf..e4aaddcf61 100644 --- a/drivers/DeviceKey.h +++ b/drivers/DeviceKey.h @@ -20,6 +20,8 @@ #include "stdint.h" #include "platform/NonCopyable.h" +#if (NVSTORE_ENABLED) + namespace mbed { /** \addtogroup drivers */ @@ -44,8 +46,7 @@ enum DeviceKeyStatus { /** Use this singleton if you need to derive a new key from the device root of trust. * - * - * @endcode + * @note Synchronization level: Thread safe * @ingroup drivers */ @@ -69,7 +70,7 @@ public: return instance; } - virtual ~DeviceKey(); + ~DeviceKey(); /** Derive a new key based on the salt string. key type can be with values 16 bytes and 32 bytes * @param isalt input buffer used to create the new key. Same input will generate always the same key @@ -79,7 +80,7 @@ public: * @param ikey_type type of the required key. Type must be 16 bytes or 32 bytes. * @return 0 on success, negative error code on failure */ - int device_key_derived_key(const unsigned char *isalt, size_t isalt_size, unsigned char *output, uint16_t ikey_type); + int generate_derived_key(const unsigned char *isalt, size_t isalt_size, unsigned char *output, uint16_t ikey_type); /** Set a device key into the NVStore. In case TRNG support is missing, Call this method * before calling device_key_derived_key. This method should be called only once! @@ -101,7 +102,7 @@ private: * @param output buffer for the CMAC result. * @return 0 on success, negative error code on failure */ - int calc_cmac(const unsigned char *input, size_t isize, uint32_t *ikey_buff, int ikey_size, unsigned char *output); + 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 * @param output buffer for the returned key. @@ -145,4 +146,5 @@ private: } +#endif //NVSTORE_ENABLED #endif From fd92c994482efe97371eec333ed1fd24aef14b59 Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Sun, 22 Apr 2018 18:13:55 +0300 Subject: [PATCH 03/17] Fixing the use of trng_get_bytes to include recalls if not all entropy achieved. also some other minor fixes --- drivers/DeviceKey.cpp | 46 ++++++++++++++++++++++++++++++++++++------- drivers/DeviceKey.h | 1 + 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/drivers/DeviceKey.cpp b/drivers/DeviceKey.cpp index fd12dc0631..f2f4bba61e 100644 --- a/drivers/DeviceKey.cpp +++ b/drivers/DeviceKey.cpp @@ -19,6 +19,7 @@ #include "mbedtls/cmac.h" #include "nvstore.h" #include "trng_api.h" +#include "mbed_wait_api.h" #if !defined(MBEDTLS_CMAC_C) #error [NOT_SUPPORTED] MBEDTLS_CMAC_C needs to be enabled for this driver @@ -28,6 +29,14 @@ namespace mbed { +static void buffer_zeroize(void *v, size_t n) +{ + volatile unsigned char *p = (volatile unsigned char *)v; + while ( n-- ) { + *p++ = 0; + } +} + DeviceKey::DeviceKey() { return; @@ -171,7 +180,7 @@ int DeviceKey::calculate_cmac(const unsigned char *input, size_t isize, uint32_t goto finish; } - return DEVICEKEY_SUCCESS; + ret = DEVICEKEY_SUCCESS; finish: mbedtls_cipher_free( &ctx ); @@ -207,6 +216,7 @@ int DeviceKey::get_derived_key(uint32_t *ikey_buff, size_t ikey_size, const unsi finish: if (double_size_salt != NULL) { + buffer_zeroize(double_size_salt, isalt_size); delete[] double_size_salt; } @@ -221,27 +231,49 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) { #if defined(DEVICE_TRNG) size_t in_size; + size_t ongoing_size; + size_t final_size; trng_t trng_obj; + int ret = DEVICEKEY_SUCCESS; + unsigned char *pBuffer = (unsigned char *)output; memset(output, 0, size); if (DEVICE_KEY_16BYTE > size) { return DEVICEKEY_BUFFER_TO_SMALL; } else if (DEVICE_KEY_16BYTE <= size && DEVICE_KEY_32BYTE > size) { - in_size = DEVICE_KEY_16BYTE; + size = DEVICE_KEY_16BYTE; } else { - in_size = DEVICE_KEY_32BYTE; + size = DEVICE_KEY_32BYTE; } trng_init(&trng_obj); - int ret = trng_get_bytes(&trng_obj, (unsigned char *)output, in_size, &size); - if (DEVICEKEY_SUCCESS != ret || in_size != size) { - return DEVICEKEY_TRNG_ERROR; + final_size = 0; + in_size = size; + while (true) { + ongoing_size = 0; + ret = trng_get_bytes(&trng_obj, (unsigned char *)pBuffer, in_size, &ongoing_size); + final_size += ongoing_size; + if (DEVICEKEY_SUCCESS != ret) { + ret = DEVICEKEY_TRNG_ERROR; + goto finish; + } + if (DEVICEKEY_SUCCESS == ret && final_size == size) { + break; + } + wait_ms(5); + pBuffer += ongoing_size; + in_size -= ongoing_size; } + ret = DEVICEKEY_SUCCESS; + +finish: trng_free(&trng_obj); - return DEVICEKEY_SUCCESS; + size = final_size; + return ret; + #else return DEVICEKEY_NO_KEY_INJECTED; #endif diff --git a/drivers/DeviceKey.h b/drivers/DeviceKey.h index e4aaddcf61..192330c363 100644 --- a/drivers/DeviceKey.h +++ b/drivers/DeviceKey.h @@ -141,6 +141,7 @@ private: * @return 0 on success, negative error code on failure */ int generate_key_by_trng(uint32_t *output, size_t& size); + }; /** @}*/ From 582ee1df10141270374e46be3f28b704920025b6 Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Sun, 22 Apr 2018 18:21:10 +0300 Subject: [PATCH 04/17] Improving code style --- drivers/DeviceKey.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/DeviceKey.cpp b/drivers/DeviceKey.cpp index f2f4bba61e..7742f5fb4f 100644 --- a/drivers/DeviceKey.cpp +++ b/drivers/DeviceKey.cpp @@ -252,6 +252,7 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) final_size = 0; in_size = size; while (true) { + ongoing_size = 0; ret = trng_get_bytes(&trng_obj, (unsigned char *)pBuffer, in_size, &ongoing_size); final_size += ongoing_size; @@ -259,9 +260,11 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) ret = DEVICEKEY_TRNG_ERROR; goto finish; } + if (DEVICEKEY_SUCCESS == ret && final_size == size) { break; } + wait_ms(5); pBuffer += ongoing_size; in_size -= ongoing_size; From 98e83c2262a9eb8641a885ec44ad1532b3476ba3 Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Tue, 8 May 2018 16:59:05 +0300 Subject: [PATCH 05/17] Implementing KDF in Counter Mode for key derivation function. Moving device key to features --- features/device_key/README.md | 46 +++++++ .../TESTS/device_key/functionality}/main.cpp | 0 .../TESTS}/host_tests/devicekey_reset.py | 0 features/device_key/mbed_lib.json | 3 + .../device_key/source}/DeviceKey.cpp | 120 +++++++++--------- .../device_key/source}/DeviceKey.h | 13 +- 6 files changed, 112 insertions(+), 70 deletions(-) create mode 100644 features/device_key/README.md rename {TESTS/mbed_drivers/device_key => features/device_key/TESTS/device_key/functionality}/main.cpp (100%) rename {TESTS => features/device_key/TESTS}/host_tests/devicekey_reset.py (100%) create mode 100644 features/device_key/mbed_lib.json rename {drivers => features/device_key/source}/DeviceKey.cpp (73%) rename {drivers => features/device_key/source}/DeviceKey.h (91%) diff --git a/features/device_key/README.md b/features/device_key/README.md new file mode 100644 index 0000000000..e9026811a2 --- /dev/null +++ b/features/device_key/README.md @@ -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``` +``` diff --git a/TESTS/mbed_drivers/device_key/main.cpp b/features/device_key/TESTS/device_key/functionality/main.cpp similarity index 100% rename from TESTS/mbed_drivers/device_key/main.cpp rename to features/device_key/TESTS/device_key/functionality/main.cpp diff --git a/TESTS/host_tests/devicekey_reset.py b/features/device_key/TESTS/host_tests/devicekey_reset.py similarity index 100% rename from TESTS/host_tests/devicekey_reset.py rename to features/device_key/TESTS/host_tests/devicekey_reset.py diff --git a/features/device_key/mbed_lib.json b/features/device_key/mbed_lib.json new file mode 100644 index 0000000000..ccbc10690e --- /dev/null +++ b/features/device_key/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "device_key" +} diff --git a/drivers/DeviceKey.cpp b/features/device_key/source/DeviceKey.cpp similarity index 73% rename from drivers/DeviceKey.cpp rename to features/device_key/source/DeviceKey.cpp index 7742f5fb4f..7d703b6a43 100644 --- a/drivers/DeviceKey.cpp +++ b/features/device_key/source/DeviceKey.cpp @@ -14,12 +14,13 @@ * limitations under the License. */ -#include "drivers/DeviceKey.h" +#include "DeviceKey.h" #include "mbedtls/config.h" #include "mbedtls/cmac.h" #include "nvstore.h" #include "trng_api.h" #include "mbed_wait_api.h" +#include "stdlib.h" #if !defined(MBEDTLS_CMAC_C) #error [NOT_SUPPORTED] MBEDTLS_CMAC_C needs to be enabled for this driver @@ -29,13 +30,21 @@ namespace mbed { -static void buffer_zeroize(void *v, size_t n) -{ - volatile unsigned char *p = (volatile unsigned char *)v; - while ( n-- ) { - *p++ = 0; - } -} +#define DEVKEY_WRITE_UINT32_LE( dst, src ) \ + do \ + { \ + (dst)[0] = ( (src) >> 0 ) & 0xFF; \ + (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() { @@ -145,12 +154,19 @@ int DeviceKey::read_key_from_nvstore(uint32_t *output, size_t& size) return DEVICEKEY_SUCCESS; } -// Calculate CMAC functions - wrapper for mbedtls start/update and finish -int DeviceKey::calculate_cmac(const unsigned char *input, size_t isize, uint32_t *ikey_buff, int ikey_size, - unsigned char *output) +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) { + //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; + size_t counter = 0; + char separator = 0x00; 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; 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; } - ret = mbedtls_cipher_cmac_starts(&ctx, (unsigned char *)ikey_buff, ikey_size * 8); - if (ret != 0) { - goto finish; - } + do { - ret = mbedtls_cipher_cmac_update(&ctx, input, isize); - if (ret != 0) { - goto finish; - } + ret = mbedtls_cipher_cmac_starts(&ctx, (unsigned char *)ikey_buff, ikey_size * 8); + if (ret != 0) { + goto finish; + } - ret = mbedtls_cipher_cmac_finish(&ctx, output); - if (ret != 0) { - goto finish; - } + DEVKEY_WRITE_UINT8_LE(counter_enc, (counter+1)); - 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: 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) { return DEVICEKEY_ERR_CMAC_GENERIC_FAILURE; diff --git a/drivers/DeviceKey.h b/features/device_key/source/DeviceKey.h similarity index 91% rename from drivers/DeviceKey.h rename to features/device_key/source/DeviceKey.h index 192330c363..0f4cdfb6b2 100644 --- a/drivers/DeviceKey.h +++ b/features/device_key/source/DeviceKey.h @@ -94,16 +94,6 @@ private: // Private constructor, as class is a singleton 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 * @param output buffer for the returned key. * @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); - /** 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_size size of the input key. Must be 16 bytes or 32 bytes. * @param isalt input buffer contain some string. From 9df32d1f00e895a172610e46d89d79a6dfc32aed Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Wed, 9 May 2018 11:22:15 +0300 Subject: [PATCH 06/17] Fix README.md grammar mistakes --- features/device_key/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/device_key/README.md b/features/device_key/README.md index e9026811a2..0e7ccd608a 100644 --- a/features/device_key/README.md +++ b/features/device_key/README.md @@ -17,7 +17,7 @@ The characteristics required by this root of trust are: 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. +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 @@ -25,7 +25,7 @@ The root of trust is generated at the first use of Devicekey if the true random #### 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). +`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 Number Generator (`DEVICE_TRNG` is not defined). #### Using DeviceKey @@ -39,7 +39,7 @@ To instantiate DeviceKey, you need to call its `get_instance` member function as #### Testing DeviceKey -Run the DeviceKey functionality test with the `mbed` command as following: +Run the DeviceKey functionality test with the `mbed` command as follows: ``` ```mbed test -n features-device_key-tests-device_key-functionality``` From 73d1c8df93c67ea9e16f5bf1a188eb449a983693 Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Mon, 14 May 2018 13:52:20 +0300 Subject: [PATCH 07/17] Fix of error macros --- features/device_key/source/DeviceKey.cpp | 6 +++--- features/device_key/source/DeviceKey.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/features/device_key/source/DeviceKey.cpp b/features/device_key/source/DeviceKey.cpp index 7d703b6a43..c61c9d626e 100644 --- a/features/device_key/source/DeviceKey.cpp +++ b/features/device_key/source/DeviceKey.cpp @@ -122,7 +122,7 @@ int DeviceKey::write_key_to_nvstore(uint32_t *input, size_t isize) } if (NVSTORE_SUCCESS != ret) { - return DEVICEKEY_NVSTORE_UNPREDICTABLE_ERROR; + return DEVICEKEY_NVSTORE_UNPREDICTED_ERROR; } return DEVICEKEY_SUCCESS; @@ -147,7 +147,7 @@ int DeviceKey::read_key_from_nvstore(uint32_t *output, size_t& size) } if (NVSTORE_SUCCESS != nvStatus) { - return DEVICEKEY_NVSTORE_UNPREDICTABLE_ERROR; + return DEVICEKEY_NVSTORE_UNPREDICTED_ERROR; } size = out_size; @@ -242,7 +242,7 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) memset(output, 0, size); if (DEVICE_KEY_16BYTE > size) { - return DEVICEKEY_BUFFER_TO_SMALL; + return DEVICEKEY_BUFFER_TOO_SMALL; } else if (DEVICE_KEY_16BYTE <= size && DEVICE_KEY_32BYTE > size) { size = DEVICE_KEY_16BYTE; } else { diff --git a/features/device_key/source/DeviceKey.h b/features/device_key/source/DeviceKey.h index 0f4cdfb6b2..301d288763 100644 --- a/features/device_key/source/DeviceKey.h +++ b/features/device_key/source/DeviceKey.h @@ -36,9 +36,9 @@ enum DeviceKeyStatus { DEVICEKEY_ALREADY_EXIST = -4, DEVICEKEY_NOT_FOUND = -5, DEVICEKEY_READ_FAILED = -6, - DEVICEKEY_NVSTORE_UNPREDICTABLE_ERROR = -7, + DEVICEKEY_NVSTORE_UNPREDICTED_ERROR = -7, DEVICEKEY_ERR_CMAC_GENERIC_FAILURE = -8, - DEVICEKEY_BUFFER_TO_SMALL = -9, + DEVICEKEY_BUFFER_TOO_SMALL = -9, DEVICEKEY_NO_KEY_INJECTED = -10, DEVICEKEY_INVALID_PARAM = -11, DEVICEKEY_TRNG_ERROR = -12, From 789eb04617d97d342c562101a6147d58e62bcd95 Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Mon, 14 May 2018 14:07:57 +0300 Subject: [PATCH 08/17] Changed trng loop condition --- features/device_key/source/DeviceKey.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/features/device_key/source/DeviceKey.cpp b/features/device_key/source/DeviceKey.cpp index c61c9d626e..0d9751ccd0 100644 --- a/features/device_key/source/DeviceKey.cpp +++ b/features/device_key/source/DeviceKey.cpp @@ -253,7 +253,7 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) final_size = 0; in_size = size; - while (true) { + while (DEVICEKEY_SUCCESS == ret && final_size < size) { ongoing_size = 0; ret = trng_get_bytes(&trng_obj, (unsigned char *)pBuffer, in_size, &ongoing_size); @@ -263,11 +263,6 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) goto finish; } - if (DEVICEKEY_SUCCESS == ret && final_size == size) { - break; - } - - wait_ms(5); pBuffer += ongoing_size; in_size -= ongoing_size; } From 1cb43fa020204057398ee3e311f7419d805d3256 Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Mon, 14 May 2018 20:01:47 +0300 Subject: [PATCH 09/17] Fixing trng_get_bytes return status --- features/device_key/TESTS/device_key/functionality/main.cpp | 1 - features/device_key/source/DeviceKey.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/features/device_key/TESTS/device_key/functionality/main.cpp b/features/device_key/TESTS/device_key/functionality/main.cpp index ee7a38ee90..fc385e3fec 100644 --- a/features/device_key/TESTS/device_key/functionality/main.cpp +++ b/features/device_key/TESTS/device_key/functionality/main.cpp @@ -49,7 +49,6 @@ int inject_dummy_rot_key() #if !defined(DEVICE_TRNG) uint32_t key[DEVICE_KEY_16BYTE / sizeof(uint32_t)]; - memset(key, 0, DEVICE_KEY_16BYTE); memcpy(key, "1234567812345678", DEVICE_KEY_16BYTE); int size = DEVICE_KEY_16BYTE; DeviceKey& devkey = DeviceKey::get_instance(); diff --git a/features/device_key/source/DeviceKey.cpp b/features/device_key/source/DeviceKey.cpp index 0d9751ccd0..c07b528dcb 100644 --- a/features/device_key/source/DeviceKey.cpp +++ b/features/device_key/source/DeviceKey.cpp @@ -253,12 +253,12 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) final_size = 0; in_size = size; - while (DEVICEKEY_SUCCESS == ret && final_size < size) { + while (final_size < size) { ongoing_size = 0; ret = trng_get_bytes(&trng_obj, (unsigned char *)pBuffer, in_size, &ongoing_size); final_size += ongoing_size; - if (DEVICEKEY_SUCCESS != ret) { + if (0 != ret) { ret = DEVICEKEY_TRNG_ERROR; goto finish; } From efc19040ea624b124d65c6aedec413c07dd4fef6 Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Wed, 21 Feb 2018 11:48:13 +0200 Subject: [PATCH 10/17] Device key implementation --- features/device_key/README.md | 46 ++ .../TESTS/device_key/functionality/main.cpp | 491 ++++++++++++++++++ .../TESTS/host_tests/devicekey_reset.py | 150 ++++++ features/device_key/mbed_lib.json | 3 + features/device_key/source/DeviceKey.cpp | 287 ++++++++++ features/device_key/source/DeviceKey.h | 142 +++++ .../mbedtls/inc/mbedtls/config-no-entropy.h | 1 + features/mbedtls/inc/mbedtls/config.h | 2 +- features/nvstore/source/nvstore.h | 2 + 9 files changed, 1123 insertions(+), 1 deletion(-) create mode 100644 features/device_key/README.md create mode 100644 features/device_key/TESTS/device_key/functionality/main.cpp create mode 100644 features/device_key/TESTS/host_tests/devicekey_reset.py create mode 100644 features/device_key/mbed_lib.json create mode 100644 features/device_key/source/DeviceKey.cpp create mode 100644 features/device_key/source/DeviceKey.h diff --git a/features/device_key/README.md b/features/device_key/README.md new file mode 100644 index 0000000000..0e7ccd608a --- /dev/null +++ b/features/device_key/README.md @@ -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 Number 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 follows: + +``` + ```mbed test -n features-device_key-tests-device_key-functionality``` +``` diff --git a/features/device_key/TESTS/device_key/functionality/main.cpp b/features/device_key/TESTS/device_key/functionality/main.cpp new file mode 100644 index 0000000000..fc385e3fec --- /dev/null +++ b/features/device_key/TESTS/device_key/functionality/main.cpp @@ -0,0 +1,491 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DeviceKey.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "nvstore.h" + +#if !NVSTORE_ENABLED +#error [NOT_SUPPORTED] NVSTORE needs to be enabled for this test +#endif + +using namespace utest::v1; + +#define MSG_VALUE_DUMMY "0" +#define MSG_VALUE_LEN 32 +#define MSG_KEY_LEN 32 + +#define MSG_KEY_DEVICE_READY "ready" +#define MSG_KEY_DEVICE_FINISH "finish" +#define MSG_KEY_DEVICE_TEST_STEP1 "check_consistency_step1" +#define MSG_KEY_DEVICE_TEST_STEP2 "check_consistency_step2" +#define MSG_KEY_DEVICE_TEST_STEP3 "check_consistency_step3" +#define MSG_KEY_DEVICE_TEST_STEP4 "check_consistency_step4" +#define MSG_KEY_DEVICE_TEST_SUITE_ENDED "Test suite ended" + +void generate_derived_key_consistency_16_byte_key_reset_test(char *key); +void generate_derived_key_consistency_32_byte_key_reset_test(char *key); + +/* + * Injection of a dummy key when there is no TRNG + */ +int inject_dummy_rot_key() +{ +#if !defined(DEVICE_TRNG) + uint32_t key[DEVICE_KEY_16BYTE / sizeof(uint32_t)]; + + memcpy(key, "1234567812345678", DEVICE_KEY_16BYTE); + int size = DEVICE_KEY_16BYTE; + DeviceKey& devkey = DeviceKey::get_instance(); + return devkey.device_inject_root_of_trust(key, size); +#else + return DEVICEKEY_SUCCESS; +#endif +} + +void generate_derived_key_reset_test() +{ + greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY); + + static char key[MSG_KEY_LEN + 1] = { }; + static char value[MSG_VALUE_LEN + 1] = { }; + memset(key, 0, MSG_KEY_LEN + 1); + memset(value, 0, MSG_VALUE_LEN + 1); + + greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); + + if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP1) == 0 || strcmp(key, MSG_KEY_DEVICE_TEST_STEP2) == 0) { + generate_derived_key_consistency_16_byte_key_reset_test(key); + return generate_derived_key_reset_test(); + } + + if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP3) == 0 || strcmp(key, MSG_KEY_DEVICE_TEST_STEP4) == 0) { + return generate_derived_key_consistency_32_byte_key_reset_test(key); + } + + TEST_ASSERT_MESSAGE(false, key); //Indicates error!!! +} + +/* + * Test the consistency of derived 16 byte key result after device reset. + */ +void generate_derived_key_consistency_16_byte_key_reset_test(char *key) +{ + unsigned char output1[DEVICE_KEY_16BYTE]; + unsigned char output2[DEVICE_KEY_16BYTE]; + unsigned char empty_buffer[DEVICE_KEY_16BYTE]; + unsigned char salt[] = "Once upon a time, I worked for the circus and I lived in Omaha."; + int key_type = DEVICE_KEY_16BYTE; + uint16_t actual_size = 0; + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + size_t salt_size = sizeof(salt); + + if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP1) == 0) { + + //Third step: Clear NVStore, create an ROT key, derive a 16 byte + //key and store it in NVStore at index 15, At the end reset the device + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memset(output1, 0, sizeof(output1)); + ret = devkey.generate_derived_key(salt, salt_size, output1, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + bool is_empty = !memcmp(empty_buffer, output1, sizeof(output1)); + TEST_ASSERT_FALSE(is_empty); + + ret = nvstore.set(15, DEVICE_KEY_16BYTE, output1); + TEST_ASSERT_EQUAL_INT32(0, ret); + + system_reset(); + TEST_ASSERT_MESSAGE(false, "system_reset() did not reset the device as expected."); + } else if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP2) == 0) { + + //Second step: Read from NVStore at index 15 there should be a derived key there. + //Now try to derive a key for 100 times and check it is the same key like before the reset. + //At the end clear NVStore. + int ret = nvstore.get(15, DEVICE_KEY_16BYTE, output1, actual_size); + TEST_ASSERT_FALSE(NVSTORE_SUCCESS != ret) + TEST_ASSERT_EQUAL_INT(DEVICE_KEY_16BYTE, actual_size); + + for (int i = 0; i < 100; i++) { + memset(output2, 0, sizeof(output2)); + ret = devkey.generate_derived_key(salt, salt_size, output2, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY(output1, output2, DEVICE_KEY_16BYTE); + } + + ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + greentea_send_kv(MSG_KEY_DEVICE_FINISH, MSG_VALUE_DUMMY); + } else { + TEST_ASSERT_MESSAGE(false, "Unknown test step received"); + } +} + +/* + * Test the consistency of derived 32 byte key result after device reset. + */ +void generate_derived_key_consistency_32_byte_key_reset_test(char *key) +{ + unsigned char output1[DEVICE_KEY_32BYTE]; + unsigned char output2[DEVICE_KEY_32BYTE]; + unsigned char empty_buffer[DEVICE_KEY_32BYTE]; + unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; + int key_type = DEVICE_KEY_32BYTE; + uint16_t actual_size = 0; + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + size_t salt_size = sizeof(salt); + + if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP3) == 0) { + + //Third step: Clear NVStore, create an ROT key, derive a 32 byte + //key and store it in NVStore at index 15, At the end reset the device + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memset(output1, 0, sizeof(output1)); + ret = devkey.generate_derived_key(salt, salt_size, output1, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + bool is_empty = !memcmp(empty_buffer, output1, sizeof(output1)); + TEST_ASSERT_FALSE(is_empty); + + ret = nvstore.set(15, DEVICE_KEY_32BYTE, output1); + TEST_ASSERT_EQUAL_INT32(0, ret); + + system_reset(); + TEST_ASSERT_MESSAGE(false, "system_reset() did not reset the device as expected."); + } else if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP4) == 0) { + + //Fourth step: Read from NVStore at index 15 there should be a derived key there. + //Now try to derive a key for 100 times and check it is the same key like before the reset. + //At the end clear NVStore. + int ret = nvstore.get(15, DEVICE_KEY_32BYTE, output1, actual_size); + TEST_ASSERT_FALSE(NVSTORE_SUCCESS != ret) + TEST_ASSERT_EQUAL_INT(DEVICE_KEY_32BYTE, actual_size); + + for (int i = 0; i < 100; i++) { + memset(output2, 0, sizeof(output2)); + ret = devkey.generate_derived_key(salt, salt_size, output2, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY(output1, output2, DEVICE_KEY_32BYTE); + } + + ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + greentea_send_kv(MSG_KEY_DEVICE_FINISH, MSG_VALUE_DUMMY); + } else { + TEST_ASSERT_MESSAGE(false, "Unknown test step received"); + } +} + +/* + * Test that wrong size of key is rejected when trying to persist a key + */ +void device_inject_root_of_trust_wrong_size_test() +{ + DeviceKey& devkey = DeviceKey::get_instance(); + uint32_t key[DEVICE_KEY_32BYTE / sizeof(uint32_t)]; + + memcpy(key, "12345678123456788765432187654321", DEVICE_KEY_32BYTE); + + for (int i = 0; i < 50; i++) { + if (DEVICE_KEY_16BYTE == i || DEVICE_KEY_32BYTE == i) { + continue; + } + int ret = devkey.device_inject_root_of_trust(key, i); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_INVALID_KEY_SIZE, ret); + } +} + +/* + * Test that a 16 byte size key is written to persistent storage + */ +void device_inject_root_of_trust_16_byte_size_test() +{ + DeviceKey& devkey = DeviceKey::get_instance(); + uint32_t rkey[DEVICE_KEY_16BYTE / sizeof(uint32_t)]; + uint16_t actual_size; + uint32_t key[DEVICE_KEY_16BYTE / sizeof(uint32_t)]; + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memcpy(key, "1234567812345678", sizeof(key)); + ret = devkey.device_inject_root_of_trust(key, DEVICE_KEY_16BYTE); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + //Read the key from NVStore. + memset(rkey, 0, sizeof(rkey)); + ret = nvstore.get(NVSTORE_DEVICEKEY_KEY, DEVICE_KEY_16BYTE, rkey, actual_size); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + TEST_ASSERT_EQUAL_INT(DEVICE_KEY_16BYTE, actual_size); + TEST_ASSERT_EQUAL_INT32_ARRAY(key, rkey, actual_size / sizeof(uint32_t)); +} + +/* + * Test that a 32 byte size key is written to persistent storage + */ +void device_inject_root_of_trust_32_byte_size_test() +{ + DeviceKey& devkey = DeviceKey::get_instance(); + uint32_t rkey[DEVICE_KEY_32BYTE / sizeof(uint32_t)]; + uint16_t actual_size; + uint32_t key[DEVICE_KEY_32BYTE / sizeof(uint32_t)]; + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memcpy(key, "12345678123456788765432187654321", sizeof(key)); + ret = devkey.device_inject_root_of_trust(key, DEVICE_KEY_32BYTE); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + //Read the key from NVStore. + memset(rkey, 0, sizeof(rkey)); + ret = nvstore.get(NVSTORE_DEVICEKEY_KEY, DEVICE_KEY_32BYTE, rkey, actual_size); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + TEST_ASSERT_EQUAL_INT(DEVICE_KEY_32BYTE, actual_size); + TEST_ASSERT_EQUAL_INT32_ARRAY(key, rkey, actual_size / sizeof(uint32_t)); +} + +/* + * Test that a key can be written to persistent storage only once. + */ +void device_inject_root_of_trust_several_times_test() +{ + DeviceKey& devkey = DeviceKey::get_instance(); + uint32_t key[DEVICE_KEY_32BYTE / sizeof(uint32_t)]; + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memcpy(key, "12345678123456788765432187654321", DEVICE_KEY_32BYTE); + ret = devkey.device_inject_root_of_trust(key, DEVICE_KEY_32BYTE); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + //Trying to use the same key should fail. + ret = devkey.device_inject_root_of_trust(key, DEVICE_KEY_32BYTE); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_ALREADY_EXIST, ret); + + //Trying to use different key should also fail. + ret = devkey.device_inject_root_of_trust(key, DEVICE_KEY_16BYTE); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_ALREADY_EXIST, ret); +} + +/* + * Test the consistency of derived 16 byte key result. + */ +void generate_derived_key_consistency_16_byte_key_test() +{ + unsigned char output1[DEVICE_KEY_16BYTE]; + unsigned char output2[DEVICE_KEY_16BYTE]; + unsigned char empty_buffer[DEVICE_KEY_16BYTE]; + unsigned char salt[] = "Once upon a time, I worked for the circus and I lived in Omaha."; + int key_type = DEVICE_KEY_16BYTE; + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + size_t salt_size = sizeof(salt); + memset(output1, 0, sizeof(output1)); + ret = devkey.generate_derived_key(salt, salt_size, output1, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + bool is_empty = !memcmp(empty_buffer, output1, sizeof(output1)); + TEST_ASSERT_FALSE(is_empty); + + for (int i = 0; i < 100; i++) { + memset(output2, 0, sizeof(output2)); + ret = devkey.generate_derived_key(salt, salt_size, output2, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY(output1, output2, DEVICE_KEY_16BYTE); + } +} + +/* + * Test the consistency of derived 32 byte key result. + */ +void generate_derived_key_consistency_32_byte_key_test() +{ + unsigned char output1[DEVICE_KEY_32BYTE]; + unsigned char output2[DEVICE_KEY_32BYTE]; + unsigned char empty_buffer[DEVICE_KEY_32BYTE]; + unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; + int key_type = DEVICE_KEY_32BYTE; + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + size_t salt_size = sizeof(salt); + memset(output1, 0, sizeof(output1)); + ret = devkey.generate_derived_key(salt, salt_size, output1, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + bool is_empty = !memcmp(empty_buffer, output1, sizeof(output1)); + TEST_ASSERT_FALSE(is_empty); + + for (int i = 0; i < 100; i++) { + memset(output2, 0, sizeof(output2)); + ret = devkey.generate_derived_key(salt, salt_size, output2, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY(output1, output2, DEVICE_KEY_32BYTE); + } +} + +/* + * Test request for 16 byte key is returning a correct key size. + */ +void generate_derived_key_key_type_16_test() +{ + unsigned char output[DEVICE_KEY_16BYTE * 2]; + unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; + unsigned char expectedString[] = "Some String"; + int key_type = DEVICE_KEY_16BYTE; + size_t salt_size = sizeof(salt); + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memset(output, 0, DEVICE_KEY_16BYTE * 2); + memcpy(output + DEVICE_KEY_16BYTE - sizeof(expectedString), expectedString, sizeof(expectedString)); + memcpy(output + DEVICE_KEY_16BYTE + 1, expectedString, sizeof(expectedString)); + + ret = devkey.generate_derived_key(salt, salt_size, output, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + //Test that we didn't override the buffer after the 16 byte size + TEST_ASSERT_EQUAL_UINT8_ARRAY(output + DEVICE_KEY_16BYTE + 1, expectedString, sizeof(expectedString)); + //Test that we did override the buffer all 16 byte + TEST_ASSERT(memcmp(output + DEVICE_KEY_16BYTE - sizeof(expectedString), expectedString, sizeof(expectedString)) != 0); +} + +/* + * Test request for 32 byte key is returning a correct key size. + */ +void generate_derived_key_key_type_32_test() +{ + unsigned char output[DEVICE_KEY_32BYTE * 2]; + unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; + int key_type = DEVICE_KEY_32BYTE; + size_t salt_size = sizeof(salt); + unsigned char expectedString[] = "Some String"; + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memset(output, 0, DEVICE_KEY_32BYTE * 2); + memcpy(output + DEVICE_KEY_32BYTE - sizeof(expectedString), expectedString, sizeof(expectedString)); + memcpy(output + DEVICE_KEY_32BYTE + 1, expectedString, sizeof(expectedString)); + + ret = devkey.generate_derived_key(salt, salt_size, output, key_type); + TEST_ASSERT_EQUAL_INT32(0, ret); + //Test that we didn't override the buffer after the 32 byte size + TEST_ASSERT_EQUAL_UINT8_ARRAY(output + DEVICE_KEY_32BYTE + 1, expectedString, sizeof(expectedString)); + //Test that we did override the buffer all 32 byte + TEST_ASSERT(memcmp(output + DEVICE_KEY_32BYTE - sizeof(expectedString), expectedString, sizeof(expectedString)) != 0); +} + +/* + * Test request for unknown key size returns an error + */ +void generate_derived_key_wrong_key_type_test() +{ + unsigned char output[DEVICE_KEY_16BYTE]; + unsigned char salt[] = "The quick brown fox jumps over the lazy dog"; + size_t salt_size = sizeof(salt); + DeviceKey& devkey = DeviceKey::get_instance(); + NVStore& nvstore = NVStore::get_instance(); + + nvstore.init(); + int ret = nvstore.reset(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + ret = inject_dummy_rot_key(); + TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); + + memset(output, 0, DEVICE_KEY_32BYTE); + ret = devkey.generate_derived_key(salt, salt_size, output, 12);//96 bit key type is not supported + TEST_ASSERT_EQUAL_INT32(DEVICEKEY_INVALID_KEY_TYPE, ret); + +} + +utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason) +{ + greentea_case_failure_abort_handler(source, reason); + return STATUS_CONTINUE; +} + +//Currently there can be only one test that contains reset and it has to be the first test! +Case cases[] = { + Case("Device Key - derived key reset", generate_derived_key_reset_test, greentea_failure_handler), + Case("Device Key - inject value wrong size", device_inject_root_of_trust_wrong_size_test, greentea_failure_handler), + Case("Device Key - inject value 16 byte size", device_inject_root_of_trust_16_byte_size_test, greentea_failure_handler), + Case("Device Key - inject value 32 byte size", device_inject_root_of_trust_32_byte_size_test, greentea_failure_handler), + Case("Device Key - inject value several times", device_inject_root_of_trust_several_times_test, greentea_failure_handler), + Case("Device Key - derived key consistency 16 byte key", generate_derived_key_consistency_16_byte_key_test, greentea_failure_handler), + Case("Device Key - derived key consistency 32 byte key", generate_derived_key_consistency_32_byte_key_test, greentea_failure_handler), + Case("Device Key - derived key key type 16", generate_derived_key_key_type_16_test, greentea_failure_handler), + Case("Device Key - derived key key type 32", generate_derived_key_key_type_32_test, greentea_failure_handler), + Case("Device Key - derived key wrong key type", generate_derived_key_wrong_key_type_test, greentea_failure_handler) +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(14, "devicekey_reset"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + bool ret = Harness::run(specification); + greentea_send_kv(MSG_KEY_DEVICE_TEST_SUITE_ENDED, MSG_VALUE_DUMMY); + + return ret; +} + diff --git a/features/device_key/TESTS/host_tests/devicekey_reset.py b/features/device_key/TESTS/host_tests/devicekey_reset.py new file mode 100644 index 0000000000..08379739cb --- /dev/null +++ b/features/device_key/TESTS/host_tests/devicekey_reset.py @@ -0,0 +1,150 @@ +""" +Copyright (c) 2018 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import time +from mbed_host_tests import BaseHostTest +from mbed_host_tests.host_tests_runner.host_test_default import DefaultTestSelector + +DEFAULT_CYCLE_PERIOD = 1.0 +MSG_VALUE_DUMMY = '0' +MSG_KEY_DEVICE_READY = 'ready' +MSG_KEY_DEVICE_FINISH = 'finish' +MSG_KEY_DEVICE_TEST_STEP1 = 'check_consistency_step1' +MSG_KEY_DEVICE_TEST_STEP2 = 'check_consistency_step2' +MSG_KEY_DEVICE_TEST_STEP3 = 'check_consistency_step3' +MSG_KEY_DEVICE_TEST_STEP4 = 'check_consistency_step4' +MSG_KEY_SYNC = '__sync' +MSG_KEY_TEST_SUITE_ENDED = 'Test suite ended' + +class DeviceKeyResetTest(BaseHostTest): + """Test for the DeviceKey driver API. + """ + + def __init__(self): + super(DeviceKeyResetTest, self).__init__() + self.reset = False + self.finish = False + self.suite_ended = False + cycle_s = self.get_config_item('program_cycle_s') + self.program_cycle_s = cycle_s if cycle_s is not None else DEFAULT_CYCLE_PERIOD + self.test_steps_sequence = self.test_steps() + # Advance the coroutine to it's first yield statement. + self.test_steps_sequence.send(None) + + def setup(self): + self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready) + self.register_callback(MSG_KEY_DEVICE_FINISH, self.cb_device_finish) + self.register_callback(MSG_KEY_TEST_SUITE_ENDED, self.cb_device_test_suit_ended) + + def cb_device_ready(self, key, value, timestamp): + """Acknowledge device rebooted correctly and feed the test execution + """ + self.reset = True + + try: + if self.test_steps_sequence.send(value): + self.notify_complete(True) + except (StopIteration, RuntimeError) as exc: + self.notify_complete(False) + + def cb_device_finish(self, key, value, timestamp): + """Acknowledge device finished a test step correctly and feed the test execution + """ + self.finish = True + + try: + if self.test_steps_sequence.send(value): + self.notify_complete(True) + except (StopIteration, RuntimeError) as exc: + self.notify_complete(False) + + def cb_device_test_suit_ended(self, key, value, timestamp): + """Acknowledge device finished a test step correctly and feed the test execution + """ + self.suite_ended = True + + try: + if self.test_steps_sequence.send(value): + self.notify_complete(True) + except (StopIteration, RuntimeError) as exc: + self.notify_complete(False) + + def test_steps(self): + """Test step 1 (16 byte key test) + """ + wait_for_communication = yield + + self.reset = False + self.send_kv(MSG_KEY_DEVICE_TEST_STEP1, MSG_VALUE_DUMMY) + time.sleep(self.program_cycle_s) + self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY) + + wait_for_communication = yield + + if self.reset == False: + raise RuntimeError('Phase 1: Platform did not reset as expected.') + + """Test step 2 (After reset) + """ + self.finish = False + self.send_kv(MSG_KEY_DEVICE_TEST_STEP2, MSG_VALUE_DUMMY) + time.sleep(self.program_cycle_s) + wait_for_communication = yield + + if self.finish == False: + raise RuntimeError('Test failed.') + + """Test Step 3 (32 byte key test) + """ + wait_for_communication = yield + + self.reset = False + self.send_kv(MSG_KEY_DEVICE_TEST_STEP3, MSG_VALUE_DUMMY) + time.sleep(self.program_cycle_s) + self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY) + + wait_for_communication = yield + + if self.reset == False: + raise RuntimeError('Phase 3: Platform did not reset as expected.') + + """Test step 4 (After reset) + """ + self.finish = False + self.send_kv(MSG_KEY_DEVICE_TEST_STEP4, MSG_VALUE_DUMMY) + time.sleep(self.program_cycle_s) + + wait_for_communication = yield + + if self.finish == False: + raise RuntimeError('Test failed.') + + """Test step 4 (After reset) + """ + self.suite_ended = False + time.sleep(self.program_cycle_s) + + wait_for_communication = yield + + if self.suite_ended == False: + raise RuntimeError('Test failed.') + + # The sequence is correct -- test passed. + yield True + + + + \ No newline at end of file diff --git a/features/device_key/mbed_lib.json b/features/device_key/mbed_lib.json new file mode 100644 index 0000000000..ccbc10690e --- /dev/null +++ b/features/device_key/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "device_key" +} diff --git a/features/device_key/source/DeviceKey.cpp b/features/device_key/source/DeviceKey.cpp new file mode 100644 index 0000000000..c07b528dcb --- /dev/null +++ b/features/device_key/source/DeviceKey.cpp @@ -0,0 +1,287 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DeviceKey.h" +#include "mbedtls/config.h" +#include "mbedtls/cmac.h" +#include "nvstore.h" +#include "trng_api.h" +#include "mbed_wait_api.h" +#include "stdlib.h" + +#if !defined(MBEDTLS_CMAC_C) +#error [NOT_SUPPORTED] MBEDTLS_CMAC_C needs to be enabled for this driver +#else + +#if NVSTORE_ENABLED + +namespace mbed { + +#define DEVKEY_WRITE_UINT32_LE( dst, src ) \ + do \ + { \ + (dst)[0] = ( (src) >> 0 ) & 0xFF; \ + (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() +{ + return; +} + +DeviceKey::~DeviceKey() +{ + return; +} + +int DeviceKey::generate_derived_key(const unsigned char *salt, size_t isalt_size, unsigned char *output, + uint16_t ikey_type) +{ + uint32_t key_buff[DEVICE_KEY_32BYTE / sizeof(uint32_t)]; + size_t actual_size = DEVICE_KEY_32BYTE; + + if (DEVICE_KEY_16BYTE != ikey_type && DEVICE_KEY_32BYTE != ikey_type) { + return DEVICEKEY_INVALID_KEY_TYPE; + } + + //First try to read the key from NVStore + int ret = read_key_from_nvstore(key_buff, actual_size); + if (DEVICEKEY_SUCCESS != ret && DEVICEKEY_NOT_FOUND != ret) { + return ret; + } + + if (DEVICE_KEY_16BYTE != actual_size && DEVICE_KEY_32BYTE != actual_size) { + return DEVICEKEY_READ_FAILED; + } + + //If the key was not found in NVStore we will create it by using TRNG and then save it to NVStore + if (DEVICEKEY_NOT_FOUND == ret) { + ret = generate_key_by_trng(key_buff, actual_size); + if (DEVICEKEY_SUCCESS != ret) { + return ret; + } + + ret = device_inject_root_of_trust(key_buff, actual_size); + if (DEVICEKEY_SUCCESS != ret) { + return ret; + } + } + + ret = get_derived_key(key_buff, actual_size, salt, isalt_size, output, ikey_type); + return ret; +} + +int DeviceKey::device_inject_root_of_trust(uint32_t *value, size_t isize) +{ + return write_key_to_nvstore(value, isize); +} + +int DeviceKey::write_key_to_nvstore(uint32_t *input, size_t isize) +{ + if (DEVICE_KEY_16BYTE != isize && DEVICE_KEY_32BYTE != isize) { + return DEVICEKEY_INVALID_KEY_SIZE; + } + + //First we read if key exist. If it is exists, we return DEVICEKEY_ALREADY_EXIST error + uint32_t read_key[DEVICE_KEY_32BYTE / sizeof(uint32_t)] = {0}; + size_t read_size = DEVICE_KEY_32BYTE; + int ret = read_key_from_nvstore(read_key, read_size); + if (DEVICEKEY_SUCCESS == ret) { + return DEVICEKEY_ALREADY_EXIST; + } + if (DEVICEKEY_NOT_FOUND != ret) { + return ret; + } + + NVStore& nvstore = NVStore::get_instance(); + ret = nvstore.set(NVSTORE_DEVICEKEY_KEY, (uint16_t)isize, input); + if (NVSTORE_WRITE_ERROR == ret || NVSTORE_BUFF_TOO_SMALL == ret) { + return DEVICEKEY_SAVE_FAILED; + } + + if (NVSTORE_SUCCESS != ret) { + return DEVICEKEY_NVSTORE_UNPREDICTED_ERROR; + } + + return DEVICEKEY_SUCCESS; +} + +int DeviceKey::read_key_from_nvstore(uint32_t *output, size_t& size) +{ + if (size > UINT16_MAX) { + return DEVICEKEY_INVALID_PARAM; + } + + uint16_t in_size = size; + uint16_t out_size = 0; + NVStore& nvstore = NVStore::get_instance(); + int nvStatus = nvstore.get(NVSTORE_DEVICEKEY_KEY, in_size, output, out_size); + if (NVSTORE_NOT_FOUND == nvStatus) { + return DEVICEKEY_NOT_FOUND; + } + + if (NVSTORE_READ_ERROR == nvStatus || NVSTORE_BUFF_TOO_SMALL == nvStatus) { + return DEVICEKEY_READ_FAILED; + } + + if (NVSTORE_SUCCESS != nvStatus) { + return DEVICEKEY_NVSTORE_UNPREDICTED_ERROR; + } + + size = out_size; + return DEVICEKEY_SUCCESS; +} + +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) +{ + //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; + size_t counter = 0; + char separator = 0x00; + 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; + if (DEVICE_KEY_32BYTE == ikey_size) { + mbedtls_cipher_type = MBEDTLS_CIPHER_AES_256_ECB; + } + + const mbedtls_cipher_info_t *cipher_info = mbedtls_cipher_info_from_type(mbedtls_cipher_type); + + mbedtls_cipher_init(&ctx); + ret = mbedtls_cipher_setup(&ctx, cipher_info); + if (ret != 0) { + goto finish; + } + + do { + + ret = mbedtls_cipher_cmac_starts(&ctx, (unsigned char *)ikey_buff, ikey_size * 8); + if (ret != 0) { + goto finish; + } + + DEVKEY_WRITE_UINT8_LE(counter_enc, (counter+1)); + + 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: + mbedtls_cipher_free( &ctx ); + + if (DEVICEKEY_SUCCESS != ret) { + return DEVICEKEY_ERR_CMAC_GENERIC_FAILURE; + } + + return DEVICEKEY_SUCCESS; +} + +int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) +{ +#if defined(DEVICE_TRNG) + size_t in_size; + size_t ongoing_size; + size_t final_size; + trng_t trng_obj; + int ret = DEVICEKEY_SUCCESS; + unsigned char *pBuffer = (unsigned char *)output; + + memset(output, 0, size); + + if (DEVICE_KEY_16BYTE > size) { + return DEVICEKEY_BUFFER_TOO_SMALL; + } else if (DEVICE_KEY_16BYTE <= size && DEVICE_KEY_32BYTE > size) { + size = DEVICE_KEY_16BYTE; + } else { + size = DEVICE_KEY_32BYTE; + } + + trng_init(&trng_obj); + + final_size = 0; + in_size = size; + while (final_size < size) { + + ongoing_size = 0; + ret = trng_get_bytes(&trng_obj, (unsigned char *)pBuffer, in_size, &ongoing_size); + final_size += ongoing_size; + if (0 != ret) { + ret = DEVICEKEY_TRNG_ERROR; + goto finish; + } + + pBuffer += ongoing_size; + in_size -= ongoing_size; + } + + ret = DEVICEKEY_SUCCESS; + +finish: + trng_free(&trng_obj); + size = final_size; + return ret; + +#else + return DEVICEKEY_NO_KEY_INJECTED; +#endif +} + +} // namespace mbed + +#endif //NVSTORE_ENABLED +#endif + + diff --git a/features/device_key/source/DeviceKey.h b/features/device_key/source/DeviceKey.h new file mode 100644 index 0000000000..301d288763 --- /dev/null +++ b/features/device_key/source/DeviceKey.h @@ -0,0 +1,142 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_DEVICEKEY_H +#define MBED_DEVICEKEY_H + +#include "stddef.h" +#include "stdint.h" +#include "platform/NonCopyable.h" + +#if (NVSTORE_ENABLED) + +namespace mbed { +/** \addtogroup drivers */ + +#define DEVICE_KEY_16BYTE 16 +#define DEVICE_KEY_32BYTE 32 + +enum DeviceKeyStatus { + DEVICEKEY_SUCCESS = 0, + DEVICEKEY_INVALID_KEY_SIZE = -1, + DEVICEKEY_INVALID_KEY_TYPE = -2, + DEVICEKEY_SAVE_FAILED = -3, + DEVICEKEY_ALREADY_EXIST = -4, + DEVICEKEY_NOT_FOUND = -5, + DEVICEKEY_READ_FAILED = -6, + DEVICEKEY_NVSTORE_UNPREDICTED_ERROR = -7, + DEVICEKEY_ERR_CMAC_GENERIC_FAILURE = -8, + DEVICEKEY_BUFFER_TOO_SMALL = -9, + DEVICEKEY_NO_KEY_INJECTED = -10, + DEVICEKEY_INVALID_PARAM = -11, + DEVICEKEY_TRNG_ERROR = -12, +}; + +/** Use this singleton if you need to derive a new key from the device root of trust. + * + * @note Synchronization level: Thread safe + * @ingroup drivers + */ + +class DeviceKey : private mbed::NonCopyable { +public: + + /** + * @brief As a singleton, return the single instance of the class. + * Reason for this class being a singleton is the following: + * - Ease the use for users of this class not having to coordinate instantiations. + * - Lazy instantiation of internal data (which we can't achieve with simple static classes). + * + * @returns Singleton instance reference. + */ + static DeviceKey& get_instance() + { + // Use this implementation of singleton (Meyer's) rather than the one that allocates + // the instance on the heap, as it ensures destruction at program end (preventing warnings + // from memory checking tools such as valgrind). + static DeviceKey instance; + return instance; + } + + ~DeviceKey(); + + /** Derive a new key based on the salt string. key type can be with values 16 bytes and 32 bytes + * @param isalt input buffer used to create the new key. Same input will generate always the same key + * @param isalt_size size of the data in salt buffer + * @param output buffer to receive the derived key. Size must be 16 bytes or 32 bytes + * according to the ikey_type parameter + * @param ikey_type type of the required key. Type must be 16 bytes or 32 bytes. + * @return 0 on success, negative error code on failure + */ + int generate_derived_key(const unsigned char *isalt, size_t isalt_size, unsigned char *output, uint16_t ikey_type); + + /** Set a device key into the NVStore. In case TRNG support is missing, Call this method + * before calling device_key_derived_key. This method should be called only once! + * @param value input buffer contain the key. + * @param isize size of the supplied key. Must be 16 bytes or 32 bytes. + * @return 0 on success, negative error code on failure + */ + int device_inject_root_of_trust(uint32_t *value, size_t isize); + +private: + // Private constructor, as class is a singleton + DeviceKey(); + + /** Read a device key from the NVStore + * @param output buffer for the returned key. + * @param size input: the size of the output buffer. + * output: the actual size of the written data + * @return 0 on success, negative error code on failure + */ + int read_key_from_nvstore(uint32_t *output, size_t& size); + + /** Set a device key into the NVStore + * @param input input buffer contain the key. + * @param isize the size of the input buffer. + * @return 0 on success, negative error code on failure + */ + int write_key_to_nvstore(uint32_t *input, size_t isize); + + /** 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_size size of the input key. Must be 16 bytes or 32 bytes. + * @param isalt input buffer contain some string. + * @param isalt_size size of the supplied input string. + * @param output buffer for the derived key result. + * @param ikey_type the requested key size. Must be 16 bytes or 32 bytes. + * @return 0 on success, negative error code on failure + */ + int 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); + + /** Generate a random ROT key by using TRNG + * @param output output buffer for the generated key. + * @param size input: the size of the buffer. if size is less + * then 16 bytes the method will generate an + * error. 16-31 bytes will create a 16 byte key. + * 32 or higher will generate a 32 bytes key + * output: the actual written size to the buffer + * @return 0 on success, negative error code on failure + */ + int generate_key_by_trng(uint32_t *output, size_t& size); + +}; +/** @}*/ + +} + +#endif //NVSTORE_ENABLED +#endif diff --git a/features/mbedtls/inc/mbedtls/config-no-entropy.h b/features/mbedtls/inc/mbedtls/config-no-entropy.h index b4a0930b9c..a0e979f0c2 100644 --- a/features/mbedtls/inc/mbedtls/config-no-entropy.h +++ b/features/mbedtls/inc/mbedtls/config-no-entropy.h @@ -82,6 +82,7 @@ #define MBEDTLS_X509_USE_C #define MBEDTLS_X509_CRT_PARSE_C #define MBEDTLS_X509_CRL_PARSE_C +#define MBEDTLS_CMAC_C /* Miscellaneous options */ #define MBEDTLS_AES_ROM_TABLES diff --git a/features/mbedtls/inc/mbedtls/config.h b/features/mbedtls/inc/mbedtls/config.h index f1a0307ad1..fe174e0c61 100644 --- a/features/mbedtls/inc/mbedtls/config.h +++ b/features/mbedtls/inc/mbedtls/config.h @@ -1861,7 +1861,7 @@ * Requires: MBEDTLS_AES_C or MBEDTLS_DES_C * */ -//#define MBEDTLS_CMAC_C +#define MBEDTLS_CMAC_C /** * \def MBEDTLS_CTR_DRBG_C diff --git a/features/nvstore/source/nvstore.h b/features/nvstore/source/nvstore.h index 91c675e057..39859d3285 100644 --- a/features/nvstore/source/nvstore.h +++ b/features/nvstore/source/nvstore.h @@ -49,6 +49,8 @@ typedef enum { // All predefined keys used for internal features should be defined here + NVSTORE_DEVICEKEY_KEY = 4, + NVSTORE_LAST_PREDEFINED_KEY = 15, NVSTORE_NUM_PREDEFINED_KEYS } nvstore_predefined_keys_e; From d88f4b3084ecbdaa829f7348d61b1f750484d14c Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Wed, 16 May 2018 11:44:45 +0300 Subject: [PATCH 11/17] Stricter parameter check --- features/device_key/source/DeviceKey.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/features/device_key/source/DeviceKey.cpp b/features/device_key/source/DeviceKey.cpp index c07b528dcb..07df0f0715 100644 --- a/features/device_key/source/DeviceKey.cpp +++ b/features/device_key/source/DeviceKey.cpp @@ -243,10 +243,8 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) if (DEVICE_KEY_16BYTE > size) { return DEVICEKEY_BUFFER_TOO_SMALL; - } else if (DEVICE_KEY_16BYTE <= size && DEVICE_KEY_32BYTE > size) { - size = DEVICE_KEY_16BYTE; - } else { - size = DEVICE_KEY_32BYTE; + } else if (DEVICE_KEY_16BYTE != size && DEVICE_KEY_32BYTE != size) { + return DEVICEKEY_INVALID_PARAM; } trng_init(&trng_obj); From bf9b2cb6c7791da18547cfaf8378313c09378c2b Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Wed, 16 May 2018 12:17:20 +0300 Subject: [PATCH 12/17] Fix for generate_key_by_trng --- features/device_key/source/DeviceKey.cpp | 9 ++++----- features/device_key/source/DeviceKey.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/features/device_key/source/DeviceKey.cpp b/features/device_key/source/DeviceKey.cpp index 07df0f0715..16364807d2 100644 --- a/features/device_key/source/DeviceKey.cpp +++ b/features/device_key/source/DeviceKey.cpp @@ -229,7 +229,7 @@ finish: return DEVICEKEY_SUCCESS; } -int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) +int DeviceKey::generate_key_by_trng(uint32_t *output, size_t size) { #if defined(DEVICE_TRNG) size_t in_size; @@ -249,14 +249,13 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t& size) trng_init(&trng_obj); - final_size = 0; + //final_size = 0; in_size = size; - while (final_size < size) { + while (in_size > 0) { ongoing_size = 0; ret = trng_get_bytes(&trng_obj, (unsigned char *)pBuffer, in_size, &ongoing_size); - final_size += ongoing_size; - if (0 != ret) { + if (0 != ret || ongoing_size > in_size) { ret = DEVICEKEY_TRNG_ERROR; goto finish; } diff --git a/features/device_key/source/DeviceKey.h b/features/device_key/source/DeviceKey.h index 301d288763..e5b3a25a4a 100644 --- a/features/device_key/source/DeviceKey.h +++ b/features/device_key/source/DeviceKey.h @@ -131,7 +131,7 @@ private: * output: the actual written size to the buffer * @return 0 on success, negative error code on failure */ - int generate_key_by_trng(uint32_t *output, size_t& size); + int generate_key_by_trng(uint32_t *output, size_t size); }; /** @}*/ From d81693797587e6a45b6497b41d9b2b0223aad2b8 Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Wed, 16 May 2018 13:24:46 +0300 Subject: [PATCH 13/17] Remove unnecessary remark --- features/device_key/source/DeviceKey.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/features/device_key/source/DeviceKey.cpp b/features/device_key/source/DeviceKey.cpp index 16364807d2..5c420dfd77 100644 --- a/features/device_key/source/DeviceKey.cpp +++ b/features/device_key/source/DeviceKey.cpp @@ -249,7 +249,6 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t size) trng_init(&trng_obj); - //final_size = 0; in_size = size; while (in_size > 0) { From 492dc9f1fca4eecac6745c175633af506482828a Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Wed, 16 May 2018 14:48:30 +0300 Subject: [PATCH 14/17] Remove uneeded variables --- features/device_key/source/DeviceKey.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/features/device_key/source/DeviceKey.cpp b/features/device_key/source/DeviceKey.cpp index 5c420dfd77..f45f88ac4c 100644 --- a/features/device_key/source/DeviceKey.cpp +++ b/features/device_key/source/DeviceKey.cpp @@ -234,7 +234,6 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t size) #if defined(DEVICE_TRNG) size_t in_size; size_t ongoing_size; - size_t final_size; trng_t trng_obj; int ret = DEVICEKEY_SUCCESS; unsigned char *pBuffer = (unsigned char *)output; @@ -267,7 +266,6 @@ int DeviceKey::generate_key_by_trng(uint32_t *output, size_t size) finish: trng_free(&trng_obj); - size = final_size; return ret; #else From b95c5f79c0d40a269731e491424e07390a0c52f4 Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Mon, 21 May 2018 18:47:39 +0300 Subject: [PATCH 15/17] Replace UINT16_MAX with (uint16_t)-1 because of build failure --- features/device_key/source/DeviceKey.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/device_key/source/DeviceKey.cpp b/features/device_key/source/DeviceKey.cpp index f45f88ac4c..4d0538f473 100644 --- a/features/device_key/source/DeviceKey.cpp +++ b/features/device_key/source/DeviceKey.cpp @@ -130,7 +130,7 @@ int DeviceKey::write_key_to_nvstore(uint32_t *input, size_t isize) int DeviceKey::read_key_from_nvstore(uint32_t *output, size_t& size) { - if (size > UINT16_MAX) { + if (size > (uint16_t)-1) { return DEVICEKEY_INVALID_PARAM; } From 7401aea36a4877caf1b9ee0aa58aadb5995ab425 Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Tue, 22 May 2018 21:35:33 +0300 Subject: [PATCH 16/17] Remove reset tests. Small change to cmac claculation in get_derived_key --- .../TESTS/device_key/functionality/main.cpp | 44 ++--- .../TESTS/host_tests/devicekey_reset.py | 150 ------------------ features/device_key/source/DeviceKey.cpp | 17 +- 3 files changed, 24 insertions(+), 187 deletions(-) delete mode 100644 features/device_key/TESTS/host_tests/devicekey_reset.py diff --git a/features/device_key/TESTS/device_key/functionality/main.cpp b/features/device_key/TESTS/device_key/functionality/main.cpp index fc385e3fec..f51ff94122 100644 --- a/features/device_key/TESTS/device_key/functionality/main.cpp +++ b/features/device_key/TESTS/device_key/functionality/main.cpp @@ -38,8 +38,8 @@ using namespace utest::v1; #define MSG_KEY_DEVICE_TEST_STEP4 "check_consistency_step4" #define MSG_KEY_DEVICE_TEST_SUITE_ENDED "Test suite ended" -void generate_derived_key_consistency_16_byte_key_reset_test(char *key); -void generate_derived_key_consistency_32_byte_key_reset_test(char *key); +void generate_derived_key_consistency_16_byte_key_long_consistency_test(char *key); +void generate_derived_key_consistency_32_byte_key_long_consistency_test(char *key); /* * Injection of a dummy key when there is no TRNG @@ -58,33 +58,26 @@ int inject_dummy_rot_key() #endif } -void generate_derived_key_reset_test() +void generate_derived_key_long_consistency_test() { - greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY); - static char key[MSG_KEY_LEN + 1] = { }; static char value[MSG_VALUE_LEN + 1] = { }; - memset(key, 0, MSG_KEY_LEN + 1); - memset(value, 0, MSG_VALUE_LEN + 1); - greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); + strcpy(key, MSG_KEY_DEVICE_TEST_STEP1); + generate_derived_key_consistency_16_byte_key_long_consistency_test(key); + strcpy(key, MSG_KEY_DEVICE_TEST_STEP2); + generate_derived_key_consistency_16_byte_key_long_consistency_test(key); + strcpy(key, MSG_KEY_DEVICE_TEST_STEP3); + generate_derived_key_consistency_32_byte_key_long_consistency_test(key); + strcpy(key, MSG_KEY_DEVICE_TEST_STEP4); + generate_derived_key_consistency_32_byte_key_long_consistency_test(key); - if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP1) == 0 || strcmp(key, MSG_KEY_DEVICE_TEST_STEP2) == 0) { - generate_derived_key_consistency_16_byte_key_reset_test(key); - return generate_derived_key_reset_test(); - } - - if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP3) == 0 || strcmp(key, MSG_KEY_DEVICE_TEST_STEP4) == 0) { - return generate_derived_key_consistency_32_byte_key_reset_test(key); - } - - TEST_ASSERT_MESSAGE(false, key); //Indicates error!!! } /* * Test the consistency of derived 16 byte key result after device reset. */ -void generate_derived_key_consistency_16_byte_key_reset_test(char *key) +void generate_derived_key_consistency_16_byte_key_long_consistency_test(char *key) { unsigned char output1[DEVICE_KEY_16BYTE]; unsigned char output2[DEVICE_KEY_16BYTE]; @@ -115,8 +108,6 @@ void generate_derived_key_consistency_16_byte_key_reset_test(char *key) ret = nvstore.set(15, DEVICE_KEY_16BYTE, output1); TEST_ASSERT_EQUAL_INT32(0, ret); - system_reset(); - TEST_ASSERT_MESSAGE(false, "system_reset() did not reset the device as expected."); } else if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP2) == 0) { //Second step: Read from NVStore at index 15 there should be a derived key there. @@ -136,7 +127,6 @@ void generate_derived_key_consistency_16_byte_key_reset_test(char *key) ret = nvstore.reset(); TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); - greentea_send_kv(MSG_KEY_DEVICE_FINISH, MSG_VALUE_DUMMY); } else { TEST_ASSERT_MESSAGE(false, "Unknown test step received"); } @@ -145,7 +135,7 @@ void generate_derived_key_consistency_16_byte_key_reset_test(char *key) /* * Test the consistency of derived 32 byte key result after device reset. */ -void generate_derived_key_consistency_32_byte_key_reset_test(char *key) +void generate_derived_key_consistency_32_byte_key_long_consistency_test(char *key) { unsigned char output1[DEVICE_KEY_32BYTE]; unsigned char output2[DEVICE_KEY_32BYTE]; @@ -176,8 +166,6 @@ void generate_derived_key_consistency_32_byte_key_reset_test(char *key) ret = nvstore.set(15, DEVICE_KEY_32BYTE, output1); TEST_ASSERT_EQUAL_INT32(0, ret); - system_reset(); - TEST_ASSERT_MESSAGE(false, "system_reset() did not reset the device as expected."); } else if (strcmp(key, MSG_KEY_DEVICE_TEST_STEP4) == 0) { //Fourth step: Read from NVStore at index 15 there should be a derived key there. @@ -197,7 +185,6 @@ void generate_derived_key_consistency_32_byte_key_reset_test(char *key) ret = nvstore.reset(); TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); - greentea_send_kv(MSG_KEY_DEVICE_FINISH, MSG_VALUE_DUMMY); } else { TEST_ASSERT_MESSAGE(false, "Unknown test step received"); } @@ -461,7 +448,7 @@ utest::v1::status_t greentea_failure_handler(const Case *const source, const fai //Currently there can be only one test that contains reset and it has to be the first test! Case cases[] = { - Case("Device Key - derived key reset", generate_derived_key_reset_test, greentea_failure_handler), + Case("Device Key - long consistency test", generate_derived_key_long_consistency_test, greentea_failure_handler), Case("Device Key - inject value wrong size", device_inject_root_of_trust_wrong_size_test, greentea_failure_handler), Case("Device Key - inject value 16 byte size", device_inject_root_of_trust_16_byte_size_test, greentea_failure_handler), Case("Device Key - inject value 32 byte size", device_inject_root_of_trust_32_byte_size_test, greentea_failure_handler), @@ -475,7 +462,7 @@ Case cases[] = { utest::v1::status_t greentea_test_setup(const size_t number_of_cases) { - GREENTEA_SETUP(14, "devicekey_reset"); + GREENTEA_SETUP(14, "default_auto"); return greentea_test_setup_handler(number_of_cases); } @@ -484,7 +471,6 @@ Specification specification(greentea_test_setup, cases, greentea_test_teardown_h int main() { bool ret = Harness::run(specification); - greentea_send_kv(MSG_KEY_DEVICE_TEST_SUITE_ENDED, MSG_VALUE_DUMMY); return ret; } diff --git a/features/device_key/TESTS/host_tests/devicekey_reset.py b/features/device_key/TESTS/host_tests/devicekey_reset.py deleted file mode 100644 index 08379739cb..0000000000 --- a/features/device_key/TESTS/host_tests/devicekey_reset.py +++ /dev/null @@ -1,150 +0,0 @@ -""" -Copyright (c) 2018 ARM Limited - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import time -from mbed_host_tests import BaseHostTest -from mbed_host_tests.host_tests_runner.host_test_default import DefaultTestSelector - -DEFAULT_CYCLE_PERIOD = 1.0 -MSG_VALUE_DUMMY = '0' -MSG_KEY_DEVICE_READY = 'ready' -MSG_KEY_DEVICE_FINISH = 'finish' -MSG_KEY_DEVICE_TEST_STEP1 = 'check_consistency_step1' -MSG_KEY_DEVICE_TEST_STEP2 = 'check_consistency_step2' -MSG_KEY_DEVICE_TEST_STEP3 = 'check_consistency_step3' -MSG_KEY_DEVICE_TEST_STEP4 = 'check_consistency_step4' -MSG_KEY_SYNC = '__sync' -MSG_KEY_TEST_SUITE_ENDED = 'Test suite ended' - -class DeviceKeyResetTest(BaseHostTest): - """Test for the DeviceKey driver API. - """ - - def __init__(self): - super(DeviceKeyResetTest, self).__init__() - self.reset = False - self.finish = False - self.suite_ended = False - cycle_s = self.get_config_item('program_cycle_s') - self.program_cycle_s = cycle_s if cycle_s is not None else DEFAULT_CYCLE_PERIOD - self.test_steps_sequence = self.test_steps() - # Advance the coroutine to it's first yield statement. - self.test_steps_sequence.send(None) - - def setup(self): - self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready) - self.register_callback(MSG_KEY_DEVICE_FINISH, self.cb_device_finish) - self.register_callback(MSG_KEY_TEST_SUITE_ENDED, self.cb_device_test_suit_ended) - - def cb_device_ready(self, key, value, timestamp): - """Acknowledge device rebooted correctly and feed the test execution - """ - self.reset = True - - try: - if self.test_steps_sequence.send(value): - self.notify_complete(True) - except (StopIteration, RuntimeError) as exc: - self.notify_complete(False) - - def cb_device_finish(self, key, value, timestamp): - """Acknowledge device finished a test step correctly and feed the test execution - """ - self.finish = True - - try: - if self.test_steps_sequence.send(value): - self.notify_complete(True) - except (StopIteration, RuntimeError) as exc: - self.notify_complete(False) - - def cb_device_test_suit_ended(self, key, value, timestamp): - """Acknowledge device finished a test step correctly and feed the test execution - """ - self.suite_ended = True - - try: - if self.test_steps_sequence.send(value): - self.notify_complete(True) - except (StopIteration, RuntimeError) as exc: - self.notify_complete(False) - - def test_steps(self): - """Test step 1 (16 byte key test) - """ - wait_for_communication = yield - - self.reset = False - self.send_kv(MSG_KEY_DEVICE_TEST_STEP1, MSG_VALUE_DUMMY) - time.sleep(self.program_cycle_s) - self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY) - - wait_for_communication = yield - - if self.reset == False: - raise RuntimeError('Phase 1: Platform did not reset as expected.') - - """Test step 2 (After reset) - """ - self.finish = False - self.send_kv(MSG_KEY_DEVICE_TEST_STEP2, MSG_VALUE_DUMMY) - time.sleep(self.program_cycle_s) - wait_for_communication = yield - - if self.finish == False: - raise RuntimeError('Test failed.') - - """Test Step 3 (32 byte key test) - """ - wait_for_communication = yield - - self.reset = False - self.send_kv(MSG_KEY_DEVICE_TEST_STEP3, MSG_VALUE_DUMMY) - time.sleep(self.program_cycle_s) - self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY) - - wait_for_communication = yield - - if self.reset == False: - raise RuntimeError('Phase 3: Platform did not reset as expected.') - - """Test step 4 (After reset) - """ - self.finish = False - self.send_kv(MSG_KEY_DEVICE_TEST_STEP4, MSG_VALUE_DUMMY) - time.sleep(self.program_cycle_s) - - wait_for_communication = yield - - if self.finish == False: - raise RuntimeError('Test failed.') - - """Test step 4 (After reset) - """ - self.suite_ended = False - time.sleep(self.program_cycle_s) - - wait_for_communication = yield - - if self.suite_ended == False: - raise RuntimeError('Test failed.') - - # The sequence is correct -- test passed. - yield True - - - - \ No newline at end of file diff --git a/features/device_key/source/DeviceKey.cpp b/features/device_key/source/DeviceKey.cpp index 4d0538f473..070d22475a 100644 --- a/features/device_key/source/DeviceKey.cpp +++ b/features/device_key/source/DeviceKey.cpp @@ -175,14 +175,14 @@ int DeviceKey::get_derived_key(uint32_t *ikey_buff, size_t ikey_size, const unsi const mbedtls_cipher_info_t *cipher_info = mbedtls_cipher_info_from_type(mbedtls_cipher_type); - mbedtls_cipher_init(&ctx); - ret = mbedtls_cipher_setup(&ctx, cipher_info); - if (ret != 0) { - goto finish; - } - do { + mbedtls_cipher_init(&ctx); + ret = mbedtls_cipher_setup(&ctx, cipher_info); + if (ret != 0) { + goto finish; + } + ret = mbedtls_cipher_cmac_starts(&ctx, (unsigned char *)ikey_buff, ikey_size * 8); if (ret != 0) { goto finish; @@ -215,14 +215,15 @@ int DeviceKey::get_derived_key(uint32_t *ikey_buff, size_t ikey_size, const unsi goto finish; } + mbedtls_cipher_free( &ctx ); + counter++; } while (DEVICE_KEY_16BYTE * counter < ikey_type); finish: - mbedtls_cipher_free( &ctx ); - if (DEVICEKEY_SUCCESS != ret) { + mbedtls_cipher_free( &ctx ); return DEVICEKEY_ERR_CMAC_GENERIC_FAILURE; } From f33f4dab26223e7b77ca7a41a3c688392adb2f7d Mon Sep 17 00:00:00 2001 From: Yossi Levy Date: Wed, 23 May 2018 11:29:46 +0300 Subject: [PATCH 17/17] Increase tests timeout to 45 second --- features/device_key/TESTS/device_key/functionality/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/device_key/TESTS/device_key/functionality/main.cpp b/features/device_key/TESTS/device_key/functionality/main.cpp index f51ff94122..32362fd4d4 100644 --- a/features/device_key/TESTS/device_key/functionality/main.cpp +++ b/features/device_key/TESTS/device_key/functionality/main.cpp @@ -462,7 +462,7 @@ Case cases[] = { utest::v1::status_t greentea_test_setup(const size_t number_of_cases) { - GREENTEA_SETUP(14, "default_auto"); + GREENTEA_SETUP(45, "default_auto"); return greentea_test_setup_handler(number_of_cases); }