From 44f6b8a07c4b6ebdf2e95c3e370c8c5229fccca6 Mon Sep 17 00:00:00 2001 From: Michael Schwarcz Date: Wed, 7 Mar 2018 17:48:20 +0200 Subject: [PATCH] Add secure_time library implementation --- TESTS/secure_time/library_api/main.cpp | 572 ++++++++++++++++++ .../library_api/secure_time_test_utils.cpp | 192 ++++++ .../library_api/secure_time_test_utils.h | 30 + features/storage/nvstore/source/nvstore.h | 3 + secure_time/Readme.md | 144 +++++ secure_time/crypto/secure_time_crypto.h | 57 ++ secure_time/crypto/secure_time_mbedtls.c | 167 +++++ secure_time/secure_time_client.h | 91 +++ secure_time/secure_time_client_direct.c | 42 ++ secure_time/secure_time_client_spe.h | 121 ++++ secure_time/secure_time_client_spe_direct.c | 43 ++ secure_time/secure_time_impl.c | 407 +++++++++++++ secure_time/secure_time_impl.h | 50 ++ secure_time/secure_time_utils.cpp | 39 ++ secure_time/secure_time_utils.h | 53 ++ secure_time/storage/secure_time_nvstore.cpp | 125 ++++ secure_time/storage/secure_time_storage.h | 62 ++ 17 files changed, 2198 insertions(+) create mode 100644 TESTS/secure_time/library_api/main.cpp create mode 100644 TESTS/secure_time/library_api/secure_time_test_utils.cpp create mode 100644 TESTS/secure_time/library_api/secure_time_test_utils.h create mode 100644 secure_time/Readme.md create mode 100644 secure_time/crypto/secure_time_crypto.h create mode 100644 secure_time/crypto/secure_time_mbedtls.c create mode 100644 secure_time/secure_time_client.h create mode 100644 secure_time/secure_time_client_direct.c create mode 100644 secure_time/secure_time_client_spe.h create mode 100644 secure_time/secure_time_client_spe_direct.c create mode 100644 secure_time/secure_time_impl.c create mode 100644 secure_time/secure_time_impl.h create mode 100644 secure_time/secure_time_utils.cpp create mode 100644 secure_time/secure_time_utils.h create mode 100644 secure_time/storage/secure_time_nvstore.cpp create mode 100644 secure_time/storage/secure_time_storage.h diff --git a/TESTS/secure_time/library_api/main.cpp b/TESTS/secure_time/library_api/main.cpp new file mode 100644 index 0000000000..a619f037d3 --- /dev/null +++ b/TESTS/secure_time/library_api/main.cpp @@ -0,0 +1,572 @@ +/* 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 +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "secure_time_client_spe.h" +#include "secure_time_impl.h" +#include "secure_time_storage.h" +#include "secure_time_test_utils.h" +#include "nvstore.h" + +#ifdef ENABLE_LIBGCOV_PORT +#include "libgcov-embedded.h" +#endif + +#if !NVSTORE_ENABLED +#error [NOT_SUPPORTED] NVSTORE needs to be enabled for this test +#endif + +using namespace utest::v1; + +#define SECURE_TIME_TEST_SECS_PER_MINUTE (60) +#define SECURE_TIME_TEST_SECS_PER_HOUR (60 * SECURE_TIME_TEST_SECS_PER_MINUTE) +#define SECURE_TIME_TEST_SECS_PER_DAY (24 * SECURE_TIME_TEST_SECS_PER_HOUR) + +#define SECURE_TIME_TEST_DEFAULT_TIME (2555562978ULL) + +#define EXTRACT_UINT16(buf) ((((uint16_t)(((uint8_t *)(buf))[1])) << 8) + (uint16_t)(((uint8_t *)(buf))[0])) +#define WRITE_UINT16(buf, val) \ + { \ + ((uint8_t *)(buf))[0] = (uint8_t)(((uint16_t)(val)) & 0xff); \ + ((uint8_t *)(buf))[1] = (uint8_t)(((uint16_t)(val)) >> 8); \ + } + +MBED_ALIGN(4) // a WA for a failure with ARMCC +static const uint8_t ca_prvkey[] = { + 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x47, 0x2d, 0x6d, 0x08, 0x7c, + 0xeb, 0x6d, 0x4c, 0xb1, 0xa1, 0x20, 0xb4, 0x80, 0x5f, 0x47, 0x78, 0xd6, + 0xa5, 0x69, 0xf7, 0x34, 0xf2, 0xa2, 0x85, 0xb1, 0x5d, 0xae, 0xfa, 0x53, + 0x57, 0x33, 0x7b, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0x7f, 0x4a, 0x31, + 0xc8, 0x30, 0xbf, 0x71, 0x0a, 0x62, 0x91, 0xd9, 0xef, 0x54, 0xfa, 0x66, + 0xe4, 0xab, 0xe9, 0xfa, 0x80, 0x12, 0x42, 0xdc, 0x16, 0x9f, 0x09, 0x37, + 0x4d, 0xc6, 0x8c, 0x06, 0x03, 0x51, 0x9b, 0x1d, 0xd2, 0x36, 0x69, 0xe6, + 0xc8, 0x30, 0x62, 0x44, 0x5d, 0xe5, 0x15, 0xb4, 0x9c, 0x9f, 0x9b, 0x23, + 0x0a, 0x00, 0x1f, 0x8b, 0x4e, 0x8c, 0x8f, 0x5e, 0x80, 0x46, 0x71, 0xdc, + 0xb4 +}; + +MBED_ALIGN(4) // a WA for a failure with ARMCC +static const uint8_t ca_pubkey[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0x7f, 0x4a, 0x31, 0xc8, 0x30, 0xbf, 0x71, 0x0a, 0x62, + 0x91, 0xd9, 0xef, 0x54, 0xfa, 0x66, 0xe4, 0xab, 0xe9, 0xfa, 0x80, 0x12, + 0x42, 0xdc, 0x16, 0x9f, 0x09, 0x37, 0x4d, 0xc6, 0x8c, 0x06, 0x03, 0x51, + 0x9b, 0x1d, 0xd2, 0x36, 0x69, 0xe6, 0xc8, 0x30, 0x62, 0x44, 0x5d, 0xe5, + 0x15, 0xb4, 0x9c, 0x9f, 0x9b, 0x23, 0x0a, 0x00, 0x1f, 0x8b, 0x4e, 0x8c, + 0x8f, 0x5e, 0x80, 0x46, 0x71, 0xdc, 0xb4 +}; + +MBED_ALIGN(4) // a WA for a failure with ARMCC +static const uint8_t prvkey1[] = { + 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x61, 0x93, 0xa8, 0x0f, 0xaf, + 0xd0, 0xc0, 0x60, 0x78, 0x65, 0x40, 0xc8, 0xd7, 0x77, 0x83, 0xda, 0x5a, + 0x12, 0x92, 0x26, 0x94, 0x77, 0xac, 0x1e, 0xb5, 0x69, 0xa2, 0x24, 0x3c, + 0x97, 0x86, 0x93, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0x10, 0x47, 0x96, + 0xc0, 0x7e, 0x64, 0x84, 0x9a, 0x56, 0xd5, 0xf0, 0xf6, 0x8f, 0x08, 0x74, + 0xed, 0x90, 0x87, 0x89, 0x45, 0x41, 0x7a, 0xf4, 0xd0, 0x1f, 0x98, 0xce, + 0xc2, 0xad, 0x0c, 0x62, 0x3e, 0x9d, 0x59, 0xde, 0x0f, 0x45, 0x4d, 0x8f, + 0xa6, 0x54, 0x25, 0x91, 0xf5, 0x93, 0xd5, 0xb1, 0xd7, 0xe7, 0x62, 0x49, + 0x61, 0x21, 0x6b, 0x62, 0xa7, 0x18, 0x4b, 0xc9, 0x73, 0x28, 0xc8, 0x5d, + 0xa9 +}; + +MBED_ALIGN(4) // a WA for a failure with ARMCC +static const uint8_t pubkey1[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0x10, 0x47, 0x96, 0xc0, 0x7e, 0x64, 0x84, 0x9a, 0x56, + 0xd5, 0xf0, 0xf6, 0x8f, 0x08, 0x74, 0xed, 0x90, 0x87, 0x89, 0x45, 0x41, + 0x7a, 0xf4, 0xd0, 0x1f, 0x98, 0xce, 0xc2, 0xad, 0x0c, 0x62, 0x3e, 0x9d, + 0x59, 0xde, 0x0f, 0x45, 0x4d, 0x8f, 0xa6, 0x54, 0x25, 0x91, 0xf5, 0x93, + 0xd5, 0xb1, 0xd7, 0xe7, 0x62, 0x49, 0x61, 0x21, 0x6b, 0x62, 0xa7, 0x18, + 0x4b, 0xc9, 0x73, 0x28, 0xc8, 0x5d, 0xa9 +}; + +MBED_ALIGN(4) // a WA for a failure with ARMCC +static const uint8_t prvkey2[] = { + 0x30, 0x76, 0x02, 0x01, 0x01, 0x04, 0x1f, 0x47, 0x17, 0x35, 0x1f, 0xb1, + 0xbd, 0xb9, 0x94, 0x83, 0x10, 0x11, 0xee, 0x4a, 0x6f, 0xc1, 0xc7, 0xa9, + 0xab, 0xec, 0xf7, 0xd8, 0x33, 0xb1, 0xf8, 0x63, 0xe5, 0xf2, 0xa7, 0xaa, + 0x68, 0xc9, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, + 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xd9, 0xbf, 0xff, 0xcb, + 0xb1, 0x40, 0x0c, 0x9b, 0x8b, 0x7e, 0x0e, 0xbd, 0x69, 0x89, 0x82, 0xa1, + 0xed, 0x33, 0xee, 0x95, 0xa5, 0x34, 0xc8, 0x41, 0xb0, 0x88, 0xf9, 0xcb, + 0xac, 0x15, 0xdf, 0xab, 0x4e, 0x0a, 0x16, 0x45, 0xd5, 0xdf, 0x89, 0x3e, + 0xb3, 0x7f, 0x05, 0xc2, 0x78, 0x28, 0xb3, 0xf5, 0x2f, 0x29, 0xed, 0xa1, + 0x6c, 0x43, 0xf6, 0x7a, 0x59, 0x81, 0x73, 0x1e, 0x1d, 0xa5, 0x22, 0x19 +}; + +MBED_ALIGN(4) // a WA for a failure with ARMCC +static const uint8_t pubkey2[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0xd9, 0xbf, 0xff, 0xcb, 0xb1, 0x40, 0x0c, 0x9b, 0x8b, + 0x7e, 0x0e, 0xbd, 0x69, 0x89, 0x82, 0xa1, 0xed, 0x33, 0xee, 0x95, 0xa5, + 0x34, 0xc8, 0x41, 0xb0, 0x88, 0xf9, 0xcb, 0xac, 0x15, 0xdf, 0xab, 0x4e, + 0x0a, 0x16, 0x45, 0xd5, 0xdf, 0x89, 0x3e, 0xb3, 0x7f, 0x05, 0xc2, 0x78, + 0x28, 0xb3, 0xf5, 0x2f, 0x29, 0xed, 0xa1, 0x6c, 0x43, 0xf6, 0x7a, 0x59, + 0x81, 0x73, 0x1e, 0x1d, 0xa5, 0x22, 0x19 +}; + +MBED_ALIGN(4) // a WA for a failure with ARMCC +static const uint8_t prvkey3[] = { + 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0xd2, 0x4a, 0x9c, 0xf3, 0xe2, + 0x26, 0x1e, 0x25, 0xc0, 0x04, 0xdf, 0x90, 0x19, 0xf0, 0xdc, 0xb1, 0xd6, + 0x2d, 0xdb, 0x5a, 0x10, 0x25, 0x2a, 0x48, 0xd2, 0xdd, 0x85, 0x4d, 0x90, + 0xda, 0x3a, 0xbc, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xd8, 0x4a, 0x68, + 0x3a, 0xf1, 0x20, 0xec, 0x8f, 0x55, 0x68, 0x15, 0xbc, 0x9c, 0xc3, 0xb2, + 0x29, 0xcb, 0x91, 0x99, 0xea, 0xd9, 0xe7, 0xa3, 0x6c, 0x58, 0xec, 0xd7, + 0x22, 0x80, 0xc0, 0xfe, 0xf2, 0x63, 0x2a, 0x13, 0x71, 0x93, 0x11, 0xc0, + 0x8b, 0x70, 0x04, 0xd5, 0x2f, 0x95, 0xfa, 0xd7, 0x06, 0x79, 0xc6, 0x32, + 0xdf, 0xca, 0xbb, 0xa8, 0xf5, 0x68, 0xdc, 0x9d, 0x5a, 0x5a, 0x18, 0x6f, + 0xad +}; + +MBED_ALIGN(4) // a WA for a failure with ARMCC +static const uint8_t pubkey3[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0xd8, 0x4a, 0x68, 0x3a, 0xf1, 0x20, 0xec, 0x8f, 0x55, + 0x68, 0x15, 0xbc, 0x9c, 0xc3, 0xb2, 0x29, 0xcb, 0x91, 0x99, 0xea, 0xd9, + 0xe7, 0xa3, 0x6c, 0x58, 0xec, 0xd7, 0x22, 0x80, 0xc0, 0xfe, 0xf2, 0x63, + 0x2a, 0x13, 0x71, 0x93, 0x11, 0xc0, 0x8b, 0x70, 0x04, 0xd5, 0x2f, 0x95, + 0xfa, 0xd7, 0x06, 0x79, 0xc6, 0x32, 0xdf, 0xca, 0xbb, 0xa8, 0xf5, 0x68, + 0xdc, 0x9d, 0x5a, 0x5a, 0x18, 0x6f, 0xad +}; + +static uint8_t blob[SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES] = {0}; +static uint8_t blob2[SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES] = {0}; + +static data_buffer_t delegation_pubkeys[] = { + {pubkey1, sizeof(pubkey1)}, + {pubkey2, sizeof(pubkey2)}, + {pubkey3, sizeof(pubkey3)} +}; + +static data_buffer_t privkeys[] = { + {ca_prvkey, sizeof(ca_prvkey)}, + {prvkey1, sizeof(prvkey1)}, + {prvkey2, sizeof(prvkey2)}, + {prvkey3, sizeof(prvkey3)} +}; + +static void blob_without_delegations(void) +{ + uint64_t set_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t nonce = 0; + + provision_data(ca_pubkey, sizeof(ca_pubkey)); + + secure_time_set_trusted_init(&nonce); + size_t blob_size = create_blob(set_time, nonce, delegation_pubkeys, privkeys, 0, blob, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_SUCCESS, + secure_time_set_trusted_commit(blob, blob_size) + ); + TEST_ASSERT_UINT64_WITHIN(3, set_time, secure_time_get()); +} + +static void blob_with_delegations(void) +{ + uint64_t set_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t nonce = 0; + + provision_data(ca_pubkey, sizeof(ca_pubkey)); + + secure_time_set_trusted_init(&nonce); + size_t blob_size = create_blob(set_time, nonce, delegation_pubkeys, privkeys, 3, blob, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_SUCCESS, + secure_time_set_trusted_commit(blob, blob_size) + ); + TEST_ASSERT_UINT64_WITHIN(3, set_time, secure_time_get()); +} + +static void malformed_blob_signature(void) +{ + uint64_t set_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t nonce = 0; + + provision_data(ca_pubkey, sizeof(ca_pubkey)); + + secure_time_set_trusted_init(&nonce); + size_t blob_size = create_blob(set_time, nonce, delegation_pubkeys, privkeys, 1, blob, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + + // Break the last byte of the blob which is also the last byte of the blob's signature. + blob[blob_size - 1] ^= 1; + + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_SIGNATURE_VERIFICATION_FAILED, + secure_time_set_trusted_commit(blob, blob_size) + ); +} + +static void malformed_delegation_signature(void) +{ + uint64_t set_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t nonce = 0; + + provision_data(ca_pubkey, sizeof(ca_pubkey)); + + secure_time_set_trusted_init(&nonce); + size_t blob_size = create_blob(set_time, nonce, delegation_pubkeys, privkeys, 1, blob, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + + // Break the first byte of the delegation signature. + blob[ + SECURE_TIME_BLOB_HEADER_SIZE_BYTES + + SECURE_TIME_PUBKEY_LENGTH_SIZE_BYTES + + delegation_pubkeys[0].size + + SECURE_TIME_SIGNATURE_LENGTH_SIZE_BYTES + ] ^= 1; + + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_SIGNATURE_VERIFICATION_FAILED, + secure_time_set_trusted_commit(blob, blob_size) + ); +} + +static void malformed_blob_signature_size(void) +{ + uint64_t set_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t nonce = 0; + + provision_data(ca_pubkey, sizeof(ca_pubkey)); + + secure_time_set_trusted_init(&nonce); + size_t blob_size = create_blob(set_time, nonce, delegation_pubkeys, privkeys, 0, blob, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + + // Break blob's signature size field. + uint16_t delegation_size = EXTRACT_UINT16(blob + SECURE_TIME_DELEGATION_LENGTH_OFFSET); + uint16_t blob_signature_size = EXTRACT_UINT16(blob + SECURE_TIME_BLOB_HEADER_SIZE_BYTES + delegation_size); + WRITE_UINT16(blob + SECURE_TIME_BLOB_HEADER_SIZE_BYTES + delegation_size, blob_signature_size - 1); + + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_INVALID_BLOB_SIZE, + secure_time_set_trusted_commit(blob, blob_size) + ); +} + +static void malformed_delegation_signature_size(void) +{ + uint64_t set_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t nonce = 0; + + provision_data(ca_pubkey, sizeof(ca_pubkey)); + + secure_time_set_trusted_init(&nonce); + size_t blob_size = create_blob(set_time, nonce, delegation_pubkeys, privkeys, 2, blob, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + + // Break first delegation signature size field. + size_t signature_offset = SECURE_TIME_BLOB_HEADER_SIZE_BYTES + SECURE_TIME_PUBKEY_LENGTH_SIZE_BYTES + delegation_pubkeys[0].size; + uint16_t signature_size = EXTRACT_UINT16(blob + signature_offset); + WRITE_UINT16(blob + signature_offset, signature_size + 1); + + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_SIGNATURE_VERIFICATION_FAILED, + secure_time_set_trusted_commit(blob, blob_size) + ); +} + +static void delegation_size_too_short(void) +{ + uint64_t set_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t nonce = 0; + + provision_data(ca_pubkey, sizeof(ca_pubkey)); + + secure_time_set_trusted_init(&nonce); + size_t blob_size = create_blob(set_time, nonce, delegation_pubkeys, privkeys, 1, blob, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + + // Decrement delegation section size by 1 byte. + uint16_t delegation_size = EXTRACT_UINT16(blob + SECURE_TIME_DELEGATION_LENGTH_OFFSET); + WRITE_UINT16(blob + SECURE_TIME_DELEGATION_LENGTH_OFFSET, delegation_size - 1); + + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_INVALID_BLOB_SIZE, + secure_time_set_trusted_commit(blob, blob_size) + ); +} + +static void delegation_size_too_long(void) +{ + uint64_t set_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t nonce = 0; + + provision_data(ca_pubkey, sizeof(ca_pubkey)); + + secure_time_set_trusted_init(&nonce); + size_t blob_size = create_blob(set_time, nonce, delegation_pubkeys, privkeys, 1, blob, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + + // Increment delegation section size by 1 byte. + uint16_t delegation_size = EXTRACT_UINT16(blob + SECURE_TIME_DELEGATION_LENGTH_OFFSET); + WRITE_UINT16(blob + SECURE_TIME_DELEGATION_LENGTH_OFFSET, delegation_size + 1); + + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_INVALID_BLOB_SIZE, + secure_time_set_trusted_commit(blob, blob_size) + ); +} + +void wrong_nonce(void) +{ + uint64_t set_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t nonce1 = 0, nonce2 = 0; + + provision_data(ca_pubkey, sizeof(ca_pubkey)); + + secure_time_set_trusted_init(&nonce1); + size_t blob_size1 = create_blob(set_time, nonce1, delegation_pubkeys, privkeys, 0, blob, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + + secure_time_set_trusted_init(&nonce2); + size_t blob_size2 = create_blob(set_time, nonce2, delegation_pubkeys, privkeys, 0, blob2, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_NONCE_NOT_MATCH, + secure_time_set_trusted_commit(blob, blob_size1) + ); + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_SUCCESS, + secure_time_set_trusted_commit(blob2, blob_size2) + ); +} + +void wrong_nonce2(void) +{ + uint64_t set_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t nonce1 = 0, nonce2 = 0; + + provision_data(ca_pubkey, sizeof(ca_pubkey)); + + secure_time_set_trusted_init(&nonce1); + size_t blob_size1 = create_blob(set_time, nonce1, delegation_pubkeys, privkeys, 0, blob, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + + secure_time_set_trusted_init(&nonce2); + size_t blob_size2 = create_blob(set_time, nonce2, delegation_pubkeys, privkeys, 0, blob2, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_SUCCESS, + secure_time_set_trusted_commit(blob2, blob_size2) + ); + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_NONCE_MISSING, + secure_time_set_trusted_commit(blob, blob_size1) + ); +} + +static void replay_blob(void) +{ + uint64_t set_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t nonce = 0; + + provision_data(ca_pubkey, sizeof(ca_pubkey)); + + secure_time_set_trusted_init(&nonce); + size_t blob_size = create_blob(set_time, nonce, delegation_pubkeys, privkeys, 0, blob, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_SUCCESS, + secure_time_set_trusted_commit(blob, blob_size) + ); + TEST_ASSERT_UINT64_WITHIN(3, set_time, secure_time_get()); + + wait(4); + + // Send the blob again + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_NONCE_MISSING, + secure_time_set_trusted_commit(blob, blob_size) + ); + TEST_ASSERT_UINT64_WITHIN(7, set_time, secure_time_get()); +} +/* +void nonce_timeout(void) +{ + uint64_t set_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t nonce = 0; + + provision_data(ca_pubkey, sizeof(ca_pubkey)); + + secure_time_set_trusted_init(&nonce); + + // Create a significant timeout for the nonce to become obsolete. + wait(SECURE_TIME_NONCE_TIMEOUT_SECONDS + 1); + + size_t blob_size = create_blob(set_time, nonce, delegation_pubkeys, privkeys, 0, blob, SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES); + TEST_ASSERT_EQUAL_HEX( + SECURE_TIME_NONCE_TIMEOUT, + secure_time_set_trusted_commit(blob, blob_size) + ); + TEST_ASSERT_UINT64_WITHIN(3, set_time, secure_time_get()); +} +*/ +void normal_set_forward_no_storage_update(void) +{ + uint64_t curr_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t set_time = curr_time + (SECURE_TIME_MIN_STORAGE_FORWARD_LATENCY_SEC - 100); + uint64_t stored_time = 0; + + secure_time_update_boot_time(curr_time); + secure_time_set_stored_time(curr_time); + + secure_time_set(set_time); + TEST_ASSERT_UINT64_WITHIN(3, set_time, secure_time_get()); + secure_time_get_stored_time(&stored_time); + TEST_ASSERT(stored_time > 0); + TEST_ASSERT_EQUAL_UINT64(curr_time, stored_time); +} + +void normal_set_forward_with_storage_update(void) +{ + uint64_t curr_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t set_time = curr_time + (SECURE_TIME_MIN_STORAGE_FORWARD_LATENCY_SEC + 100); + uint64_t stored_time = 0; + + secure_time_update_boot_time(curr_time); + secure_time_set_stored_time(curr_time); + + secure_time_set(set_time); + TEST_ASSERT_UINT64_WITHIN(3, set_time, secure_time_get()); + secure_time_get_stored_time(&stored_time); + TEST_ASSERT(stored_time > 0); + TEST_ASSERT_EQUAL_UINT64(set_time, stored_time); +} + +void normal_set_forward_with_storage_update2(void) +{ + uint64_t curr_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t set_time = curr_time + (SECURE_TIME_MIN_STORAGE_FORWARD_LATENCY_SEC - 100); + uint64_t stored_time = 0; + + secure_time_update_boot_time(curr_time); + secure_time_set_stored_time(curr_time - (SECURE_TIME_MIN_STORAGE_IDLE_LATENCY_SEC + 100)); + + secure_time_set(set_time); + TEST_ASSERT_UINT64_WITHIN(3, set_time, secure_time_get()); + secure_time_get_stored_time(&stored_time); + TEST_ASSERT(stored_time > 0); + TEST_ASSERT_EQUAL_UINT64(set_time, stored_time); +} + +void normal_set_backward_no_drift(void) +{ + uint64_t curr_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t set_time = curr_time - ((30 * SECURE_TIME_TEST_SECS_PER_MINUTE) + 100); + uint64_t back_time = set_time - (10 * SECURE_TIME_TEST_SECS_PER_DAY); + uint64_t stored_time = 0; + + secure_time_update_boot_time(curr_time); + secure_time_set_stored_time(curr_time); + secure_time_set_stored_back_time(back_time); + + secure_time_set(set_time); + TEST_ASSERT_UINT64_WITHIN(3, curr_time, secure_time_get()); + secure_time_get_stored_time(&stored_time); + TEST_ASSERT(stored_time > 0); + TEST_ASSERT_EQUAL_UINT64(curr_time, stored_time); + secure_time_get_stored_back_time(&stored_time); + TEST_ASSERT(stored_time > 0); + TEST_ASSERT_EQUAL_UINT64(back_time, stored_time); +} + +void normal_set_backward_with_drift(void) +{ + uint64_t curr_time = SECURE_TIME_TEST_DEFAULT_TIME; + uint64_t set_time = curr_time - ((30 * SECURE_TIME_TEST_SECS_PER_MINUTE) - 100); + uint64_t back_time = set_time - (10 * SECURE_TIME_TEST_SECS_PER_DAY); + uint64_t stored_time = 0; + + secure_time_update_boot_time(curr_time); + secure_time_set_stored_time(curr_time); + + // Can't set backwards if no STORED_BACK entry in storage + secure_time_set(set_time); + TEST_ASSERT_UINT64_WITHIN(3, curr_time, secure_time_get()); + secure_time_get_stored_time(&stored_time); + TEST_ASSERT(stored_time > 0); + TEST_ASSERT_EQUAL_UINT64(curr_time, stored_time); + secure_time_get_stored_back_time(&stored_time); + TEST_ASSERT_EQUAL_UINT64(0, stored_time); + + secure_time_set_stored_back_time(back_time); + secure_time_get_stored_back_time(&stored_time); + TEST_ASSERT(stored_time > 0); + TEST_ASSERT_EQUAL_UINT64(back_time, stored_time); + + secure_time_set(set_time); + TEST_ASSERT_UINT64_WITHIN(3, set_time, secure_time_get()); + secure_time_get_stored_time(&stored_time); + TEST_ASSERT(stored_time > 0); + TEST_ASSERT_EQUAL_UINT64(set_time, stored_time); + secure_time_get_stored_back_time(&stored_time); + TEST_ASSERT(stored_time > 0); + TEST_ASSERT_EQUAL_UINT64(set_time, stored_time); +} + +utest::v1::status_t storage_setup(const Case *const source, const size_t index_of_case) +{ + NVStore &nvstore = NVStore::get_instance(); + TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, nvstore.reset()); + + // Call the default handler for proper reporting + return greentea_case_setup_handler(source, index_of_case); +} + +// Test cases +Case cases[] = { + Case("Set trusted: Blob with an empty delegation list", blob_without_delegations), + Case("Set trusted: Blob with non-empty delegation list", blob_with_delegations), + Case("Set trusted: Malformed blob signature", malformed_blob_signature), + Case("Set trusted: Malformed delegation signature", malformed_delegation_signature), + Case("Set trusted: Malformed blob signature size", malformed_blob_signature_size), + Case("Set trusted: Malformed blob signature size", malformed_delegation_signature_size), + Case("Set trusted: Delegation size too short", delegation_size_too_short), + Case("Set trusted: Delegation size too long", delegation_size_too_long), + Case("Set trusted: Wrong nonce #1", wrong_nonce), + Case("Set trusted: Wrong nonce #2", wrong_nonce2), + Case("Set trusted: Replay same blob", replay_blob), + //Case("Set trusted: Nonce timeout", nonce_timeout), + Case("Set normal: Forward time, no storage update", storage_setup, normal_set_forward_no_storage_update), + Case("Set normal: Forward time, with storage update #1", storage_setup, normal_set_forward_with_storage_update), + Case("Set normal: Forward time, with storage update #2", storage_setup, normal_set_forward_with_storage_update2), + Case("Set normal: Backward time, no clock drift", storage_setup, normal_set_backward_no_drift), + Case("Set normal: Backward time, clock drift", storage_setup, normal_set_backward_with_drift) +}; + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + // Setup Greentea using a reasonable timeout in seconds +#ifndef NO_GREENTEA + GREENTEA_SETUP(120, "default_auto"); +#endif + return verbose_test_setup_handler(number_of_cases); +} + +Specification specification(test_setup, cases); + +int main() +{ +#ifdef ENABLE_LIBGCOV_PORT + on_exit(collect_coverage, NULL); + static_init(); +#endif + !Harness::run(specification); + return 0; +} diff --git a/TESTS/secure_time/library_api/secure_time_test_utils.cpp b/TESTS/secure_time/library_api/secure_time_test_utils.cpp new file mode 100644 index 0000000000..4b21e4836d --- /dev/null +++ b/TESTS/secure_time/library_api/secure_time_test_utils.cpp @@ -0,0 +1,192 @@ +#include "secure_time_test_utils.h" +#include "secure_time_client_spe.h" +#include "unity.h" +#include + +#include "mbedtls/pk.h" +#include "mbedtls/md.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" + +#include + +mbedtls_entropy_context entropy = {0}; + +void provision_data( + const uint8_t *ca_pubkey, + size_t ca_pubkey_size + ) +{ + TEST_ASSERT_EQUAL_HEX(0, secure_time_set_stored_public_key(ca_pubkey, ca_pubkey_size)); +} + +static void sign_data( + const uint8_t *data, + size_t data_size, + const uint8_t *prvkey, + size_t prvkey_size, + uint8_t *sign, + size_t *sign_size + ) +{ + mbedtls_pk_context pk = {0}; + mbedtls_pk_init(&pk); + TEST_ASSERT_EQUAL_HEX(0, mbedtls_pk_parse_key(&pk, prvkey, prvkey_size, NULL, 0)); + + mbedtls_md_context_t md_ctx = {0}; + mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256; + + // The below variables were made static to prevent stack overflow when compiling with ARMCC. + static unsigned char hash[MBEDTLS_MD_MAX_SIZE]; + static mbedtls_ctr_drbg_context ctr_drbg; + memset(hash, 0, sizeof(hash)); + memset(&ctr_drbg, 0, sizeof(ctr_drbg)); + + const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type); + TEST_ASSERT_NOT_NULL(md_info); + + mbedtls_md_init(&md_ctx); + TEST_ASSERT_EQUAL_HEX(0, mbedtls_md_setup(&md_ctx, md_info, 0)); + TEST_ASSERT_EQUAL_HEX(0, mbedtls_md_starts(&md_ctx)); + TEST_ASSERT_EQUAL_HEX(0, mbedtls_md_update(&md_ctx, data, data_size)); + TEST_ASSERT_EQUAL_HEX(0, mbedtls_md_finish(&md_ctx, hash)); + mbedtls_md_free(&md_ctx); + + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + TEST_ASSERT_EQUAL_HEX(0, mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0)); + + TEST_ASSERT_EQUAL_HEX(0, mbedtls_pk_sign(&pk, md_type, hash, 0, sign, sign_size, mbedtls_ctr_drbg_random, &ctr_drbg)); +} + +static uint8_t signature[SECURE_TIME_TESTS_MAX_SIGN_SIZE_BYTES] = {0}; // define it global static to avoid stack overflow + +static size_t create_delegation_record( + const uint8_t *delegation_pubkey, + size_t delegation_pubkey_size, + const uint8_t *prvkey, + size_t prvkey_size, + uint8_t *record_buffer, + size_t record_buffer_size +) +{ + uint8_t *delegation_record_start = record_buffer; + size_t signature_size = 0; + + size_t record_size = SECURE_TIME_PUBKEY_LENGTH_SIZE_BYTES; + TEST_ASSERT(record_size <= record_buffer_size); + record_buffer[0] = (uint8_t)(delegation_pubkey_size & 0xff); + record_buffer[1] = (uint8_t)(delegation_pubkey_size >> 8); + record_buffer += SECURE_TIME_PUBKEY_LENGTH_SIZE_BYTES; + + record_size += delegation_pubkey_size; + TEST_ASSERT(record_size <= record_buffer_size); + memcpy(record_buffer, delegation_pubkey, delegation_pubkey_size); + record_buffer += delegation_pubkey_size; + + sign_data( + delegation_record_start, + SECURE_TIME_PUBKEY_LENGTH_SIZE_BYTES + delegation_pubkey_size, + prvkey, + prvkey_size, + signature, + &signature_size + ); + TEST_ASSERT(SECURE_TIME_TESTS_MAX_SIGN_SIZE_BYTES >= signature_size); + + record_size += SECURE_TIME_SIGNATURE_LENGTH_SIZE_BYTES; + TEST_ASSERT(record_size <= record_buffer_size); + record_buffer[0] = (uint8_t)(signature_size & 0xff); + record_buffer[1] = (uint8_t)(signature_size >> 8); + record_buffer += SECURE_TIME_SIGNATURE_LENGTH_SIZE_BYTES; + + record_size += signature_size; + TEST_ASSERT(record_size <= record_buffer_size); + memcpy(record_buffer, signature, signature_size); + + return record_size; +} + +size_t create_blob( + uint64_t time, + uint64_t nonce, + const data_buffer_t *delegation_pubkeys, + const data_buffer_t *prvkeys, + uint32_t delegation_list_length, + uint8_t *blob_buffer, + size_t blob_buffer_size + ) +{ + uint32_t blob_size = 0; + + // Make sure the necessary buffers aren't NULL (delegation_pubkeys is allowed to be NULL). + TEST_ASSERT(NULL != prvkeys); + TEST_ASSERT(NULL != blob_buffer); + + // Construct the blob. + uint8_t *blob_start = blob_buffer; + size_t signature_size = 0; + + // Add the timestamp top the blob. + blob_size += SECURE_TIME_TIMESTAMP_SIZE_BYTES; + TEST_ASSERT(blob_size <= blob_buffer_size); + memcpy(blob_buffer, &time, SECURE_TIME_TIMESTAMP_SIZE_BYTES); + blob_buffer += SECURE_TIME_TIMESTAMP_SIZE_BYTES; + + // Add nonce to the blob. + blob_size += SECURE_TIME_NONCE_SIZE_BYTES; + TEST_ASSERT(blob_size <= blob_buffer_size); + memcpy(blob_buffer, &nonce, SECURE_TIME_NONCE_SIZE_BYTES); + blob_buffer += SECURE_TIME_NONCE_SIZE_BYTES; + + // Reserve space in the blob for the overall delegation record size which is still unknown. + blob_size += SECURE_TIME_DELEGATION_LENGTH_SIZE_BYTES; + TEST_ASSERT(blob_size <= blob_buffer_size); + uint8_t *blob_delegation_length = blob_buffer; + blob_buffer += SECURE_TIME_DELEGATION_LENGTH_SIZE_BYTES; + + // Add all the delegation records to the blob. + uint8_t *delegation_section_start = blob_buffer; + uint32_t i = 0; + size_t delegation_record_length = 0; + for (i = 0; delegation_pubkeys != NULL && i < delegation_list_length; i++) { + delegation_record_length = create_delegation_record( + delegation_pubkeys[i].data, + delegation_pubkeys[i].size, + prvkeys[i].data, + prvkeys[i].size, + blob_buffer, + blob_buffer_size - blob_size + ); + blob_size += delegation_record_length; + blob_buffer += delegation_record_length; + } + + // Write the overall delegation section size to the blob. + uint16_t delegation_length = blob_buffer - delegation_section_start; + blob_delegation_length[0] = (uint8_t)(delegation_length & 0xff); + blob_delegation_length[1] = (uint8_t)(delegation_length >> 8); + + // Sign the blob. + sign_data( + blob_start, + blob_size, + prvkeys[i].data, + prvkeys[i].size, + signature, + &signature_size + ); + TEST_ASSERT(SECURE_TIME_TESTS_MAX_SIGN_SIZE_BYTES >= signature_size); + + blob_size += SECURE_TIME_SIGNATURE_LENGTH_SIZE_BYTES; + TEST_ASSERT(blob_size <= blob_buffer_size); + blob_buffer[0] = (uint8_t)(signature_size & 0xff); + blob_buffer[1] = (uint8_t)(signature_size >> 8); + blob_buffer += SECURE_TIME_SIGNATURE_LENGTH_SIZE_BYTES; + + blob_size += signature_size; + TEST_ASSERT(blob_size <= blob_buffer_size); + memcpy(blob_buffer, signature, signature_size); + + return blob_size; +} diff --git a/TESTS/secure_time/library_api/secure_time_test_utils.h b/TESTS/secure_time/library_api/secure_time_test_utils.h new file mode 100644 index 0000000000..5f82e60048 --- /dev/null +++ b/TESTS/secure_time/library_api/secure_time_test_utils.h @@ -0,0 +1,30 @@ +#ifndef __SECURE_TIME_TEST_UTILS__ +#define __SECURE_TIME_TEST_UTILS__ + +#include +#include + +#define SECURE_TIME_TESTS_MAX_SIGN_SIZE_BYTES (128UL) +#define SECURE_TIME_TESTS_MAX_BLOB_SIZE_BYTES (1024UL) + +typedef struct data_buffer { + const uint8_t *data; + size_t size; +} data_buffer_t; + +void provision_data( + const uint8_t *ca_pubkey, + size_t ca_pubkey_size + ); + +size_t create_blob( + uint64_t time, + uint64_t nonce, + const data_buffer *delegation_pubkeys, + const data_buffer *prvkeys, + uint32_t pub_key_number, + uint8_t *blob_buffer, + size_t blob_buffer_size + ); + +#endif // __SECURE_TIME_TEST_UTILS__ diff --git a/features/storage/nvstore/source/nvstore.h b/features/storage/nvstore/source/nvstore.h index 53f3f7dcdf..275d8c53a1 100644 --- a/features/storage/nvstore/source/nvstore.h +++ b/features/storage/nvstore/source/nvstore.h @@ -49,7 +49,10 @@ typedef enum { // All predefined keys used for internal features should be defined here + NVSTORE_STORED_TIME_KEY = 2, + NVSTORE_STORED_BACK_TIME_KEY = 3, NVSTORE_DEVICEKEY_KEY = 4, + NVSTORE_CA_PUBKEY_KEY = 9, NVSTORE_LAST_PREDEFINED_KEY = 15, NVSTORE_NUM_PREDEFINED_KEYS diff --git a/secure_time/Readme.md b/secure_time/Readme.md new file mode 100644 index 0000000000..17d0bb4c44 --- /dev/null +++ b/secure_time/Readme.md @@ -0,0 +1,144 @@ +# Secure time + +## Table of Contents: + +- [Overview](#overview). +- [Setting Regular time](#setting-regular-time). +- [Setting Trusted time](#setting-trusted-time). +- [Getting current time](#getting-current-time). +- [Code example](#code-example). + +## Overview + +In IoT applications, a trusted source of time is needed to, among other things, ensure the confidentiality and integrity +of network connections. This is largely due to the fact that X.509 certificates are used by TLS stack to establish a secure +connection between the device and any services. TLS stack enforces X.509 certificate expiration with a required accuracy of up to one hour. + +To protect against an attacker who might use an expired certificate to pretend to be a trusted web service, +IoT devices need a mechanism for ensuring that they have an internal system time that is accurate to a specified fidelity. + +The secure time module supports setting the time from both trusted and regular time sources. + +## Setting regular time + +For regular operations, time source does not require trust requirements. + +When setting the time from a regular time source: + +* Time data is plain 64-bit value of seconds since UNIX epoch. +* The user can move time forward with no restrictions. +* The user can move time backward only slightly. Up to 3 minutes per day or 1 second per 8 minutes. + This is used for natural clock drift adjustment. + +See `secure_time_set()` in [secure_time_client.h](secure_time_client.h). + +## Setting trusted time + +For operations that must trust the current time of the device, time source must be a trusted one. + +Setting the time, obtained from a trusted time source (e.g. trusted time server): + +* Time data is 64-bit value of seconds since UNIX epoch, which is encapsulated inside a [signed blob](#signed-blob-content), + which a trusted authority signs. +* The user can move time forward and backward with no restrictions. +* Time is valid only if the blob passes verification for authenticity and freshness. + * Verifying the signature with the aid of a factory-provisioned public key guarantees authenticity. + * Using a nonce guarantees freshness. The device generates the nonce, saves it and sends it to the trusted time + source to be encapsulated in the blob and verified when the time blob reaches the device. + +See `secure_time_set_trusted_init()` and `secure_time_set_trusted_commit()` in +[secure_time_client.h](secure_time_client.h). + +### Signed blob content + +```text + |---------- signed fields -------------------| + v v + |---------|-----|----------------|-----------|---------|----| + |timestamp|nonce|delegations_size|delegations|sign_size|sign| + |---------|-----|----------------|-----------|---------|----| + ^ ^ ^ ^ ^ ^ + | | | | | | + | | | | | --- DER-encoded signature + | | | | | over the signed fields. + | | | | | + | | | | --- Size of the following + | | | | signature, 16-bit unsigned + | | | | integer in Little Endian format. + | | | | + | | | --- 0 or more delegation records. + | | | + | | --- Size of the following delegation records, + | | 16-bit unsigned integer in Little Endian format. + | | + | -------- 64-bit nonce generated by Secure Time code. + | + ------ 64-bit value of seconds since UNIX epoch in Little Endian format. +``` + +#### Blob content description + +- timestamp - 64-bit value of seconds since UNIX epoch in **Little Endian** format. +- nonce - 64-bit nonce **generated by Secure Time code** for validating time blob freshness. +- delegations_size - size of following delegation records, 16-bit unsigned + integer in Little Endian format. 0 size means no delegations are provided. +- delegations - 0 (delegations_size==0) or more delegation records for + supporting trust chains. +- sign_size - 16-bit **Little Endian** size in bytes of the following + DER-encoded signature. +- signature - DER-encoded signature over signed fields using either CA + (delegations_size==0) or delegation private key. + +Each delegation record consists of: + +- key_len - 16-bit **Little Endian** size in bytes of the following DER-encoded + public key. +- DER-encoded public key. +- Signature size - 16-bit **Little Endian** size in bytes of the following + DER-encoded signature. +- DER-encoded signature over key size and key fields. + +```text + |------------------ First delegation ---------| |----------------- N-th delegation -------------| + | | | | + |-- Signed fields -----| | |-- Signed fields -----| | + v v v v v v + |-----------|----------|-------------|--------| .. |-----------|----------|------------- |---------| + |Key_size(1)|Pub_key(1)|Sign_size(CA)|Sign(CA)| .. |Key_size(N)|Pub_key(N)|Sign_size(N-1)|Sign(N-1)| + |-----------|----------|-------------|--------| .. |-----------|----------|------------- |---------| + ^ ^ ^ ^ ^ + | | | | | + | | | | Signature using Nth-1 private key over --- + | | | | Nth public key & size fields + | | | | + | | | --- DER-encoded signature over the signed + | | | fields using CA private key. + | | | + | | --- Signature size - 16-bit value in Little Endian format. + | | + | --- First public key. + | + --- 16-bit value in Little Endian format. +``` + + In case no delegation records are present (delegations_size==0) - the time blob is signed by CA using its private key. + > CA public key expected to be available on the device using factory provisioning. + + A single delegation record is combined from the size of DER-encoded public key, DER-encoded public key, + size of DER-encoded signature and DER-encoded signature + over that public key and size fields using private key belonging to signing authority located downstream + on the trust chain. The signature is represented using signature size and signature data. + + Secure Time code performing blob's authenticity verification acts as follows: + - Delegation records are processed from left to right. + - The blob's signature is validated using the CA public key in case no delegations are provided. + Alternatively, in case when one or more delegation records are present, the last (rightmost) public + key in the delegation chain is used to verify time blob signature. + +## Getting current time + +See `secure_time_get()` in [secure_time_client.h](secure_time_client.h). + +## Code example + +[Secure time example on GitHub](https://github.com/ARMmbed/mbed-os-example-secure-time) diff --git a/secure_time/crypto/secure_time_crypto.h b/secure_time/crypto/secure_time_crypto.h new file mode 100644 index 0000000000..aa29465c67 --- /dev/null +++ b/secure_time/crypto/secure_time_crypto.h @@ -0,0 +1,57 @@ +/* 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 __SECURE_TIME_CRYPTO_H__ +#define __SECURE_TIME_CRYPTO_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Verify the data buffer signature. + * + * @param[in] data Data buffer. + * @param[in] data_size Size in bytes of buffer. + * @param[in] sign Data buffer's signature + * @param[in] sign_size Signature size + * @param[in] pubkey Buffer which holds the public key. + * @param[in] pubkey_size Size in bytes of the public key. + * @return SECURE_TIME_SUCCESS or negative error code if failed. + */ +int32_t secure_time_verify_signature( + const void *data, + size_t data_size, + const void *sign, + size_t sign_size, + const void *pubkey, + size_t pubkey_size + ); + +/* + * Generate a sequence of random bytes. + * + * @param[in] size Size in bytes of the random buffer + * @param[out] random_buf Buffer to fill with the generated random bytes. + */ +void secure_time_generate_random_bytes(size_t size, void *random_buf); + +#ifdef __cplusplus +} +#endif + +#endif // __SECURE_TIME_CRYPTO_H__ diff --git a/secure_time/crypto/secure_time_mbedtls.c b/secure_time/crypto/secure_time_mbedtls.c new file mode 100644 index 0000000000..a964b2edd5 --- /dev/null +++ b/secure_time/crypto/secure_time_mbedtls.c @@ -0,0 +1,167 @@ +/* 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 "secure_time_crypto.h" +#include "secure_time_client_spe.h" +#include "mbed_error.h" + +#include "mbedtls/pk.h" +#include "mbedtls/md.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" + +/* + * Structure containing contexts for random number generation. + */ +typedef struct secure_time_random_ctx { + mbedtls_ctr_drbg_context ctr_drbg_ctx; /* CTR_DRBG context structure. */ + mbedtls_entropy_context entropy_ctx; /* Entropy context structure. */ +} secure_time_random_ctx_t; + +static mbedtls_md_type_t md_type_from_signature_alg(SignatureAlg alg) +{ + switch(alg) { + case SIGNATURE_ALG_SHA256_ECDSA: + return MBEDTLS_MD_SHA256; + default: + return MBEDTLS_MD_NONE; + } +} + +static mbedtls_pk_type_t pk_type_from_signature_alg(SignatureAlg alg) +{ + switch(alg) { + case SIGNATURE_ALG_SHA256_ECDSA: + return MBEDTLS_PK_ECDSA; + default: + return MBEDTLS_PK_NONE; + } +} + +static void calculate_hash( + const void *data, + size_t data_size, + mbedtls_md_type_t md_type, + uint8_t *hash + ) +{ + int rc = SECURE_TIME_SUCCESS; + mbedtls_md_context_t md_ctx = {0}; + const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type); + if (NULL == md_info) { + error("mbedtls_md_info_from_type() returned NULL!"); + } + + mbedtls_md_init(&md_ctx); + rc = mbedtls_md_setup(&md_ctx, md_info, 0); + if (SECURE_TIME_SUCCESS != rc) { + error("mbedtls_md_setup() failed! (rc=%d)", rc); + } + rc = mbedtls_md_starts(&md_ctx); + if (SECURE_TIME_SUCCESS != rc) { + error("mbedtls_md_starts() failed! (rc=%d)", rc); + } + rc = mbedtls_md_update(&md_ctx, (const unsigned char *)data, data_size); + if (SECURE_TIME_SUCCESS != rc) { + error("mbedtls_md_update() failed! (rc=%d)", rc); + } + rc = mbedtls_md_finish(&md_ctx, hash); + if (SECURE_TIME_SUCCESS != rc) { + error("mbedtls_md_finish() failed! (rc=%d)", rc); + } + mbedtls_md_free(&md_ctx); +} + +static void random_ctx_init(secure_time_random_ctx_t *ctx) +{ + int rc = SECURE_TIME_SUCCESS; + + mbedtls_entropy_init(&(ctx->entropy_ctx)); + mbedtls_ctr_drbg_init(&(ctx->ctr_drbg_ctx)); + rc = mbedtls_ctr_drbg_seed( + &(ctx->ctr_drbg_ctx), + mbedtls_entropy_func, + &(ctx->entropy_ctx), + 0, + 0 + ); + if (SECURE_TIME_SUCCESS != rc) { + error("mbedtls_ctr_drbg_seed() failed! (rc=%d)", rc); + } +} + +int32_t secure_time_verify_signature( + const void *data, + size_t data_size, + const void *sign, + size_t sign_size, + const void *pubkey, + size_t pubkey_size + ) +{ + int rc = SECURE_TIME_SUCCESS; + uint8_t hash[MBEDTLS_MD_MAX_SIZE] = {0}; + mbedtls_pk_context pubkey_ctx = {0}; + + mbedtls_md_type_t md_type = md_type_from_signature_alg(SIGNATURE_ALG_SHA256_ECDSA); + mbedtls_pk_type_t pk_type = pk_type_from_signature_alg(SIGNATURE_ALG_SHA256_ECDSA); + if ((MBEDTLS_MD_NONE == md_type) || (MBEDTLS_PK_NONE == pk_type)) { + error("Failed to determine the signature algorithm!"); + } + + calculate_hash(data, data_size, md_type, hash); + mbedtls_pk_init(&pubkey_ctx); + + rc = mbedtls_pk_parse_public_key(&pubkey_ctx, pubkey, pubkey_size); + if (0 != rc) { + error("Failed to parse public key! (rc=%d)", rc); + } + + if (!mbedtls_pk_can_do(&pubkey_ctx, pk_type)) { + error("Unable to verify signature"); + } + + rc = mbedtls_pk_verify( + &pubkey_ctx, + md_type, + hash, + 0, + (const unsigned char *)sign, + sign_size + ); + if (SECURE_TIME_SUCCESS != rc) { + rc = SECURE_TIME_SIGNATURE_VERIFICATION_FAILED; + } + mbedtls_pk_free(&pubkey_ctx); + + return rc; +} + +void secure_time_generate_random_bytes(size_t size, void *random_buf) +{ + int rc = SECURE_TIME_SUCCESS; + secure_time_random_ctx_t *random_ctx = + (secure_time_random_ctx_t *)malloc(sizeof(*random_ctx)); + if (NULL == random_ctx) { + error("Failed to allocate memory for random_ctx!"); + } + random_ctx_init(random_ctx); + + rc = mbedtls_ctr_drbg_random(&(random_ctx->ctr_drbg_ctx), (unsigned char *)random_buf, size); + if (SECURE_TIME_SUCCESS != rc) { + error("mbedtls_ctr_drbg_random() failed! (rc=%d)", rc); + } + free(random_ctx); +} diff --git a/secure_time/secure_time_client.h b/secure_time/secure_time_client.h new file mode 100644 index 0000000000..ccc20d90cf --- /dev/null +++ b/secure_time/secure_time_client.h @@ -0,0 +1,91 @@ +/* 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 __SECURE_TIME_CLIENT_H__ +#define __SECURE_TIME_CLIENT_H__ + +#include +#include + +/** @addtogroup Secure-Time-API + * The C interface for setting and getting secure time. + * All functions are blocking. + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Error codes */ +#define SECURE_TIME_SUCCESS (0UL) +#define SECURE_TIME_BAD_PARAMS (-1L) +#define SECURE_TIME_INVALID_BLOB_SIZE (-2L) +#define SECURE_TIME_SIGNATURE_VERIFICATION_FAILED (-3L) +#define SECURE_TIME_NONCE_MISSING (-4L) +#define SECURE_TIME_NONCE_NOT_MATCH (-5L) +#define SECURE_TIME_NONCE_TIMEOUT (-6L) +#define SECURE_TIME_NOT_ALLOWED (-7L) + +/** + * Initialize secure time setting from a trusted time source. + * + * The function generates 64-bit nonce that will be used in the next invocation + * of ::secure_time_set_trusted_commit() to verify the trusted time blob freshness. + * + * @param[out] nonce The generated nonce. + * @return 0 or negative error code if failed. + */ +int32_t secure_time_set_trusted_init(uint64_t *nonce); + +/** + * Set the secure time from a trusted time source. + * + * The time is encapsulated inside blob which is signed with the trusted time + * sources' private key. + * The blob will be verified with the trusted time sources' public key. + * The signatuire to verify is located within the blob's data. + * + * @param[in] blob Buffer which holds the blob. + * @param[in] blob_size Size in bytes of blob. + * @return 0 or negative error code if failed. + */ +int32_t secure_time_set_trusted_commit(const void *blob, size_t blob_size); + +/** + * Set the secure time from an arbitrary time source. + * + * @param[in] new_time Time value in seconds since EPOCH. + * @return 0 or negative error code if failed. + */ +int32_t secure_time_set(uint64_t new_time); + +/** + * Return the current secure-time value. + * + * This function will return 0 in case secure time was never set. + * + * @return 64-bit value which can be:\n + * @a time in seconds since EPOCH.\n + * @a 0 in case of en error + */ +uint64_t secure_time_get(void); + +#ifdef __cplusplus +} +#endif + +/** @}*/ // end of Secure-Time-API group + +#endif // __SECURE_TIME_CLIENT_H__ diff --git a/secure_time/secure_time_client_direct.c b/secure_time/secure_time_client_direct.c new file mode 100644 index 0000000000..5dce3e223f --- /dev/null +++ b/secure_time/secure_time_client_direct.c @@ -0,0 +1,42 @@ +/* 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. + */ + +// This is the direct API call implementation of the Secure Time NSPE client +// interface. +// It can be used to built standalone Secure Time module without requiring SPM +// implementation. + +#include "secure_time_client.h" +#include "secure_time_impl.h" + +int32_t secure_time_set_trusted_init(uint64_t *nonce) +{ + return secure_time_set_trusted_init_impl(nonce); +} + +int32_t secure_time_set_trusted_commit(const void *blob, size_t blob_size) +{ + return secure_time_set_trusted_commit_impl(blob, blob_size); +} + +int32_t secure_time_set(uint64_t new_time) +{ + return secure_time_set_impl(new_time); +} + +uint64_t secure_time_get(void) +{ + return secure_time_get_impl(); +} diff --git a/secure_time/secure_time_client_spe.h b/secure_time/secure_time_client_spe.h new file mode 100644 index 0000000000..b6905f38c4 --- /dev/null +++ b/secure_time/secure_time_client_spe.h @@ -0,0 +1,121 @@ +/* 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 __SECURE_TIME_CLIENT_SPE_H__ +#define __SECURE_TIME_CLIENT_SPE_H__ + +#include +#include + +#include "secure_time_client.h" + +/** @addtogroup Secure-Time-API-SPE + * SPE only C interface for setting and getting secure time. + * All functions are blocking. + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/**< Maximal allowed blob size in bytes. */ +#define SECURE_TIME_MAX_BLOB_SIZE_BYTES (10 * 1024UL) + +/**< Timestamp size in bytes. */ +#define SECURE_TIME_TIMESTAMP_SIZE_BYTES (8UL) + +/**< Nonce size in bytes. */ +#define SECURE_TIME_NONCE_SIZE_BYTES (8UL) + +/**< The size of delegation length in bytes. */ +#define SECURE_TIME_DELEGATION_LENGTH_SIZE_BYTES (2UL) + +/**< The size of public key length field in bytes. */ +#define SECURE_TIME_PUBKEY_LENGTH_SIZE_BYTES (2UL) + +/**< The size of signature length field in bytes. */ +#define SECURE_TIME_SIGNATURE_LENGTH_SIZE_BYTES (2UL) + +/**< The size of public key length field in bytes. */ +#define SECURE_TIME_PUBKEY_LENGTH_SIZE_BYTES (2UL) + +/**< The size of the constant length blob header. */ +#define SECURE_TIME_BLOB_HEADER_SIZE_BYTES \ + ( \ + SECURE_TIME_TIMESTAMP_SIZE_BYTES + \ + SECURE_TIME_NONCE_SIZE_BYTES + \ + SECURE_TIME_DELEGATION_LENGTH_SIZE_BYTES \ + ) + +/**< The location of the delegation length field in the blob. */ +#define SECURE_TIME_DELEGATION_LENGTH_OFFSET \ + ( \ + SECURE_TIME_TIMESTAMP_SIZE_BYTES + \ + SECURE_TIME_NONCE_SIZE_BYTES \ + ) + +/** + * Enumeration for the possible blob signature algorithms + */ +typedef enum signature_alg { + SIGNATURE_ALG_INVALID = 0, /**< Invalid algorithm type */ + SIGNATURE_ALG_SHA256_ECDSA = 1, /**< ECDSA on a SHA256 hash */ + SIGNATURE_ALG_MAX = SIGNATURE_ALG_SHA256_ECDSA +} SignatureAlg; + +/** + * Factory-setup provisioning of public key to be used by secure_time_set_trusted(). + * Defined as a weak function which by default tries to write the public key to NVStore. + * If the user wants to provision the public key differently than this function needs + * to be implemented by the user according to the provisioning method, as well as + * secure_time_get_stored_public_key(). + * + * @param[in] pubkey Public key for blob verification. + * @param[in] key_size Size in bytes of public key. + * @return 0 or negative error code if failed. + */ +int32_t secure_time_set_stored_public_key(const void* pubkey, size_t key_size); + +/** + * Return the previously-provisioned public key. + * Defined as a weak function which by default tries to read the public key from NVStore. + * If the user provisioned the public key differently (By implementing secure_time_set_stored_public_key()) + * than this function also needs to be implemented. + * + * @param[out] pubkey Buffer to fill with the public key. + * @param[in] size Size in bytes of the buffer. + * @param[out] actual_size Actual size in bytes of the returned public key. + * @return 0 or negative error code if failed. + */ +int32_t secure_time_get_stored_public_key(uint8_t *pubkey, size_t size, size_t *actual_size); + +/** + * Return the size in bytes of the previously-provisioned public key. + * Defined as a weak function which by default tries to read the public key from NVStore. + * If the user provisioned the public key differently (By implementing secure_time_set_stored_public_key()) + * than this function also needs to be implemented. + * + * @param[out] actual_size Actual size in bytes of the returned public key. + * @return 0 or negative error code if failed. + */ +int32_t secure_time_get_stored_public_key_size(size_t *actual_size); + +#ifdef __cplusplus +} +#endif + +/** @}*/ // end of Secure-Time-API group + +#endif // __SECURE_TIME_CLIENT_SPE_H__ diff --git a/secure_time/secure_time_client_spe_direct.c b/secure_time/secure_time_client_spe_direct.c new file mode 100644 index 0000000000..ee7ae837c8 --- /dev/null +++ b/secure_time/secure_time_client_spe_direct.c @@ -0,0 +1,43 @@ +/* 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. + */ + +// This is the direct API call implementation of the Secure Time SPE client +// interface. +// This file extends NSPE interface implementation by implementing APIs +// available from SPE only. +// This file can be used to built standalone Secure Time module without +// requiring full SPM implementation. + +#include "secure_time_client_spe.h" +#include "secure_time_impl.h" + +int32_t secure_time_set_stored_public_key(const void* pubkey, size_t key_size) +{ + return secure_time_set_stored_public_key_impl(pubkey, key_size); +} + +int32_t secure_time_get_stored_public_key( + uint8_t *pubkey, + size_t size, + size_t *actual_size +) +{ + return secure_time_get_stored_public_key_impl(pubkey, size, actual_size); +} + +int32_t secure_time_get_stored_public_key_size(size_t *actual_size) +{ + return secure_time_get_stored_public_key_size_impl(actual_size); +} diff --git a/secure_time/secure_time_impl.c b/secure_time/secure_time_impl.c new file mode 100644 index 0000000000..1ad9f27ced --- /dev/null +++ b/secure_time/secure_time_impl.c @@ -0,0 +1,407 @@ +/* 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 "secure_time_utils.h" +#include "secure_time_impl.h" +#include "secure_time_client_spe.h" +#include "secure_time_storage.h" +#include "secure_time_crypto.h" +#include "mbed_error.h" +#include "platform/mbed_rtc_time.h" +#include + +#define SECURE_TIME_NONCE_GENERATION_TIME_INVALID UINT64_MAX + +#define EXTRACT_UINT16(buf) ((((uint16_t)(((uint8_t *)(buf))[1])) << 8) + (uint16_t)(((uint8_t *)(buf))[0])) + +/* + * Enumeration for the possible directions for setting the time. + */ +typedef enum { + SECURE_TIME_FORWARD = 1, + SECURE_TIME_BACKWARDS = 2 +} SecureTimeDirection; + +/* + * Structure used during delegation record parsiong. + */ +typedef struct delegation_record_info { + const uint8_t *record_start; + uint16_t pubkey_size; + const uint8_t *pubkey; + uint16_t signature_size; + const uint8_t *signature; +} delegation_record_info_t; + +/* + * Structure containing context of the NONCE. + */ +typedef struct nonce_ctx { + uint64_t generation_time; /* Timestamp of last generated NONCE. */ + uint64_t nonce; /* The last generated nonce. */ +} nonce_data_t; + +static nonce_data_t g_trusted_time_nonce = { + .generation_time = SECURE_TIME_NONCE_GENERATION_TIME_INVALID, + .nonce = 0 +}; + +static void invalidate_nonce(nonce_data_t *nonce_data) +{ + nonce_data->generation_time = SECURE_TIME_NONCE_GENERATION_TIME_INVALID; + nonce_data->nonce = 0; +} + +static bool is_nonce_valid(nonce_data_t *nonce) +{ + return (SECURE_TIME_NONCE_GENERATION_TIME_INVALID != nonce->generation_time); +} + +static int32_t extract_nonce_and_verify(const uint8_t *blob, size_t blob_size) +{ + if (!is_nonce_valid(&g_trusted_time_nonce)) { + return SECURE_TIME_NONCE_MISSING; + } + + uint64_t blob_nonce; + memcpy(&blob_nonce, blob + SECURE_TIME_TIMESTAMP_SIZE_BYTES, SECURE_TIME_NONCE_SIZE_BYTES); + + if (blob_nonce != g_trusted_time_nonce.nonce) { + return SECURE_TIME_NONCE_NOT_MATCH; + } + + if ((secure_time_get_impl() - g_trusted_time_nonce.generation_time) > SECURE_TIME_NONCE_TIMEOUT_SECONDS) { + // If invalidation timeout expired, invalidate SPE nonce. + invalidate_nonce(&g_trusted_time_nonce); + return SECURE_TIME_NONCE_TIMEOUT; + } + + return SECURE_TIME_SUCCESS; +} + +static size_t parse_delegation_record( + const uint8_t *record_ptr, + uint32_t bytes_left, + delegation_record_info_t *record_info + ) +{ + record_info->record_start = record_ptr; + + // Make sure the delegation record is big enough for the public key length field. + if (bytes_left < SECURE_TIME_PUBKEY_LENGTH_SIZE_BYTES) { + return 0; + } + + // Extract the public key length. + record_info->pubkey_size = EXTRACT_UINT16(record_ptr); + bytes_left -= SECURE_TIME_PUBKEY_LENGTH_SIZE_BYTES; + record_ptr += SECURE_TIME_PUBKEY_LENGTH_SIZE_BYTES; + + // Make sure there're enough bytes left for the key. + if (record_info->pubkey_size == 0 || record_info->pubkey_size > bytes_left) { + return 0; + } + + // Remember the key location. + record_info->pubkey = record_ptr; + bytes_left -= record_info->pubkey_size; + record_ptr += record_info->pubkey_size; + + // Make sure the delegation record is big enough for the signature length field. + if (bytes_left < SECURE_TIME_SIGNATURE_LENGTH_SIZE_BYTES) { + return 0; + } + + // Extract the signature length. + record_info->signature_size = EXTRACT_UINT16(record_ptr); + bytes_left -= SECURE_TIME_SIGNATURE_LENGTH_SIZE_BYTES; + record_ptr += SECURE_TIME_SIGNATURE_LENGTH_SIZE_BYTES; + + // Make sure there're enough bytes left for the signature. + if (record_info->signature_size == 0 || record_info->signature_size > bytes_left) { + return 0; + } + + // Remember the signature location. + record_info->signature = record_ptr; + record_ptr += record_info->signature_size; + + // Return the record size. + return record_ptr - record_info->record_start; +} + +static int32_t verify_blob( + const uint8_t *blob, + size_t blob_size, + const uint8_t *ca_pubkey, + size_t ca_pubkey_size + ) +{ + int rc = SECURE_TIME_SUCCESS; + + // Make sure that the blob is big enough to contain at least the header. + if (blob_size < SECURE_TIME_BLOB_HEADER_SIZE_BYTES || blob_size > SECURE_TIME_MAX_BLOB_SIZE_BYTES) { + return SECURE_TIME_INVALID_BLOB_SIZE; + } + + // Extract the size of the delegation section. + uint16_t delegation_size = EXTRACT_UINT16(blob + SECURE_TIME_DELEGATION_LENGTH_OFFSET); + + // Make sure the delegation section followed by the signature length field fits within the blob. + if (SECURE_TIME_BLOB_HEADER_SIZE_BYTES + + delegation_size + + SECURE_TIME_SIGNATURE_LENGTH_SIZE_BYTES > blob_size) { + return SECURE_TIME_INVALID_BLOB_SIZE; + } + + // Go over all the keys in the delegation records of the blob and verify + // their signatures. + const uint8_t *delegation_start = blob + SECURE_TIME_BLOB_HEADER_SIZE_BYTES; + const uint8_t *delegation_end = delegation_start + delegation_size; + uint32_t bytes_left = 0; + delegation_record_info_t prev_record_info = {0}; + prev_record_info.pubkey = ca_pubkey; + prev_record_info.pubkey_size = ca_pubkey_size; + delegation_record_info_t cur_record_info = {0}; + size_t cur_record_size = 0; + + while (delegation_start < delegation_end) { + bytes_left = delegation_end - delegation_start; + + cur_record_size = parse_delegation_record(delegation_start, bytes_left, &cur_record_info); + if (cur_record_size == 0) { + return SECURE_TIME_INVALID_BLOB_SIZE; + } + + rc = secure_time_verify_signature( + cur_record_info.record_start, + cur_record_info.pubkey_size + SECURE_TIME_PUBKEY_LENGTH_SIZE_BYTES, + cur_record_info.signature, + cur_record_info.signature_size, + prev_record_info.pubkey, + prev_record_info.pubkey_size + ); + + if (SECURE_TIME_SUCCESS != rc) { + return SECURE_TIME_SIGNATURE_VERIFICATION_FAILED; + } + + delegation_start += cur_record_size; + prev_record_info = cur_record_info; + } + + // Extract the size of the blob's signature. + uint16_t blob_signature_size = EXTRACT_UINT16(delegation_end); + const uint8_t *blob_signature = delegation_end + SECURE_TIME_SIGNATURE_LENGTH_SIZE_BYTES; + + // Make sure the number of the remaining bytes is exactly as the blob's signature size. + if ((blob_size - + SECURE_TIME_BLOB_HEADER_SIZE_BYTES - + delegation_size - + SECURE_TIME_SIGNATURE_LENGTH_SIZE_BYTES) != blob_signature_size) { + return SECURE_TIME_INVALID_BLOB_SIZE; + } + + // Verify the overall blob signature. + // At this stage prev_record_info contains the public key that should be used for the verification. + rc = secure_time_verify_signature( + blob, + SECURE_TIME_BLOB_HEADER_SIZE_BYTES + delegation_size, + blob_signature, + blob_signature_size, + prev_record_info.pubkey, + prev_record_info.pubkey_size + ); + + if (SECURE_TIME_SUCCESS != rc) { + return SECURE_TIME_SIGNATURE_VERIFICATION_FAILED; + } + + return rc; +} + +int32_t secure_time_set_trusted_init_impl(uint64_t *nonce) +{ + if (!nonce) { + error("nonce is NULL!"); + } + + // Invalidate any existing nonce + invalidate_nonce(&g_trusted_time_nonce); + + secure_time_generate_random_bytes(sizeof(uint64_t), &g_trusted_time_nonce.nonce); + *nonce = g_trusted_time_nonce.nonce; + g_trusted_time_nonce.generation_time = secure_time_get_impl(); + return SECURE_TIME_SUCCESS; +} + +int32_t secure_time_set_trusted_commit_impl(const void *blob, size_t blob_size) +{ + int rc = SECURE_TIME_SUCCESS; + if (!blob || (0 == blob_size)) { + error("blob is NULL or size 0!"); + } + + // Read the CA public key from secure storage. + size_t ca_pubkey_size = 0; + + rc = secure_time_get_stored_public_key_size(&ca_pubkey_size); + if (SECURE_TIME_SUCCESS != rc) { + error("Failed to read the CA public key size! (rc=%d)", rc); + } + uint8_t *ca_pubkey = (uint8_t *)malloc(ca_pubkey_size); + if (!ca_pubkey) { + error("Failed to allocate memory for CA public key data!"); + } + rc = secure_time_get_stored_public_key(ca_pubkey, ca_pubkey_size, &ca_pubkey_size); + if (SECURE_TIME_SUCCESS != rc) { + error("Failed to read the CA public key! (rc=%d)", rc); + } + + // Verify the blob's correctness. + rc = verify_blob(blob, blob_size, ca_pubkey, ca_pubkey_size); + free(ca_pubkey); + + if (SECURE_TIME_SUCCESS == rc) { + // Extract the new time value from the blob according to the schema. + uint64_t new_time; + memcpy(&new_time, blob, SECURE_TIME_TIMESTAMP_SIZE_BYTES); + + // Extract the nonce from the blob and verify its' correctness and freshness. + // In case the the nonce is different than the last generated nonce or is too old, + // the call is ignored. + rc = extract_nonce_and_verify((const uint8_t *)blob, blob_size); + if (SECURE_TIME_SUCCESS == rc) { + // Get current RTC time. + uint64_t rtc_time = (uint64_t)time(NULL); + + // Set RTC with new time if it is around 1-2 minutes forward/backward + // than current RTC time. + if(llabs(new_time - rtc_time) > SECURE_TIME_MIN_RTC_LATENCY_SEC) { + set_time(new_time); + } + + // Read the current stored time from secure storage. + uint64_t stored_time = 0; + secure_time_get_stored_time(&stored_time); + + SecureTimeDirection direction = (new_time > stored_time) ? + SECURE_TIME_FORWARD : + SECURE_TIME_BACKWARDS; + bool set_storage = false; + + // If new time is less than 1 day forward or less than 1-2 minutes backwards + // do not update secure storage. + if (SECURE_TIME_FORWARD == direction) { + set_storage = (new_time - stored_time) > SECURE_TIME_MIN_STORAGE_FORWARD_LATENCY_SEC; + } else { + set_storage = (stored_time - new_time) > SECURE_TIME_MIN_STORAGE_BACKWARD_LATENCY_SEC; + } + + if (set_storage) { + // Write the new time to secure storage entry of last backwards time. + secure_time_set_stored_back_time(new_time); + + // Write the new time to secure storage entry of current stored time. + secure_time_set_stored_time(new_time); + } + + // Update the SPE delta value as the new time minus current SPE tick count. + secure_time_update_boot_time(new_time); + + // Invalidate nonce + invalidate_nonce(&g_trusted_time_nonce); + } + } + + return rc; +} + +static void set_time_forward(uint64_t new_time, uint64_t curr_os_time) +{ + // Update the SPE delta value as the new time minus current SPE tick count. + secure_time_update_boot_time(new_time); + + // Set RTC with new time if it is around 1-2 minutes forward than current time. + uint64_t rtc_time = (uint64_t)time(NULL); + if((new_time - rtc_time) > SECURE_TIME_MIN_RTC_LATENCY_SEC) { + set_time(new_time); + } + + // Write new time to secure storage entry of current stored time if it's more than 1 day forward + // than current OS time. + bool set_storage = (new_time - curr_os_time) > SECURE_TIME_MIN_STORAGE_FORWARD_LATENCY_SEC; + if (set_storage) { + secure_time_set_stored_time(new_time); + } +} + +static int32_t set_time_backwards(uint64_t new_time, uint64_t curr_os_time) +{ + uint64_t stored_back_time = 0; + secure_time_get_stored_back_time(&stored_back_time); + bool stored_back_time_exist = (stored_back_time > 0); + + // For each day between stored_back_time and new_time we can move backwards by up to 3 minutes: + // Which is same as up to 480 seconds for each second in this interval. + // So: (A) MAX_BACK_SECONDS = (new_time - stored_back_time)/480 + // (B) (curr_os_time - new_time) <= MAX_BACK_SECONDS + // (A & B) (curr_os_time - new_time) <= (new_time - stored_back_time)/480 + bool set_back = ( stored_back_time_exist && + (new_time > stored_back_time) && + ((curr_os_time - new_time) <= (new_time - stored_back_time)/480) ); + if (set_back) { + // Update the SPE delta value as the new time minus current SPE tick count. + secure_time_update_boot_time(new_time); + + // Write the new time to secure storage entry of last backwards time and current stored time. + secure_time_set_stored_back_time(new_time); + secure_time_set_stored_time(new_time); + return SECURE_TIME_SUCCESS; + } + return SECURE_TIME_NOT_ALLOWED; +} + +int32_t secure_time_set_impl(uint64_t new_time) +{ + // Get the current time in the device. + uint64_t curr_os_time = secure_time_get_impl(); + SecureTimeDirection direction = (new_time > curr_os_time) ? + SECURE_TIME_FORWARD : + SECURE_TIME_BACKWARDS; + + if (SECURE_TIME_FORWARD == direction) { + set_time_forward(new_time, curr_os_time); + } else { + return set_time_backwards(new_time, curr_os_time); + } + + uint64_t stored_time = 0; + secure_time_get_stored_time(&stored_time); + + // Write the new time to secure storage entry of current stored time + // if new time is more than around 5-6 days forward than current stored time. + if ( (new_time > stored_time) && + ((new_time - stored_time) > SECURE_TIME_MIN_STORAGE_IDLE_LATENCY_SEC) ) { + secure_time_set_stored_time(new_time); + } + return SECURE_TIME_SUCCESS; +} + +uint64_t secure_time_get_impl(void) +{ + uint64_t boot_time = secure_time_get_boot_time(); + uint64_t secs_since_boot = secure_time_get_seconds_since_boot(); + return (boot_time > 0) ? (boot_time + secs_since_boot) : 0; +} diff --git a/secure_time/secure_time_impl.h b/secure_time/secure_time_impl.h new file mode 100644 index 0000000000..bb58becf56 --- /dev/null +++ b/secure_time/secure_time_impl.h @@ -0,0 +1,50 @@ +/* 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 __SECURE_TIME_IMPL_H__ +#define __SECURE_TIME_IMPL_H__ + +#include "secure_time_client_spe.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**< Maximum time a nonce is valid after generation.*/ +#define SECURE_TIME_NONCE_TIMEOUT_SECONDS (300UL) + +int32_t secure_time_set_trusted_init_impl(uint64_t *nonce); + +int32_t secure_time_set_trusted_commit_impl(const void *blob, size_t blob_size); + +int32_t secure_time_set_impl(uint64_t new_time); + +uint64_t secure_time_get_impl(void); + +int32_t secure_time_set_stored_public_key_impl(const void* pubkey, size_t key_size); + +int32_t secure_time_get_stored_public_key_impl( + uint8_t *pubkey, + size_t size, + size_t *actual_size +); + +int32_t secure_time_get_stored_public_key_size_impl(size_t *actual_size); + +#ifdef __cplusplus +} +#endif + +#endif // __SECURE_TIME_IMPL_H__ diff --git a/secure_time/secure_time_utils.cpp b/secure_time/secure_time_utils.cpp new file mode 100644 index 0000000000..f0883801f8 --- /dev/null +++ b/secure_time/secure_time_utils.cpp @@ -0,0 +1,39 @@ +/* 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 +#include "secure_time_client_spe.h" +#include "mbed_error.h" +#include "cmsis_os2.h" +#include "rtos/Kernel.h" + +using namespace rtos; + +static uint64_t g_boot_time_in_secs = 0; + +uint64_t secure_time_get_seconds_since_boot(void) +{ + uint32_t freq = osKernelGetTickFreq(); + return Kernel::get_ms_count() / freq; +} + +void secure_time_update_boot_time(uint64_t new_time) +{ + g_boot_time_in_secs = new_time - secure_time_get_seconds_since_boot(); +} + +uint64_t secure_time_get_boot_time(void) +{ + return g_boot_time_in_secs; +} diff --git a/secure_time/secure_time_utils.h b/secure_time/secure_time_utils.h new file mode 100644 index 0000000000..376de8d9ec --- /dev/null +++ b/secure_time/secure_time_utils.h @@ -0,0 +1,53 @@ +/* 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 __SECURE_TIME_INTERNAL_H__ +#define __SECURE_TIME_INTERNAL_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SECURE_TIME_MIN_RTC_LATENCY_SEC (100UL) + +/* + * Return the device boot time in seconds since EPOCH. + * + * @return 64-bit value of seconds. + */ +uint64_t secure_time_get_boot_time(void); + +/* + * Update the device boot time according to the new time. + * + * @param[in] new_time Time value in seconds since EPOCH to update boot time.\n + * updated_boot_time = new_time - seconds_since_last_boot + */ +void secure_time_update_boot_time(uint64_t new_time); + +/* + * Return how many seconds have passed since last boot. + * + * @return 64-bit value of seconds. + */ +uint64_t secure_time_get_seconds_since_boot(void); + +#ifdef __cplusplus +} +#endif + +#endif // __SECURE_TIME_INTERNAL_H__ diff --git a/secure_time/storage/secure_time_nvstore.cpp b/secure_time/storage/secure_time_nvstore.cpp new file mode 100644 index 0000000000..4986069346 --- /dev/null +++ b/secure_time/storage/secure_time_nvstore.cpp @@ -0,0 +1,125 @@ +/* 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 +#include "secure_time_storage.h" +#include "secure_time_client_spe.h" +#include "secure_time_impl.h" +#include "nvstore.h" +#include "mbed_toolchain.h" +#include "mbed_error.h" + +#if !NVSTORE_ENABLED +#error [NOT_SUPPORTED] NVSTORE needs to be enabled +#endif + +MBED_WEAK int32_t secure_time_set_stored_public_key_impl(const void* pubkey, size_t key_size) +{ + if (NULL == pubkey) { + error("pubkey is NULL!"); + } + NVStore &nvstore = NVStore::get_instance(); + int rc = nvstore.set(NVSTORE_CA_PUBKEY_KEY, key_size, pubkey); + return rc; +} + +MBED_WEAK int32_t secure_time_get_stored_public_key_impl(uint8_t *pubkey, size_t size, size_t *actual_size) +{ + if (NULL == pubkey) { + error("pubkey is NULL!"); + } + if (NULL == actual_size) { + error("actual_size is NULL!"); + } + uint16_t len = 0; + NVStore &nvstore = NVStore::get_instance(); + int rc = nvstore.get( + NVSTORE_CA_PUBKEY_KEY, + size, + pubkey, + len + ); + *actual_size = (size_t)len; + return rc; +} + +MBED_WEAK int32_t secure_time_get_stored_public_key_size_impl(size_t *actual_size) +{ + if (NULL == actual_size) { + error("actual_size is NULL!"); + } + uint16_t len = 0; + NVStore &nvstore = NVStore::get_instance(); + int rc = nvstore.get_item_size(NVSTORE_CA_PUBKEY_KEY, len); + *actual_size = (size_t)len; + return rc; +} + +void secure_time_set_stored_time(uint64_t new_time) +{ + NVStore &nvstore = NVStore::get_instance(); + int rc = nvstore.set(NVSTORE_STORED_TIME_KEY, sizeof(new_time), &new_time); + if (NVSTORE_SUCCESS != rc) { + error("Failed to set STORED_TIME to NVStore! (rc=%d)", rc); + } +} + +void secure_time_get_stored_time(uint64_t *stored_time) +{ + if (NULL == stored_time) { + error("stored_time is NULL!"); + } + NVStore &nvstore = NVStore::get_instance(); + uint16_t len = 0; + int rc = nvstore.get(NVSTORE_STORED_TIME_KEY, sizeof(*stored_time), stored_time, len); + if ((NVSTORE_SUCCESS != rc) && (NVSTORE_NOT_FOUND != rc)) { + error("Failed to get STORED_TIME from NVStore! (rc=%d)", rc); + } + else if ((sizeof(*stored_time) != len) && (NVSTORE_NOT_FOUND != rc)) { + error("Length of STORED_TIME entry too short! (%uh)", len); + } + + if (NVSTORE_NOT_FOUND == rc) { + *stored_time = 0; + } +} + +void secure_time_set_stored_back_time(uint64_t new_time) +{ + NVStore &nvstore = NVStore::get_instance(); + int rc = nvstore.set(NVSTORE_STORED_BACK_TIME_KEY, sizeof(new_time), &new_time); + if (NVSTORE_SUCCESS != rc) { + error("Failed to set STORED_BACK_TIME to NVStore! (rc=%d)", rc); + } +} + +void secure_time_get_stored_back_time(uint64_t *stored_back_time) +{ + if (NULL == stored_back_time) { + error("stored_back_time is NULL!"); + } + NVStore &nvstore = NVStore::get_instance(); + uint16_t len = 0; + int rc = nvstore.get(NVSTORE_STORED_BACK_TIME_KEY, sizeof(*stored_back_time), stored_back_time, len); + if ((NVSTORE_SUCCESS != rc) && (NVSTORE_NOT_FOUND != rc)) { + error("Failed to get STORED_BACK_TIME from NVStore! (rc=%d)", rc); + } + else if ((sizeof(*stored_back_time) != len) && (NVSTORE_NOT_FOUND != rc)) { + error("Length of STORED_BACK_TIME entry too short! (%uh)", len); + } + + if (NVSTORE_NOT_FOUND == rc) { + *stored_back_time = 0; + } +} diff --git a/secure_time/storage/secure_time_storage.h b/secure_time/storage/secure_time_storage.h new file mode 100644 index 0000000000..f55af6e0ff --- /dev/null +++ b/secure_time/storage/secure_time_storage.h @@ -0,0 +1,62 @@ +/* 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 __SECURE_TIME_STORAGE_H__ +#define __SECURE_TIME_STORAGE_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Storage latency defines +#define SECURE_TIME_MIN_STORAGE_FORWARD_LATENCY_SEC (100000UL) +#define SECURE_TIME_MIN_STORAGE_BACKWARD_LATENCY_SEC (100UL) +#define SECURE_TIME_MIN_STORAGE_IDLE_LATENCY_SEC (500000UL) + +/* + * Set storage entry of last set time. + * + * @param[in] new_time Time value in seconds since EPOCH. + */ +void secure_time_set_stored_time(uint64_t new_time); + +/* + * Get storage entry of last set time. + * + * @param[out] stored_time Time value in seconds since EPOCH, 0 if not exists. + */ +void secure_time_get_stored_time(uint64_t *stored_time); + +/* + * Set storage entry of last set-backwards time. + * + * @param[in] new_time Time value in seconds since EPOCH. + */ +void secure_time_set_stored_back_time(uint64_t new_time); + +/* + * Get storage entry of last set-backwards time. + * + * @param[out] stored_back_time Time value in seconds since EPOCH, 0 if not exists. + */ +void secure_time_get_stored_back_time(uint64_t *stored_back_time); + +#ifdef __cplusplus +} +#endif + +#endif // __SECURE_TIME_STORAGE_H__