mbed-os/secure_time/secure_time_impl.c

408 lines
14 KiB
C

/* 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;
}