Add secure_time library implementation

feature-secure-time
Michael Schwarcz 2018-03-07 17:48:20 +02:00 committed by Cruz Monrreal II
parent a1625895c3
commit 44f6b8a07c
17 changed files with 2198 additions and 0 deletions

View File

@ -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 <secure_time_utils.h>
#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;
}

View File

@ -0,0 +1,192 @@
#include "secure_time_test_utils.h"
#include "secure_time_client_spe.h"
#include "unity.h"
#include <string.h>
#include "mbedtls/pk.h"
#include "mbedtls/md.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include <stdio.h>
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;
}

View File

@ -0,0 +1,30 @@
#ifndef __SECURE_TIME_TEST_UTILS__
#define __SECURE_TIME_TEST_UTILS__
#include <stdint.h>
#include <stdlib.h>
#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__

View File

@ -49,7 +49,10 @@ typedef enum {
// All predefined keys used for internal features should be defined here // 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_DEVICEKEY_KEY = 4,
NVSTORE_CA_PUBKEY_KEY = 9,
NVSTORE_LAST_PREDEFINED_KEY = 15, NVSTORE_LAST_PREDEFINED_KEY = 15,
NVSTORE_NUM_PREDEFINED_KEYS NVSTORE_NUM_PREDEFINED_KEYS

144
secure_time/Readme.md Normal file
View File

@ -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)

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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__

View File

@ -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);
}

View File

@ -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 <stdint.h>
#include <stdlib.h>
/** @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__

View File

@ -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();
}

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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__

View File

@ -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);
}

View File

@ -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 <string.h>
#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;
}

View File

@ -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__

View File

@ -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 <secure_time_utils.h>
#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;
}

View File

@ -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 <stdint.h>
#include <stdbool.h>
#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__

View File

@ -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 <secure_time_utils.h>
#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;
}
}

View File

@ -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 <stdint.h>
#include <stdbool.h>
#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__